Roo/bootstrap/Img.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * @cfg {String} xsUrl xs image source
1330  * @cfg {String} smUrl sm image source
1331  * @cfg {String} mdUrl md image source
1332  * @cfg {String} lgUrl lg image source
1333  * 
1334  * @constructor
1335  * Create a new Input
1336  * @param {Object} config The config object
1337  */
1338
1339 Roo.bootstrap.Img = function(config){
1340     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1341     
1342     this.addEvents({
1343         // img events
1344         /**
1345          * @event click
1346          * The img click event for the img.
1347          * @param {Roo.EventObject} e
1348          */
1349         "click" : true
1350     });
1351 };
1352
1353 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1354     
1355     imgResponsive: true,
1356     border: '',
1357     src: '',
1358     href: false,
1359     target: false,
1360     xsUrl: '',
1361     smUrl: '',
1362     mdUrl: '',
1363     lgUrl: '',
1364
1365     getAutoCreate : function()
1366     {   
1367         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1368             return this.createSingleImg();
1369         }
1370         
1371         var cfg = {
1372             tag: 'div',
1373             cls: 'roo-image-responsive-group',
1374             cn: []
1375         }
1376         Roo.log('run?????????????????????');
1377         Roo.each(['xsUrl', 'smUrl', 'mdUrl', 'lgUrl'], function(size){
1378             Roo.log(size);
1379             Roo.log(this[size]);
1380             if(!this[size]){
1381                 return;
1382             }
1383             
1384             var img = {
1385                 tag: 'img',
1386                 cls: (this.imgResponsive) ? 'img-responsive' : '',
1387                 html: this.html || cfg.html,
1388                 src: this[size]
1389             }
1390             
1391             img.cls += ' roo-image-responsive-' + size;
1392             
1393             if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1394                 cfg.cls += ' img-' + this.border;
1395             }
1396             
1397             if(this.alt){
1398                 cfg.alt = this.alt;
1399             }
1400             
1401             if(this.href){
1402                 var a = {
1403                     tag: 'a',
1404                     href: this.href,
1405                     cn: [
1406                         img
1407                     ]
1408                 }
1409
1410                 if(this.target){
1411                     a.target = this.target;
1412                 }
1413             }
1414             
1415             cfg.cn.push((this.href) ? a : img);
1416             
1417         });
1418         
1419         return cfg;
1420     },
1421     
1422     createSingleImg : function()
1423     {
1424         var cfg = {
1425             tag: 'img',
1426             cls: (this.imgResponsive) ? 'img-responsive' : '',
1427             html : null
1428         }
1429         
1430         cfg.html = this.html || cfg.html;
1431         
1432         cfg.src = this.src || cfg.src;
1433         
1434         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1435             cfg.cls += ' img-' + this.border;
1436         }
1437         
1438         if(this.alt){
1439             cfg.alt = this.alt;
1440         }
1441         
1442         if(this.href){
1443             var a = {
1444                 tag: 'a',
1445                 href: this.href,
1446                 cn: [
1447                     cfg
1448                 ]
1449             }
1450             
1451             if(this.target){
1452                 a.target = this.target;
1453             }
1454             
1455         }
1456         
1457         return (this.href) ? a : cfg;
1458     },
1459     
1460     initEvents: function() {
1461         
1462         if(!this.href){
1463             this.el.on('click', this.onClick, this);
1464         }
1465     },
1466     
1467     onClick : function(e)
1468     {
1469         Roo.log('img onclick');
1470         this.fireEvent('click', this, e);
1471     }
1472    
1473 });
1474
1475  /*
1476  * - LGPL
1477  *
1478  * image
1479  * 
1480  */
1481
1482
1483 /**
1484  * @class Roo.bootstrap.Link
1485  * @extends Roo.bootstrap.Component
1486  * Bootstrap Link Class
1487  * @cfg {String} alt image alternative text
1488  * @cfg {String} href a tag href
1489  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1490  * @cfg {String} html the content of the link.
1491  * @cfg {String} anchor name for the anchor link
1492
1493  * @cfg {Boolean} preventDefault (true | false) default false
1494
1495  * 
1496  * @constructor
1497  * Create a new Input
1498  * @param {Object} config The config object
1499  */
1500
1501 Roo.bootstrap.Link = function(config){
1502     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1503     
1504     this.addEvents({
1505         // img events
1506         /**
1507          * @event click
1508          * The img click event for the img.
1509          * @param {Roo.EventObject} e
1510          */
1511         "click" : true
1512     });
1513 };
1514
1515 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1516     
1517     href: false,
1518     target: false,
1519     preventDefault: false,
1520     anchor : false,
1521     alt : false,
1522
1523     getAutoCreate : function()
1524     {
1525         
1526         var cfg = {
1527             tag: 'a'
1528         };
1529         // anchor's do not require html/href...
1530         if (this.anchor === false) {
1531             cfg.html = this.html || '';
1532             cfg.href = this.href || '#';
1533         } else {
1534             cfg.name = this.anchor;
1535             if (this.html !== false) {
1536                 cfg.html = this.html;
1537             }
1538             if (this.href !== false) {
1539                 cfg.href = this.href;
1540             }
1541         }
1542         
1543         if(this.alt !== false){
1544             cfg.alt = this.alt;
1545         }
1546         
1547         
1548         if(this.target !== false) {
1549             cfg.target = this.target;
1550         }
1551         
1552         return cfg;
1553     },
1554     
1555     initEvents: function() {
1556         
1557         if(!this.href || this.preventDefault){
1558             this.el.on('click', this.onClick, this);
1559         }
1560     },
1561     
1562     onClick : function(e)
1563     {
1564         if(this.preventDefault){
1565             e.preventDefault();
1566         }
1567         //Roo.log('img onclick');
1568         this.fireEvent('click', this, e);
1569     }
1570    
1571 });
1572
1573  /*
1574  * - LGPL
1575  *
1576  * header
1577  * 
1578  */
1579
1580 /**
1581  * @class Roo.bootstrap.Header
1582  * @extends Roo.bootstrap.Component
1583  * Bootstrap Header class
1584  * @cfg {String} html content of header
1585  * @cfg {Number} level (1|2|3|4|5|6) default 1
1586  * 
1587  * @constructor
1588  * Create a new Header
1589  * @param {Object} config The config object
1590  */
1591
1592
1593 Roo.bootstrap.Header  = function(config){
1594     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1595 };
1596
1597 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1598     
1599     //href : false,
1600     html : false,
1601     level : 1,
1602     
1603     
1604     
1605     getAutoCreate : function(){
1606         
1607         
1608         
1609         var cfg = {
1610             tag: 'h' + (1 *this.level),
1611             html: this.html || ''
1612         } ;
1613         
1614         return cfg;
1615     }
1616    
1617 });
1618
1619  
1620
1621  /*
1622  * Based on:
1623  * Ext JS Library 1.1.1
1624  * Copyright(c) 2006-2007, Ext JS, LLC.
1625  *
1626  * Originally Released Under LGPL - original licence link has changed is not relivant.
1627  *
1628  * Fork - LGPL
1629  * <script type="text/javascript">
1630  */
1631  
1632 /**
1633  * @class Roo.bootstrap.MenuMgr
1634  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1635  * @singleton
1636  */
1637 Roo.bootstrap.MenuMgr = function(){
1638    var menus, active, groups = {}, attached = false, lastShow = new Date();
1639
1640    // private - called when first menu is created
1641    function init(){
1642        menus = {};
1643        active = new Roo.util.MixedCollection();
1644        Roo.get(document).addKeyListener(27, function(){
1645            if(active.length > 0){
1646                hideAll();
1647            }
1648        });
1649    }
1650
1651    // private
1652    function hideAll(){
1653        if(active && active.length > 0){
1654            var c = active.clone();
1655            c.each(function(m){
1656                m.hide();
1657            });
1658        }
1659    }
1660
1661    // private
1662    function onHide(m){
1663        active.remove(m);
1664        if(active.length < 1){
1665            Roo.get(document).un("mouseup", onMouseDown);
1666             
1667            attached = false;
1668        }
1669    }
1670
1671    // private
1672    function onShow(m){
1673        var last = active.last();
1674        lastShow = new Date();
1675        active.add(m);
1676        if(!attached){
1677           Roo.get(document).on("mouseup", onMouseDown);
1678            
1679            attached = true;
1680        }
1681        if(m.parentMenu){
1682           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1683           m.parentMenu.activeChild = m;
1684        }else if(last && last.isVisible()){
1685           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1686        }
1687    }
1688
1689    // private
1690    function onBeforeHide(m){
1691        if(m.activeChild){
1692            m.activeChild.hide();
1693        }
1694        if(m.autoHideTimer){
1695            clearTimeout(m.autoHideTimer);
1696            delete m.autoHideTimer;
1697        }
1698    }
1699
1700    // private
1701    function onBeforeShow(m){
1702        var pm = m.parentMenu;
1703        if(!pm && !m.allowOtherMenus){
1704            hideAll();
1705        }else if(pm && pm.activeChild && active != m){
1706            pm.activeChild.hide();
1707        }
1708    }
1709
1710    // private this should really trigger on mouseup..
1711    function onMouseDown(e){
1712         Roo.log("on Mouse Up");
1713         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1714             Roo.log("hideAll");
1715             hideAll();
1716             e.stopEvent();
1717         }
1718         
1719         
1720    }
1721
1722    // private
1723    function onBeforeCheck(mi, state){
1724        if(state){
1725            var g = groups[mi.group];
1726            for(var i = 0, l = g.length; i < l; i++){
1727                if(g[i] != mi){
1728                    g[i].setChecked(false);
1729                }
1730            }
1731        }
1732    }
1733
1734    return {
1735
1736        /**
1737         * Hides all menus that are currently visible
1738         */
1739        hideAll : function(){
1740             hideAll();  
1741        },
1742
1743        // private
1744        register : function(menu){
1745            if(!menus){
1746                init();
1747            }
1748            menus[menu.id] = menu;
1749            menu.on("beforehide", onBeforeHide);
1750            menu.on("hide", onHide);
1751            menu.on("beforeshow", onBeforeShow);
1752            menu.on("show", onShow);
1753            var g = menu.group;
1754            if(g && menu.events["checkchange"]){
1755                if(!groups[g]){
1756                    groups[g] = [];
1757                }
1758                groups[g].push(menu);
1759                menu.on("checkchange", onCheck);
1760            }
1761        },
1762
1763         /**
1764          * Returns a {@link Roo.menu.Menu} object
1765          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1766          * be used to generate and return a new Menu instance.
1767          */
1768        get : function(menu){
1769            if(typeof menu == "string"){ // menu id
1770                return menus[menu];
1771            }else if(menu.events){  // menu instance
1772                return menu;
1773            }
1774            /*else if(typeof menu.length == 'number'){ // array of menu items?
1775                return new Roo.bootstrap.Menu({items:menu});
1776            }else{ // otherwise, must be a config
1777                return new Roo.bootstrap.Menu(menu);
1778            }
1779            */
1780            return false;
1781        },
1782
1783        // private
1784        unregister : function(menu){
1785            delete menus[menu.id];
1786            menu.un("beforehide", onBeforeHide);
1787            menu.un("hide", onHide);
1788            menu.un("beforeshow", onBeforeShow);
1789            menu.un("show", onShow);
1790            var g = menu.group;
1791            if(g && menu.events["checkchange"]){
1792                groups[g].remove(menu);
1793                menu.un("checkchange", onCheck);
1794            }
1795        },
1796
1797        // private
1798        registerCheckable : function(menuItem){
1799            var g = menuItem.group;
1800            if(g){
1801                if(!groups[g]){
1802                    groups[g] = [];
1803                }
1804                groups[g].push(menuItem);
1805                menuItem.on("beforecheckchange", onBeforeCheck);
1806            }
1807        },
1808
1809        // private
1810        unregisterCheckable : function(menuItem){
1811            var g = menuItem.group;
1812            if(g){
1813                groups[g].remove(menuItem);
1814                menuItem.un("beforecheckchange", onBeforeCheck);
1815            }
1816        }
1817    };
1818 }();/*
1819  * - LGPL
1820  *
1821  * menu
1822  * 
1823  */
1824
1825 /**
1826  * @class Roo.bootstrap.Menu
1827  * @extends Roo.bootstrap.Component
1828  * Bootstrap Menu class - container for MenuItems
1829  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1830  * 
1831  * @constructor
1832  * Create a new Menu
1833  * @param {Object} config The config object
1834  */
1835
1836
1837 Roo.bootstrap.Menu = function(config){
1838     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1839     if (this.registerMenu) {
1840         Roo.bootstrap.MenuMgr.register(this);
1841     }
1842     this.addEvents({
1843         /**
1844          * @event beforeshow
1845          * Fires before this menu is displayed
1846          * @param {Roo.menu.Menu} this
1847          */
1848         beforeshow : true,
1849         /**
1850          * @event beforehide
1851          * Fires before this menu is hidden
1852          * @param {Roo.menu.Menu} this
1853          */
1854         beforehide : true,
1855         /**
1856          * @event show
1857          * Fires after this menu is displayed
1858          * @param {Roo.menu.Menu} this
1859          */
1860         show : true,
1861         /**
1862          * @event hide
1863          * Fires after this menu is hidden
1864          * @param {Roo.menu.Menu} this
1865          */
1866         hide : true,
1867         /**
1868          * @event click
1869          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1870          * @param {Roo.menu.Menu} this
1871          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1872          * @param {Roo.EventObject} e
1873          */
1874         click : true,
1875         /**
1876          * @event mouseover
1877          * Fires when the mouse is hovering over this menu
1878          * @param {Roo.menu.Menu} this
1879          * @param {Roo.EventObject} e
1880          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1881          */
1882         mouseover : true,
1883         /**
1884          * @event mouseout
1885          * Fires when the mouse exits this menu
1886          * @param {Roo.menu.Menu} this
1887          * @param {Roo.EventObject} e
1888          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1889          */
1890         mouseout : true,
1891         /**
1892          * @event itemclick
1893          * Fires when a menu item contained in this menu is clicked
1894          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1895          * @param {Roo.EventObject} e
1896          */
1897         itemclick: true
1898     });
1899     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1900 };
1901
1902 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1903     
1904    /// html : false,
1905     //align : '',
1906     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1907     type: false,
1908     /**
1909      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1910      */
1911     registerMenu : true,
1912     
1913     menuItems :false, // stores the menu items..
1914     
1915     hidden:true,
1916     
1917     parentMenu : false,
1918     
1919     getChildContainer : function() {
1920         return this.el;  
1921     },
1922     
1923     getAutoCreate : function(){
1924          
1925         //if (['right'].indexOf(this.align)!==-1) {
1926         //    cfg.cn[1].cls += ' pull-right'
1927         //}
1928         
1929         
1930         var cfg = {
1931             tag : 'ul',
1932             cls : 'dropdown-menu' ,
1933             style : 'z-index:1000'
1934             
1935         }
1936         
1937         if (this.type === 'submenu') {
1938             cfg.cls = 'submenu active';
1939         }
1940         if (this.type === 'treeview') {
1941             cfg.cls = 'treeview-menu';
1942         }
1943         
1944         return cfg;
1945     },
1946     initEvents : function() {
1947         
1948        // Roo.log("ADD event");
1949        // Roo.log(this.triggerEl.dom);
1950         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1951         
1952         this.triggerEl.addClass('dropdown-toggle');
1953         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1954
1955         this.el.on("mouseover", this.onMouseOver, this);
1956         this.el.on("mouseout", this.onMouseOut, this);
1957         
1958         
1959     },
1960     findTargetItem : function(e){
1961         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1962         if(!t){
1963             return false;
1964         }
1965         //Roo.log(t);         Roo.log(t.id);
1966         if(t && t.id){
1967             //Roo.log(this.menuitems);
1968             return this.menuitems.get(t.id);
1969             
1970             //return this.items.get(t.menuItemId);
1971         }
1972         
1973         return false;
1974     },
1975     onClick : function(e){
1976         Roo.log("menu.onClick");
1977         var t = this.findTargetItem(e);
1978         if(!t || t.isContainer){
1979             return;
1980         }
1981         Roo.log(e);
1982         /*
1983         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1984             if(t == this.activeItem && t.shouldDeactivate(e)){
1985                 this.activeItem.deactivate();
1986                 delete this.activeItem;
1987                 return;
1988             }
1989             if(t.canActivate){
1990                 this.setActiveItem(t, true);
1991             }
1992             return;
1993             
1994             
1995         }
1996         */
1997        
1998         Roo.log('pass click event');
1999         
2000         t.onClick(e);
2001         
2002         this.fireEvent("click", this, t, e);
2003         
2004         this.hide();
2005     },
2006      onMouseOver : function(e){
2007         var t  = this.findTargetItem(e);
2008         //Roo.log(t);
2009         //if(t){
2010         //    if(t.canActivate && !t.disabled){
2011         //        this.setActiveItem(t, true);
2012         //    }
2013         //}
2014         
2015         this.fireEvent("mouseover", this, e, t);
2016     },
2017     isVisible : function(){
2018         return !this.hidden;
2019     },
2020      onMouseOut : function(e){
2021         var t  = this.findTargetItem(e);
2022         
2023         //if(t ){
2024         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2025         //        this.activeItem.deactivate();
2026         //        delete this.activeItem;
2027         //    }
2028         //}
2029         this.fireEvent("mouseout", this, e, t);
2030     },
2031     
2032     
2033     /**
2034      * Displays this menu relative to another element
2035      * @param {String/HTMLElement/Roo.Element} element The element to align to
2036      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2037      * the element (defaults to this.defaultAlign)
2038      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2039      */
2040     show : function(el, pos, parentMenu){
2041         this.parentMenu = parentMenu;
2042         if(!this.el){
2043             this.render();
2044         }
2045         this.fireEvent("beforeshow", this);
2046         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2047     },
2048      /**
2049      * Displays this menu at a specific xy position
2050      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2051      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2052      */
2053     showAt : function(xy, parentMenu, /* private: */_e){
2054         this.parentMenu = parentMenu;
2055         if(!this.el){
2056             this.render();
2057         }
2058         if(_e !== false){
2059             this.fireEvent("beforeshow", this);
2060             //xy = this.el.adjustForConstraints(xy);
2061         }
2062         
2063         //this.el.show();
2064         this.hideMenuItems();
2065         this.hidden = false;
2066         this.triggerEl.addClass('open');
2067         
2068         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2069             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2070         }
2071         
2072         this.el.setXY(xy);
2073         this.focus();
2074         this.fireEvent("show", this);
2075     },
2076     
2077     focus : function(){
2078         return;
2079         if(!this.hidden){
2080             this.doFocus.defer(50, this);
2081         }
2082     },
2083
2084     doFocus : function(){
2085         if(!this.hidden){
2086             this.focusEl.focus();
2087         }
2088     },
2089
2090     /**
2091      * Hides this menu and optionally all parent menus
2092      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2093      */
2094     hide : function(deep){
2095         
2096         this.hideMenuItems();
2097         if(this.el && this.isVisible()){
2098             this.fireEvent("beforehide", this);
2099             if(this.activeItem){
2100                 this.activeItem.deactivate();
2101                 this.activeItem = null;
2102             }
2103             this.triggerEl.removeClass('open');;
2104             this.hidden = true;
2105             this.fireEvent("hide", this);
2106         }
2107         if(deep === true && this.parentMenu){
2108             this.parentMenu.hide(true);
2109         }
2110     },
2111     
2112     onTriggerPress  : function(e)
2113     {
2114         
2115         Roo.log('trigger press');
2116         //Roo.log(e.getTarget());
2117        // Roo.log(this.triggerEl.dom);
2118         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2119             return;
2120         }
2121         
2122         if (this.isVisible()) {
2123             Roo.log('hide');
2124             this.hide();
2125         } else {
2126             Roo.log('show');
2127             this.show(this.triggerEl, false, false);
2128         }
2129         
2130         e.stopEvent();
2131     },
2132     
2133          
2134        
2135     
2136     hideMenuItems : function()
2137     {
2138         //$(backdrop).remove()
2139         Roo.select('.open',true).each(function(aa) {
2140             
2141             aa.removeClass('open');
2142           //var parent = getParent($(this))
2143           //var relatedTarget = { relatedTarget: this }
2144           
2145            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2146           //if (e.isDefaultPrevented()) return
2147            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2148         })
2149     },
2150     addxtypeChild : function (tree, cntr) {
2151         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2152           
2153         this.menuitems.add(comp);
2154         return comp;
2155
2156     },
2157     getEl : function()
2158     {
2159         Roo.log(this.el);
2160         return this.el;
2161     }
2162 });
2163
2164  
2165  /*
2166  * - LGPL
2167  *
2168  * menu item
2169  * 
2170  */
2171
2172
2173 /**
2174  * @class Roo.bootstrap.MenuItem
2175  * @extends Roo.bootstrap.Component
2176  * Bootstrap MenuItem class
2177  * @cfg {String} html the menu label
2178  * @cfg {String} href the link
2179  * @cfg {Boolean} preventDefault (true | false) default true
2180  * @cfg {Boolean} isContainer (true | false) default false
2181  * 
2182  * 
2183  * @constructor
2184  * Create a new MenuItem
2185  * @param {Object} config The config object
2186  */
2187
2188
2189 Roo.bootstrap.MenuItem = function(config){
2190     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2191     this.addEvents({
2192         // raw events
2193         /**
2194          * @event click
2195          * The raw click event for the entire grid.
2196          * @param {Roo.bootstrap.MenuItem} this
2197          * @param {Roo.EventObject} e
2198          */
2199         "click" : true
2200     });
2201 };
2202
2203 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2204     
2205     href : false,
2206     html : false,
2207     preventDefault: true,
2208     isContainer : false,
2209     
2210     getAutoCreate : function(){
2211         
2212         if(this.isContainer){
2213             return {
2214                 tag: 'li',
2215                 cls: 'dropdown-menu-item'
2216             };
2217         }
2218         
2219         var cfg= {
2220             tag: 'li',
2221             cls: 'dropdown-menu-item',
2222             cn: [
2223                     {
2224                         tag : 'a',
2225                         href : '#',
2226                         html : 'Link'
2227                     }
2228                 ]
2229         };
2230         if (this.parent().type == 'treeview') {
2231             cfg.cls = 'treeview-menu';
2232         }
2233         
2234         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2235         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2236         return cfg;
2237     },
2238     
2239     initEvents: function() {
2240         
2241         //this.el.select('a').on('click', this.onClick, this);
2242         
2243     },
2244     onClick : function(e)
2245     {
2246         Roo.log('item on click ');
2247         //if(this.preventDefault){
2248         //    e.preventDefault();
2249         //}
2250         //this.parent().hideMenuItems();
2251         
2252         this.fireEvent('click', this, e);
2253     },
2254     getEl : function()
2255     {
2256         return this.el;
2257     }
2258 });
2259
2260  
2261
2262  /*
2263  * - LGPL
2264  *
2265  * menu separator
2266  * 
2267  */
2268
2269
2270 /**
2271  * @class Roo.bootstrap.MenuSeparator
2272  * @extends Roo.bootstrap.Component
2273  * Bootstrap MenuSeparator class
2274  * 
2275  * @constructor
2276  * Create a new MenuItem
2277  * @param {Object} config The config object
2278  */
2279
2280
2281 Roo.bootstrap.MenuSeparator = function(config){
2282     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2283 };
2284
2285 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2286     
2287     getAutoCreate : function(){
2288         var cfg = {
2289             cls: 'divider',
2290             tag : 'li'
2291         };
2292         
2293         return cfg;
2294     }
2295    
2296 });
2297
2298  
2299
2300  
2301 /*
2302 * Licence: LGPL
2303 */
2304
2305 /**
2306  * @class Roo.bootstrap.Modal
2307  * @extends Roo.bootstrap.Component
2308  * Bootstrap Modal class
2309  * @cfg {String} title Title of dialog
2310  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2311  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2312  * @cfg {Boolean} specificTitle default false
2313  * @cfg {Array} buttons Array of buttons or standard button set..
2314  * @cfg {String} buttonPosition (left|right|center) default right
2315  * @cfg {Boolean} animate default true
2316  * @cfg {Boolean} allow_close default true
2317  * 
2318  * @constructor
2319  * Create a new Modal Dialog
2320  * @param {Object} config The config object
2321  */
2322
2323 Roo.bootstrap.Modal = function(config){
2324     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event btnclick
2329          * The raw btnclick event for the button
2330          * @param {Roo.EventObject} e
2331          */
2332         "btnclick" : true
2333     });
2334     this.buttons = this.buttons || [];
2335      
2336     if (this.tmpl) {
2337         this.tmpl = Roo.factory(this.tmpl);
2338     }
2339     
2340 };
2341
2342 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2343     
2344     title : 'test dialog',
2345    
2346     buttons : false,
2347     
2348     // set on load...
2349      
2350     html: false,
2351     
2352     tmp: false,
2353     
2354     specificTitle: false,
2355     
2356     buttonPosition: 'right',
2357     
2358     allow_close : true,
2359     
2360     animate : true,
2361     
2362     
2363      // private
2364     bodyEl:  false,
2365     footerEl:  false,
2366     titleEl:  false,
2367     closeEl:  false,
2368     
2369     
2370     onRender : function(ct, position)
2371     {
2372         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2373      
2374         if(!this.el){
2375             var cfg = Roo.apply({},  this.getAutoCreate());
2376             cfg.id = Roo.id();
2377             //if(!cfg.name){
2378             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2379             //}
2380             //if (!cfg.name.length) {
2381             //    delete cfg.name;
2382            // }
2383             if (this.cls) {
2384                 cfg.cls += ' ' + this.cls;
2385             }
2386             if (this.style) {
2387                 cfg.style = this.style;
2388             }
2389             this.el = Roo.get(document.body).createChild(cfg, position);
2390         }
2391         //var type = this.el.dom.type;
2392         
2393         
2394         
2395         
2396         if(this.tabIndex !== undefined){
2397             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2398         }
2399         
2400         
2401         this.bodyEl = this.el.select('.modal-body',true).first();
2402         this.closeEl = this.el.select('.modal-header .close', true).first();
2403         this.footerEl = this.el.select('.modal-footer',true).first();
2404         this.titleEl = this.el.select('.modal-title',true).first();
2405         
2406         
2407          
2408         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2409         this.maskEl.enableDisplayMode("block");
2410         this.maskEl.hide();
2411         //this.el.addClass("x-dlg-modal");
2412     
2413         if (this.buttons.length) {
2414             Roo.each(this.buttons, function(bb) {
2415                 b = Roo.apply({}, bb);
2416                 b.xns = b.xns || Roo.bootstrap;
2417                 b.xtype = b.xtype || 'Button';
2418                 if (typeof(b.listeners) == 'undefined') {
2419                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2420                 }
2421                 
2422                 var btn = Roo.factory(b);
2423                 
2424                 btn.onRender(this.el.select('.modal-footer div').first());
2425                 
2426             },this);
2427         }
2428         // render the children.
2429         var nitems = [];
2430         
2431         if(typeof(this.items) != 'undefined'){
2432             var items = this.items;
2433             delete this.items;
2434
2435             for(var i =0;i < items.length;i++) {
2436                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2437             }
2438         }
2439         
2440         this.items = nitems;
2441         
2442         // where are these used - they used to be body/close/footer
2443         
2444        
2445         this.initEvents();
2446         //this.el.addClass([this.fieldClass, this.cls]);
2447         
2448     },
2449     getAutoCreate : function(){
2450         
2451         
2452         var bdy = {
2453                 cls : 'modal-body',
2454                 html : this.html || ''
2455         };
2456         
2457         var title = {
2458             tag: 'h4',
2459             cls : 'modal-title',
2460             html : this.title
2461         };
2462         
2463         if(this.specificTitle){
2464             title = this.title;
2465             
2466         };
2467         
2468         var header = [];
2469         if (this.allow_close) {
2470             header.push({
2471                 tag: 'button',
2472                 cls : 'close',
2473                 html : '&times'
2474             });
2475         }
2476         header.push(title);
2477         
2478         var modal = {
2479             cls: "modal",
2480             style : 'display: none',
2481             cn : [
2482                 {
2483                     cls: "modal-dialog",
2484                     cn : [
2485                         {
2486                             cls : "modal-content",
2487                             cn : [
2488                                 {
2489                                     cls : 'modal-header',
2490                                     cn : header
2491                                 },
2492                                 bdy,
2493                                 {
2494                                     cls : 'modal-footer',
2495                                     cn : [
2496                                         {
2497                                             tag: 'div',
2498                                             cls: 'btn-' + this.buttonPosition
2499                                         }
2500                                     ]
2501                                     
2502                                 }
2503                                 
2504                                 
2505                             ]
2506                             
2507                         }
2508                     ]
2509                         
2510                 }
2511             ]
2512         };
2513         
2514         if(this.animate){
2515             modal.cls += ' fade';
2516         }
2517         
2518         return modal;
2519           
2520     },
2521     getChildContainer : function() {
2522          
2523          return this.bodyEl;
2524         
2525     },
2526     getButtonContainer : function() {
2527          return this.el.select('.modal-footer div',true).first();
2528         
2529     },
2530     initEvents : function()
2531     {
2532         if (this.allow_close) {
2533             this.closeEl.on('click', this.hide, this);
2534         }
2535
2536     },
2537     show : function() {
2538         
2539         if (!this.rendered) {
2540             this.render();
2541         }
2542         
2543         this.el.setStyle('display', 'block');
2544         
2545         if(this.animate){
2546             var _this = this;
2547             (function(){ _this.el.addClass('in'); }).defer(50);
2548         }else{
2549             this.el.addClass('in');
2550         }
2551         
2552         // not sure how we can show data in here.. 
2553         //if (this.tmpl) {
2554         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2555         //}
2556         
2557         Roo.get(document.body).addClass("x-body-masked");
2558         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2559         this.maskEl.show();
2560         this.el.setStyle('zIndex', '10001');
2561        
2562         this.fireEvent('show', this);
2563         
2564         
2565     },
2566     hide : function()
2567     {
2568         this.maskEl.hide();
2569         Roo.get(document.body).removeClass("x-body-masked");
2570         this.el.removeClass('in');
2571         
2572         if(this.animate){
2573             var _this = this;
2574             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2575         }else{
2576             this.el.setStyle('display', 'none');
2577         }
2578         
2579         this.fireEvent('hide', this);
2580     },
2581     
2582     addButton : function(str, cb)
2583     {
2584          
2585         
2586         var b = Roo.apply({}, { html : str } );
2587         b.xns = b.xns || Roo.bootstrap;
2588         b.xtype = b.xtype || 'Button';
2589         if (typeof(b.listeners) == 'undefined') {
2590             b.listeners = { click : cb.createDelegate(this)  };
2591         }
2592         
2593         var btn = Roo.factory(b);
2594            
2595         btn.onRender(this.el.select('.modal-footer div').first());
2596         
2597         return btn;   
2598        
2599     },
2600     
2601     setDefaultButton : function(btn)
2602     {
2603         //this.el.select('.modal-footer').()
2604     },
2605     resizeTo: function(w,h)
2606     {
2607         // skip..
2608     },
2609     setContentSize  : function(w, h)
2610     {
2611         
2612     },
2613     onButtonClick: function(btn,e)
2614     {
2615         //Roo.log([a,b,c]);
2616         this.fireEvent('btnclick', btn.name, e);
2617     },
2618      /**
2619      * Set the title of the Dialog
2620      * @param {String} str new Title
2621      */
2622     setTitle: function(str) {
2623         this.titleEl.dom.innerHTML = str;    
2624     },
2625     /**
2626      * Set the body of the Dialog
2627      * @param {String} str new Title
2628      */
2629     setBody: function(str) {
2630         this.bodyEl.dom.innerHTML = str;    
2631     },
2632     /**
2633      * Set the body of the Dialog using the template
2634      * @param {Obj} data - apply this data to the template and replace the body contents.
2635      */
2636     applyBody: function(obj)
2637     {
2638         if (!this.tmpl) {
2639             Roo.log("Error - using apply Body without a template");
2640             //code
2641         }
2642         this.tmpl.overwrite(this.bodyEl, obj);
2643     }
2644     
2645 });
2646
2647
2648 Roo.apply(Roo.bootstrap.Modal,  {
2649     /**
2650          * Button config that displays a single OK button
2651          * @type Object
2652          */
2653         OK :  [{
2654             name : 'ok',
2655             weight : 'primary',
2656             html : 'OK'
2657         }], 
2658         /**
2659          * Button config that displays Yes and No buttons
2660          * @type Object
2661          */
2662         YESNO : [
2663             {
2664                 name  : 'no',
2665                 html : 'No'
2666             },
2667             {
2668                 name  :'yes',
2669                 weight : 'primary',
2670                 html : 'Yes'
2671             }
2672         ],
2673         
2674         /**
2675          * Button config that displays OK and Cancel buttons
2676          * @type Object
2677          */
2678         OKCANCEL : [
2679             {
2680                name : 'cancel',
2681                 html : 'Cancel'
2682             },
2683             {
2684                 name : 'ok',
2685                 weight : 'primary',
2686                 html : 'OK'
2687             }
2688         ],
2689         /**
2690          * Button config that displays Yes, No and Cancel buttons
2691          * @type Object
2692          */
2693         YESNOCANCEL : [
2694             {
2695                 name : 'yes',
2696                 weight : 'primary',
2697                 html : 'Yes'
2698             },
2699             {
2700                 name : 'no',
2701                 html : 'No'
2702             },
2703             {
2704                 name : 'cancel',
2705                 html : 'Cancel'
2706             }
2707         ]
2708 });
2709  
2710  /*
2711  * - LGPL
2712  *
2713  * messagebox - can be used as a replace
2714  * 
2715  */
2716 /**
2717  * @class Roo.MessageBox
2718  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2719  * Example usage:
2720  *<pre><code>
2721 // Basic alert:
2722 Roo.Msg.alert('Status', 'Changes saved successfully.');
2723
2724 // Prompt for user data:
2725 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2726     if (btn == 'ok'){
2727         // process text value...
2728     }
2729 });
2730
2731 // Show a dialog using config options:
2732 Roo.Msg.show({
2733    title:'Save Changes?',
2734    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2735    buttons: Roo.Msg.YESNOCANCEL,
2736    fn: processResult,
2737    animEl: 'elId'
2738 });
2739 </code></pre>
2740  * @singleton
2741  */
2742 Roo.bootstrap.MessageBox = function(){
2743     var dlg, opt, mask, waitTimer;
2744     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2745     var buttons, activeTextEl, bwidth;
2746
2747     
2748     // private
2749     var handleButton = function(button){
2750         dlg.hide();
2751         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2752     };
2753
2754     // private
2755     var handleHide = function(){
2756         if(opt && opt.cls){
2757             dlg.el.removeClass(opt.cls);
2758         }
2759         //if(waitTimer){
2760         //    Roo.TaskMgr.stop(waitTimer);
2761         //    waitTimer = null;
2762         //}
2763     };
2764
2765     // private
2766     var updateButtons = function(b){
2767         var width = 0;
2768         if(!b){
2769             buttons["ok"].hide();
2770             buttons["cancel"].hide();
2771             buttons["yes"].hide();
2772             buttons["no"].hide();
2773             //dlg.footer.dom.style.display = 'none';
2774             return width;
2775         }
2776         dlg.footerEl.dom.style.display = '';
2777         for(var k in buttons){
2778             if(typeof buttons[k] != "function"){
2779                 if(b[k]){
2780                     buttons[k].show();
2781                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2782                     width += buttons[k].el.getWidth()+15;
2783                 }else{
2784                     buttons[k].hide();
2785                 }
2786             }
2787         }
2788         return width;
2789     };
2790
2791     // private
2792     var handleEsc = function(d, k, e){
2793         if(opt && opt.closable !== false){
2794             dlg.hide();
2795         }
2796         if(e){
2797             e.stopEvent();
2798         }
2799     };
2800
2801     return {
2802         /**
2803          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2804          * @return {Roo.BasicDialog} The BasicDialog element
2805          */
2806         getDialog : function(){
2807            if(!dlg){
2808                 dlg = new Roo.bootstrap.Modal( {
2809                     //draggable: true,
2810                     //resizable:false,
2811                     //constraintoviewport:false,
2812                     //fixedcenter:true,
2813                     //collapsible : false,
2814                     //shim:true,
2815                     //modal: true,
2816                   //  width:400,
2817                   //  height:100,
2818                     //buttonAlign:"center",
2819                     closeClick : function(){
2820                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2821                             handleButton("no");
2822                         }else{
2823                             handleButton("cancel");
2824                         }
2825                     }
2826                 });
2827                 dlg.render();
2828                 dlg.on("hide", handleHide);
2829                 mask = dlg.mask;
2830                 //dlg.addKeyListener(27, handleEsc);
2831                 buttons = {};
2832                 this.buttons = buttons;
2833                 var bt = this.buttonText;
2834                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2835                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2836                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2837                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2838                 Roo.log(buttons)
2839                 bodyEl = dlg.bodyEl.createChild({
2840
2841                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2842                         '<textarea class="roo-mb-textarea"></textarea>' +
2843                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2844                 });
2845                 msgEl = bodyEl.dom.firstChild;
2846                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2847                 textboxEl.enableDisplayMode();
2848                 textboxEl.addKeyListener([10,13], function(){
2849                     if(dlg.isVisible() && opt && opt.buttons){
2850                         if(opt.buttons.ok){
2851                             handleButton("ok");
2852                         }else if(opt.buttons.yes){
2853                             handleButton("yes");
2854                         }
2855                     }
2856                 });
2857                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2858                 textareaEl.enableDisplayMode();
2859                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2860                 progressEl.enableDisplayMode();
2861                 var pf = progressEl.dom.firstChild;
2862                 if (pf) {
2863                     pp = Roo.get(pf.firstChild);
2864                     pp.setHeight(pf.offsetHeight);
2865                 }
2866                 
2867             }
2868             return dlg;
2869         },
2870
2871         /**
2872          * Updates the message box body text
2873          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2874          * the XHTML-compliant non-breaking space character '&amp;#160;')
2875          * @return {Roo.MessageBox} This message box
2876          */
2877         updateText : function(text){
2878             if(!dlg.isVisible() && !opt.width){
2879                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2880             }
2881             msgEl.innerHTML = text || '&#160;';
2882       
2883             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2884             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2885             var w = Math.max(
2886                     Math.min(opt.width || cw , this.maxWidth), 
2887                     Math.max(opt.minWidth || this.minWidth, bwidth)
2888             );
2889             if(opt.prompt){
2890                 activeTextEl.setWidth(w);
2891             }
2892             if(dlg.isVisible()){
2893                 dlg.fixedcenter = false;
2894             }
2895             // to big, make it scroll. = But as usual stupid IE does not support
2896             // !important..
2897             
2898             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2899                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2900                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2901             } else {
2902                 bodyEl.dom.style.height = '';
2903                 bodyEl.dom.style.overflowY = '';
2904             }
2905             if (cw > w) {
2906                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2907             } else {
2908                 bodyEl.dom.style.overflowX = '';
2909             }
2910             
2911             dlg.setContentSize(w, bodyEl.getHeight());
2912             if(dlg.isVisible()){
2913                 dlg.fixedcenter = true;
2914             }
2915             return this;
2916         },
2917
2918         /**
2919          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2920          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2921          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2922          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2923          * @return {Roo.MessageBox} This message box
2924          */
2925         updateProgress : function(value, text){
2926             if(text){
2927                 this.updateText(text);
2928             }
2929             if (pp) { // weird bug on my firefox - for some reason this is not defined
2930                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2931             }
2932             return this;
2933         },        
2934
2935         /**
2936          * Returns true if the message box is currently displayed
2937          * @return {Boolean} True if the message box is visible, else false
2938          */
2939         isVisible : function(){
2940             return dlg && dlg.isVisible();  
2941         },
2942
2943         /**
2944          * Hides the message box if it is displayed
2945          */
2946         hide : function(){
2947             if(this.isVisible()){
2948                 dlg.hide();
2949             }  
2950         },
2951
2952         /**
2953          * Displays a new message box, or reinitializes an existing message box, based on the config options
2954          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2955          * The following config object properties are supported:
2956          * <pre>
2957 Property    Type             Description
2958 ----------  ---------------  ------------------------------------------------------------------------------------
2959 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2960                                    closes (defaults to undefined)
2961 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2962                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2963 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2964                                    progress and wait dialogs will ignore this property and always hide the
2965                                    close button as they can only be closed programmatically.
2966 cls               String           A custom CSS class to apply to the message box element
2967 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2968                                    displayed (defaults to 75)
2969 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2970                                    function will be btn (the name of the button that was clicked, if applicable,
2971                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2972                                    Progress and wait dialogs will ignore this option since they do not respond to
2973                                    user actions and can only be closed programmatically, so any required function
2974                                    should be called by the same code after it closes the dialog.
2975 icon              String           A CSS class that provides a background image to be used as an icon for
2976                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2977 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2978 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2979 modal             Boolean          False to allow user interaction with the page while the message box is
2980                                    displayed (defaults to true)
2981 msg               String           A string that will replace the existing message box body text (defaults
2982                                    to the XHTML-compliant non-breaking space character '&#160;')
2983 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2984 progress          Boolean          True to display a progress bar (defaults to false)
2985 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2986 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2987 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2988 title             String           The title text
2989 value             String           The string value to set into the active textbox element if displayed
2990 wait              Boolean          True to display a progress bar (defaults to false)
2991 width             Number           The width of the dialog in pixels
2992 </pre>
2993          *
2994          * Example usage:
2995          * <pre><code>
2996 Roo.Msg.show({
2997    title: 'Address',
2998    msg: 'Please enter your address:',
2999    width: 300,
3000    buttons: Roo.MessageBox.OKCANCEL,
3001    multiline: true,
3002    fn: saveAddress,
3003    animEl: 'addAddressBtn'
3004 });
3005 </code></pre>
3006          * @param {Object} config Configuration options
3007          * @return {Roo.MessageBox} This message box
3008          */
3009         show : function(options)
3010         {
3011             
3012             // this causes nightmares if you show one dialog after another
3013             // especially on callbacks..
3014              
3015             if(this.isVisible()){
3016                 
3017                 this.hide();
3018                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3019                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3020                 Roo.log("New Dialog Message:" +  options.msg )
3021                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3022                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3023                 
3024             }
3025             var d = this.getDialog();
3026             opt = options;
3027             d.setTitle(opt.title || "&#160;");
3028             d.closeEl.setDisplayed(opt.closable !== false);
3029             activeTextEl = textboxEl;
3030             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3031             if(opt.prompt){
3032                 if(opt.multiline){
3033                     textboxEl.hide();
3034                     textareaEl.show();
3035                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3036                         opt.multiline : this.defaultTextHeight);
3037                     activeTextEl = textareaEl;
3038                 }else{
3039                     textboxEl.show();
3040                     textareaEl.hide();
3041                 }
3042             }else{
3043                 textboxEl.hide();
3044                 textareaEl.hide();
3045             }
3046             progressEl.setDisplayed(opt.progress === true);
3047             this.updateProgress(0);
3048             activeTextEl.dom.value = opt.value || "";
3049             if(opt.prompt){
3050                 dlg.setDefaultButton(activeTextEl);
3051             }else{
3052                 var bs = opt.buttons;
3053                 var db = null;
3054                 if(bs && bs.ok){
3055                     db = buttons["ok"];
3056                 }else if(bs && bs.yes){
3057                     db = buttons["yes"];
3058                 }
3059                 dlg.setDefaultButton(db);
3060             }
3061             bwidth = updateButtons(opt.buttons);
3062             this.updateText(opt.msg);
3063             if(opt.cls){
3064                 d.el.addClass(opt.cls);
3065             }
3066             d.proxyDrag = opt.proxyDrag === true;
3067             d.modal = opt.modal !== false;
3068             d.mask = opt.modal !== false ? mask : false;
3069             if(!d.isVisible()){
3070                 // force it to the end of the z-index stack so it gets a cursor in FF
3071                 document.body.appendChild(dlg.el.dom);
3072                 d.animateTarget = null;
3073                 d.show(options.animEl);
3074             }
3075             return this;
3076         },
3077
3078         /**
3079          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3080          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3081          * and closing the message box when the process is complete.
3082          * @param {String} title The title bar text
3083          * @param {String} msg The message box body text
3084          * @return {Roo.MessageBox} This message box
3085          */
3086         progress : function(title, msg){
3087             this.show({
3088                 title : title,
3089                 msg : msg,
3090                 buttons: false,
3091                 progress:true,
3092                 closable:false,
3093                 minWidth: this.minProgressWidth,
3094                 modal : true
3095             });
3096             return this;
3097         },
3098
3099         /**
3100          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3101          * If a callback function is passed it will be called after the user clicks the button, and the
3102          * id of the button that was clicked will be passed as the only parameter to the callback
3103          * (could also be the top-right close button).
3104          * @param {String} title The title bar text
3105          * @param {String} msg The message box body text
3106          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3107          * @param {Object} scope (optional) The scope of the callback function
3108          * @return {Roo.MessageBox} This message box
3109          */
3110         alert : function(title, msg, fn, scope){
3111             this.show({
3112                 title : title,
3113                 msg : msg,
3114                 buttons: this.OK,
3115                 fn: fn,
3116                 scope : scope,
3117                 modal : true
3118             });
3119             return this;
3120         },
3121
3122         /**
3123          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3124          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3125          * You are responsible for closing the message box when the process is complete.
3126          * @param {String} msg The message box body text
3127          * @param {String} title (optional) The title bar text
3128          * @return {Roo.MessageBox} This message box
3129          */
3130         wait : function(msg, title){
3131             this.show({
3132                 title : title,
3133                 msg : msg,
3134                 buttons: false,
3135                 closable:false,
3136                 progress:true,
3137                 modal:true,
3138                 width:300,
3139                 wait:true
3140             });
3141             waitTimer = Roo.TaskMgr.start({
3142                 run: function(i){
3143                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3144                 },
3145                 interval: 1000
3146             });
3147             return this;
3148         },
3149
3150         /**
3151          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3152          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3153          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3154          * @param {String} title The title bar text
3155          * @param {String} msg The message box body text
3156          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3157          * @param {Object} scope (optional) The scope of the callback function
3158          * @return {Roo.MessageBox} This message box
3159          */
3160         confirm : function(title, msg, fn, scope){
3161             this.show({
3162                 title : title,
3163                 msg : msg,
3164                 buttons: this.YESNO,
3165                 fn: fn,
3166                 scope : scope,
3167                 modal : true
3168             });
3169             return this;
3170         },
3171
3172         /**
3173          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3174          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3175          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3176          * (could also be the top-right close button) and the text that was entered will be passed as the two
3177          * parameters to the callback.
3178          * @param {String} title The title bar text
3179          * @param {String} msg The message box body text
3180          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3181          * @param {Object} scope (optional) The scope of the callback function
3182          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3183          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         prompt : function(title, msg, fn, scope, multiline){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.OKCANCEL,
3191                 fn: fn,
3192                 minWidth:250,
3193                 scope : scope,
3194                 prompt:true,
3195                 multiline: multiline,
3196                 modal : true
3197             });
3198             return this;
3199         },
3200
3201         /**
3202          * Button config that displays a single OK button
3203          * @type Object
3204          */
3205         OK : {ok:true},
3206         /**
3207          * Button config that displays Yes and No buttons
3208          * @type Object
3209          */
3210         YESNO : {yes:true, no:true},
3211         /**
3212          * Button config that displays OK and Cancel buttons
3213          * @type Object
3214          */
3215         OKCANCEL : {ok:true, cancel:true},
3216         /**
3217          * Button config that displays Yes, No and Cancel buttons
3218          * @type Object
3219          */
3220         YESNOCANCEL : {yes:true, no:true, cancel:true},
3221
3222         /**
3223          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3224          * @type Number
3225          */
3226         defaultTextHeight : 75,
3227         /**
3228          * The maximum width in pixels of the message box (defaults to 600)
3229          * @type Number
3230          */
3231         maxWidth : 600,
3232         /**
3233          * The minimum width in pixels of the message box (defaults to 100)
3234          * @type Number
3235          */
3236         minWidth : 100,
3237         /**
3238          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3239          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3240          * @type Number
3241          */
3242         minProgressWidth : 250,
3243         /**
3244          * An object containing the default button text strings that can be overriden for localized language support.
3245          * Supported properties are: ok, cancel, yes and no.
3246          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3247          * @type Object
3248          */
3249         buttonText : {
3250             ok : "OK",
3251             cancel : "Cancel",
3252             yes : "Yes",
3253             no : "No"
3254         }
3255     };
3256 }();
3257
3258 /**
3259  * Shorthand for {@link Roo.MessageBox}
3260  */
3261 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3262 Roo.Msg = Roo.Msg || Roo.MessageBox;
3263 /*
3264  * - LGPL
3265  *
3266  * navbar
3267  * 
3268  */
3269
3270 /**
3271  * @class Roo.bootstrap.Navbar
3272  * @extends Roo.bootstrap.Component
3273  * Bootstrap Navbar class
3274
3275  * @constructor
3276  * Create a new Navbar
3277  * @param {Object} config The config object
3278  */
3279
3280
3281 Roo.bootstrap.Navbar = function(config){
3282     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3283     
3284 };
3285
3286 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3287     
3288     
3289    
3290     // private
3291     navItems : false,
3292     loadMask : false,
3293     
3294     
3295     getAutoCreate : function(){
3296         
3297         
3298         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3299         
3300     },
3301     
3302     initEvents :function ()
3303     {
3304         //Roo.log(this.el.select('.navbar-toggle',true));
3305         this.el.select('.navbar-toggle',true).on('click', function() {
3306            // Roo.log('click');
3307             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3308         }, this);
3309         
3310         var mark = {
3311             tag: "div",
3312             cls:"x-dlg-mask"
3313         }
3314         
3315         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3316         
3317         var size = this.el.getSize();
3318         this.maskEl.setSize(size.width, size.height);
3319         this.maskEl.enableDisplayMode("block");
3320         this.maskEl.hide();
3321         
3322         if(this.loadMask){
3323             this.maskEl.show();
3324         }
3325     },
3326     
3327     
3328     getChildContainer : function()
3329     {
3330         if (this.el.select('.collapse').getCount()) {
3331             return this.el.select('.collapse',true).first();
3332         }
3333         
3334         return this.el;
3335     },
3336     
3337     mask : function()
3338     {
3339         this.maskEl.show();
3340     },
3341     
3342     unmask : function()
3343     {
3344         this.maskEl.hide();
3345     } 
3346     
3347     
3348     
3349     
3350 });
3351
3352
3353
3354  
3355
3356  /*
3357  * - LGPL
3358  *
3359  * navbar
3360  * 
3361  */
3362
3363 /**
3364  * @class Roo.bootstrap.NavSimplebar
3365  * @extends Roo.bootstrap.Navbar
3366  * Bootstrap Sidebar class
3367  *
3368  * @cfg {Boolean} inverse is inverted color
3369  * 
3370  * @cfg {String} type (nav | pills | tabs)
3371  * @cfg {Boolean} arrangement stacked | justified
3372  * @cfg {String} align (left | right) alignment
3373  * 
3374  * @cfg {Boolean} main (true|false) main nav bar? default false
3375  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3376  * 
3377  * @cfg {String} tag (header|footer|nav|div) default is nav 
3378
3379  * 
3380  * 
3381  * 
3382  * @constructor
3383  * Create a new Sidebar
3384  * @param {Object} config The config object
3385  */
3386
3387
3388 Roo.bootstrap.NavSimplebar = function(config){
3389     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3390 };
3391
3392 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3393     
3394     inverse: false,
3395     
3396     type: false,
3397     arrangement: '',
3398     align : false,
3399     
3400     
3401     
3402     main : false,
3403     
3404     
3405     tag : false,
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         var cfg = {
3412             tag : this.tag || 'div',
3413             cls : 'navbar'
3414         };
3415           
3416         
3417         cfg.cn = [
3418             {
3419                 cls: 'nav',
3420                 tag : 'ul'
3421             }
3422         ];
3423         
3424          
3425         this.type = this.type || 'nav';
3426         if (['tabs','pills'].indexOf(this.type)!==-1) {
3427             cfg.cn[0].cls += ' nav-' + this.type
3428         
3429         
3430         } else {
3431             if (this.type!=='nav') {
3432                 Roo.log('nav type must be nav/tabs/pills')
3433             }
3434             cfg.cn[0].cls += ' navbar-nav'
3435         }
3436         
3437         
3438         
3439         
3440         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3441             cfg.cn[0].cls += ' nav-' + this.arrangement;
3442         }
3443         
3444         
3445         if (this.align === 'right') {
3446             cfg.cn[0].cls += ' navbar-right';
3447         }
3448         
3449         if (this.inverse) {
3450             cfg.cls += ' navbar-inverse';
3451             
3452         }
3453         
3454         
3455         return cfg;
3456     
3457         
3458     }
3459     
3460     
3461     
3462 });
3463
3464
3465
3466  
3467
3468  
3469        /*
3470  * - LGPL
3471  *
3472  * navbar
3473  * 
3474  */
3475
3476 /**
3477  * @class Roo.bootstrap.NavHeaderbar
3478  * @extends Roo.bootstrap.NavSimplebar
3479  * Bootstrap Sidebar class
3480  *
3481  * @cfg {String} brand what is brand
3482  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3483  * @cfg {String} brand_href href of the brand
3484  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3485  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3486  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3487  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3488  * 
3489  * @constructor
3490  * Create a new Sidebar
3491  * @param {Object} config The config object
3492  */
3493
3494
3495 Roo.bootstrap.NavHeaderbar = function(config){
3496     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3497       
3498 };
3499
3500 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3501     
3502     position: '',
3503     brand: '',
3504     brand_href: false,
3505     srButton : true,
3506     autohide : false,
3507     desktopCenter : false,
3508    
3509     
3510     getAutoCreate : function(){
3511         
3512         var   cfg = {
3513             tag: this.nav || 'nav',
3514             cls: 'navbar',
3515             role: 'navigation',
3516             cn: []
3517         };
3518         
3519         var cn = cfg.cn;
3520         if (this.desktopCenter) {
3521             cn.push({cls : 'container', cn : []});
3522             cn = cn[0].cn;
3523         }
3524         
3525         if(this.srButton){
3526             cn.push({
3527                 tag: 'div',
3528                 cls: 'navbar-header',
3529                 cn: [
3530                     {
3531                         tag: 'button',
3532                         type: 'button',
3533                         cls: 'navbar-toggle',
3534                         'data-toggle': 'collapse',
3535                         cn: [
3536                             {
3537                                 tag: 'span',
3538                                 cls: 'sr-only',
3539                                 html: 'Toggle navigation'
3540                             },
3541                             {
3542                                 tag: 'span',
3543                                 cls: 'icon-bar'
3544                             },
3545                             {
3546                                 tag: 'span',
3547                                 cls: 'icon-bar'
3548                             },
3549                             {
3550                                 tag: 'span',
3551                                 cls: 'icon-bar'
3552                             }
3553                         ]
3554                     }
3555                 ]
3556             });
3557         }
3558         
3559         cn.push({
3560             tag: 'div',
3561             cls: 'collapse navbar-collapse',
3562             cn : []
3563         });
3564         
3565         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3566         
3567         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3568             cfg.cls += ' navbar-' + this.position;
3569             
3570             // tag can override this..
3571             
3572             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3573         }
3574         
3575         if (this.brand !== '') {
3576             cn[0].cn.push({
3577                 tag: 'a',
3578                 href: this.brand_href ? this.brand_href : '#',
3579                 cls: 'navbar-brand',
3580                 cn: [
3581                 this.brand
3582                 ]
3583             });
3584         }
3585         
3586         if(this.main){
3587             cfg.cls += ' main-nav';
3588         }
3589         
3590         
3591         return cfg;
3592
3593         
3594     },
3595     getHeaderChildContainer : function()
3596     {
3597         if (this.el.select('.navbar-header').getCount()) {
3598             return this.el.select('.navbar-header',true).first();
3599         }
3600         
3601         return this.getChildContainer();
3602     },
3603     
3604     
3605     initEvents : function()
3606     {
3607         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3608         
3609         if (this.autohide) {
3610             
3611             var prevScroll = 0;
3612             var ft = this.el;
3613             
3614             Roo.get(document).on('scroll',function(e) {
3615                 var ns = Roo.get(document).getScroll().top;
3616                 var os = prevScroll;
3617                 prevScroll = ns;
3618                 
3619                 if(ns > os){
3620                     ft.removeClass('slideDown');
3621                     ft.addClass('slideUp');
3622                     return;
3623                 }
3624                 ft.removeClass('slideUp');
3625                 ft.addClass('slideDown');
3626                  
3627               
3628           },this);
3629         }
3630     }    
3631     
3632 });
3633
3634
3635
3636  
3637
3638  /*
3639  * - LGPL
3640  *
3641  * navbar
3642  * 
3643  */
3644
3645 /**
3646  * @class Roo.bootstrap.NavSidebar
3647  * @extends Roo.bootstrap.Navbar
3648  * Bootstrap Sidebar class
3649  * 
3650  * @constructor
3651  * Create a new Sidebar
3652  * @param {Object} config The config object
3653  */
3654
3655
3656 Roo.bootstrap.NavSidebar = function(config){
3657     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3658 };
3659
3660 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3661     
3662     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3663     
3664     getAutoCreate : function(){
3665         
3666         
3667         return  {
3668             tag: 'div',
3669             cls: 'sidebar sidebar-nav'
3670         };
3671     
3672         
3673     }
3674     
3675     
3676     
3677 });
3678
3679
3680
3681  
3682
3683  /*
3684  * - LGPL
3685  *
3686  * nav group
3687  * 
3688  */
3689
3690 /**
3691  * @class Roo.bootstrap.NavGroup
3692  * @extends Roo.bootstrap.Component
3693  * Bootstrap NavGroup class
3694  * @cfg {String} align (left|right)
3695  * @cfg {Boolean} inverse
3696  * @cfg {String} type (nav|pills|tab) default nav
3697  * @cfg {String} navId - reference Id for navbar.
3698
3699  * 
3700  * @constructor
3701  * Create a new nav group
3702  * @param {Object} config The config object
3703  */
3704
3705 Roo.bootstrap.NavGroup = function(config){
3706     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3707     this.navItems = [];
3708    
3709     Roo.bootstrap.NavGroup.register(this);
3710      this.addEvents({
3711         /**
3712              * @event changed
3713              * Fires when the active item changes
3714              * @param {Roo.bootstrap.NavGroup} this
3715              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3716              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3717          */
3718         'changed': true
3719      });
3720     
3721 };
3722
3723 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3724     
3725     align: '',
3726     inverse: false,
3727     form: false,
3728     type: 'nav',
3729     navId : '',
3730     // private
3731     
3732     navItems : false, 
3733     
3734     getAutoCreate : function()
3735     {
3736         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3737         
3738         cfg = {
3739             tag : 'ul',
3740             cls: 'nav' 
3741         }
3742         
3743         if (['tabs','pills'].indexOf(this.type)!==-1) {
3744             cfg.cls += ' nav-' + this.type
3745         } else {
3746             if (this.type!=='nav') {
3747                 Roo.log('nav type must be nav/tabs/pills')
3748             }
3749             cfg.cls += ' navbar-nav'
3750         }
3751         
3752         if (this.parent().sidebar) {
3753             cfg = {
3754                 tag: 'ul',
3755                 cls: 'dashboard-menu sidebar-menu'
3756             }
3757             
3758             return cfg;
3759         }
3760         
3761         if (this.form === true) {
3762             cfg = {
3763                 tag: 'form',
3764                 cls: 'navbar-form'
3765             }
3766             
3767             if (this.align === 'right') {
3768                 cfg.cls += ' navbar-right';
3769             } else {
3770                 cfg.cls += ' navbar-left';
3771             }
3772         }
3773         
3774         if (this.align === 'right') {
3775             cfg.cls += ' navbar-right';
3776         }
3777         
3778         if (this.inverse) {
3779             cfg.cls += ' navbar-inverse';
3780             
3781         }
3782         
3783         
3784         return cfg;
3785     },
3786     /**
3787     * sets the active Navigation item
3788     * @param {Roo.bootstrap.NavItem} the new current navitem
3789     */
3790     setActiveItem : function(item)
3791     {
3792         var prev = false;
3793         Roo.each(this.navItems, function(v){
3794             if (v == item) {
3795                 return ;
3796             }
3797             if (v.isActive()) {
3798                 v.setActive(false, true);
3799                 prev = v;
3800                 
3801             }
3802             
3803         });
3804
3805         item.setActive(true, true);
3806         this.fireEvent('changed', this, item, prev);
3807         
3808         
3809     },
3810     /**
3811     * gets the active Navigation item
3812     * @return {Roo.bootstrap.NavItem} the current navitem
3813     */
3814     getActive : function()
3815     {
3816         
3817         var prev = false;
3818         Roo.each(this.navItems, function(v){
3819             
3820             if (v.isActive()) {
3821                 prev = v;
3822                 
3823             }
3824             
3825         });
3826         return prev;
3827     },
3828     
3829     indexOfNav : function()
3830     {
3831         
3832         var prev = false;
3833         Roo.each(this.navItems, function(v,i){
3834             
3835             if (v.isActive()) {
3836                 prev = i;
3837                 
3838             }
3839             
3840         });
3841         return prev;
3842     },
3843     /**
3844     * adds a Navigation item
3845     * @param {Roo.bootstrap.NavItem} the navitem to add
3846     */
3847     addItem : function(cfg)
3848     {
3849         var cn = new Roo.bootstrap.NavItem(cfg);
3850         this.register(cn);
3851         cn.parentId = this.id;
3852         cn.onRender(this.el, null);
3853         return cn;
3854     },
3855     /**
3856     * register a Navigation item
3857     * @param {Roo.bootstrap.NavItem} the navitem to add
3858     */
3859     register : function(item)
3860     {
3861         this.navItems.push( item);
3862         item.navId = this.navId;
3863     
3864     },
3865     
3866     /**
3867     * clear all the Navigation item
3868     */
3869    
3870     clearAll : function()
3871     {
3872         this.navItems = [];
3873         this.el.dom.innerHTML = '';
3874     },
3875     
3876     getNavItem: function(tabId)
3877     {
3878         var ret = false;
3879         Roo.each(this.navItems, function(e) {
3880             if (e.tabId == tabId) {
3881                ret =  e;
3882                return false;
3883             }
3884             return true;
3885             
3886         });
3887         return ret;
3888     },
3889     
3890     setActiveNext : function()
3891     {
3892         var i = this.indexOfNav(this.getActive());
3893         if (i > this.navItems.length) {
3894             return;
3895         }
3896         this.setActiveItem(this.navItems[i+1]);
3897     },
3898     setActivePrev : function()
3899     {
3900         var i = this.indexOfNav(this.getActive());
3901         if (i  < 1) {
3902             return;
3903         }
3904         this.setActiveItem(this.navItems[i-1]);
3905     },
3906     clearWasActive : function(except) {
3907         Roo.each(this.navItems, function(e) {
3908             if (e.tabId != except.tabId && e.was_active) {
3909                e.was_active = false;
3910                return false;
3911             }
3912             return true;
3913             
3914         });
3915     },
3916     getWasActive : function ()
3917     {
3918         var r = false;
3919         Roo.each(this.navItems, function(e) {
3920             if (e.was_active) {
3921                r = e;
3922                return false;
3923             }
3924             return true;
3925             
3926         });
3927         return r;
3928     }
3929     
3930     
3931 });
3932
3933  
3934 Roo.apply(Roo.bootstrap.NavGroup, {
3935     
3936     groups: {},
3937      /**
3938     * register a Navigation Group
3939     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3940     */
3941     register : function(navgrp)
3942     {
3943         this.groups[navgrp.navId] = navgrp;
3944         
3945     },
3946     /**
3947     * fetch a Navigation Group based on the navigation ID
3948     * @param {string} the navgroup to add
3949     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3950     */
3951     get: function(navId) {
3952         if (typeof(this.groups[navId]) == 'undefined') {
3953             return false;
3954             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3955         }
3956         return this.groups[navId] ;
3957     }
3958     
3959     
3960     
3961 });
3962
3963  /*
3964  * - LGPL
3965  *
3966  * row
3967  * 
3968  */
3969
3970 /**
3971  * @class Roo.bootstrap.NavItem
3972  * @extends Roo.bootstrap.Component
3973  * Bootstrap Navbar.NavItem class
3974  * @cfg {String} href  link to
3975  * @cfg {String} html content of button
3976  * @cfg {String} badge text inside badge
3977  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3978  * @cfg {String} glyphicon name of glyphicon
3979  * @cfg {String} icon name of font awesome icon
3980  * @cfg {Boolean} active Is item active
3981  * @cfg {Boolean} disabled Is item disabled
3982  
3983  * @cfg {Boolean} preventDefault (true | false) default false
3984  * @cfg {String} tabId the tab that this item activates.
3985  * @cfg {String} tagtype (a|span) render as a href or span?
3986  * @cfg {Boolean} animateRef (true|false) link to element default false  
3987   
3988  * @constructor
3989  * Create a new Navbar Item
3990  * @param {Object} config The config object
3991  */
3992 Roo.bootstrap.NavItem = function(config){
3993     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3994     this.addEvents({
3995         // raw events
3996         /**
3997          * @event click
3998          * The raw click event for the entire grid.
3999          * @param {Roo.EventObject} e
4000          */
4001         "click" : true,
4002          /**
4003             * @event changed
4004             * Fires when the active item active state changes
4005             * @param {Roo.bootstrap.NavItem} this
4006             * @param {boolean} state the new state
4007              
4008          */
4009         'changed': true,
4010         /**
4011             * @event scrollto
4012             * Fires when scroll to element
4013             * @param {Roo.bootstrap.NavItem} this
4014             * @param {Object} options
4015             * @param {Roo.EventObject} e
4016              
4017          */
4018         'scrollto': true
4019     });
4020    
4021 };
4022
4023 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4024     
4025     href: false,
4026     html: '',
4027     badge: '',
4028     icon: false,
4029     glyphicon: false,
4030     active: false,
4031     preventDefault : false,
4032     tabId : false,
4033     tagtype : 'a',
4034     disabled : false,
4035     animateRef : false,
4036     was_active : false,
4037     
4038     getAutoCreate : function(){
4039          
4040         var cfg = {
4041             tag: 'li',
4042             cls: 'nav-item'
4043             
4044         }
4045         if (this.active) {
4046             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4047         }
4048         if (this.disabled) {
4049             cfg.cls += ' disabled';
4050         }
4051         
4052         if (this.href || this.html || this.glyphicon || this.icon) {
4053             cfg.cn = [
4054                 {
4055                     tag: this.tagtype,
4056                     href : this.href || "#",
4057                     html: this.html || ''
4058                 }
4059             ];
4060             
4061             if (this.icon) {
4062                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4063             }
4064
4065             if(this.glyphicon) {
4066                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4067             }
4068             
4069             if (this.menu) {
4070                 
4071                 cfg.cn[0].html += " <span class='caret'></span>";
4072              
4073             }
4074             
4075             if (this.badge !== '') {
4076                  
4077                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4078             }
4079         }
4080         
4081         
4082         
4083         return cfg;
4084     },
4085     initEvents: function() 
4086     {
4087         if (typeof (this.menu) != 'undefined') {
4088             this.menu.parentType = this.xtype;
4089             this.menu.triggerEl = this.el;
4090             this.menu = this.addxtype(Roo.apply({}, this.menu));
4091         }
4092         
4093         this.el.select('a',true).on('click', this.onClick, this);
4094         
4095         if(this.tagtype == 'span'){
4096             this.el.select('span',true).on('click', this.onClick, this);
4097         }
4098        
4099         // at this point parent should be available..
4100         this.parent().register(this);
4101     },
4102     
4103     onClick : function(e)
4104     {
4105         if(
4106                 this.preventDefault || 
4107                 this.href == '#' 
4108         ){
4109             
4110             e.preventDefault();
4111         }
4112         
4113         if (this.disabled) {
4114             return;
4115         }
4116         
4117         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4118         if (tg && tg.transition) {
4119             Roo.log("waiting for the transitionend");
4120             return;
4121         }
4122         
4123         
4124         
4125         //Roo.log("fire event clicked");
4126         if(this.fireEvent('click', this, e) === false){
4127             return;
4128         };
4129         
4130         if(this.tagtype == 'span'){
4131             return;
4132         }
4133         
4134         //Roo.log(this.href);
4135         var ael = this.el.select('a',true).first();
4136         //Roo.log(ael);
4137         
4138         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4139             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4140             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4141                 return; // ignore... - it's a 'hash' to another page.
4142             }
4143             
4144             e.preventDefault();
4145             this.scrollToElement(e);
4146         }
4147         
4148         
4149         var p =  this.parent();
4150    
4151         if (['tabs','pills'].indexOf(p.type)!==-1) {
4152             if (typeof(p.setActiveItem) !== 'undefined') {
4153                 p.setActiveItem(this);
4154             }
4155         }
4156         
4157         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4158         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4159             // remove the collapsed menu expand...
4160             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4161         }
4162     },
4163     
4164     isActive: function () {
4165         return this.active
4166     },
4167     setActive : function(state, fire, is_was_active)
4168     {
4169         if (this.active && !state & this.navId) {
4170             this.was_active = true;
4171             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4172             if (nv) {
4173                 nv.clearWasActive(this);
4174             }
4175             
4176         }
4177         this.active = state;
4178         
4179         if (!state ) {
4180             this.el.removeClass('active');
4181         } else if (!this.el.hasClass('active')) {
4182             this.el.addClass('active');
4183         }
4184         if (fire) {
4185             this.fireEvent('changed', this, state);
4186         }
4187         
4188         // show a panel if it's registered and related..
4189         
4190         if (!this.navId || !this.tabId || !state || is_was_active) {
4191             return;
4192         }
4193         
4194         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4195         if (!tg) {
4196             return;
4197         }
4198         var pan = tg.getPanelByName(this.tabId);
4199         if (!pan) {
4200             return;
4201         }
4202         // if we can not flip to new panel - go back to old nav highlight..
4203         if (false == tg.showPanel(pan)) {
4204             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4205             if (nv) {
4206                 var onav = nv.getWasActive();
4207                 if (onav) {
4208                     onav.setActive(true, false, true);
4209                 }
4210             }
4211             
4212         }
4213         
4214         
4215         
4216     },
4217      // this should not be here...
4218     setDisabled : function(state)
4219     {
4220         this.disabled = state;
4221         if (!state ) {
4222             this.el.removeClass('disabled');
4223         } else if (!this.el.hasClass('disabled')) {
4224             this.el.addClass('disabled');
4225         }
4226         
4227     },
4228     
4229     /**
4230      * Fetch the element to display the tooltip on.
4231      * @return {Roo.Element} defaults to this.el
4232      */
4233     tooltipEl : function()
4234     {
4235         return this.el.select('' + this.tagtype + '', true).first();
4236     },
4237     
4238     scrollToElement : function(e)
4239     {
4240         var c = document.body;
4241         
4242         /*
4243          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4244          */
4245         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4246             c = document.documentElement;
4247         }
4248         
4249         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4250         
4251         if(!target){
4252             return;
4253         }
4254
4255         var o = target.calcOffsetsTo(c);
4256         
4257         var options = {
4258             target : target,
4259             value : o[1]
4260         }
4261         
4262         this.fireEvent('scrollto', this, options, e);
4263         
4264         Roo.get(c).scrollTo('top', options.value, true);
4265         
4266         return;
4267     }
4268 });
4269  
4270
4271  /*
4272  * - LGPL
4273  *
4274  * sidebar item
4275  *
4276  *  li
4277  *    <span> icon </span>
4278  *    <span> text </span>
4279  *    <span>badge </span>
4280  */
4281
4282 /**
4283  * @class Roo.bootstrap.NavSidebarItem
4284  * @extends Roo.bootstrap.NavItem
4285  * Bootstrap Navbar.NavSidebarItem class
4286  * @constructor
4287  * Create a new Navbar Button
4288  * @param {Object} config The config object
4289  */
4290 Roo.bootstrap.NavSidebarItem = function(config){
4291     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4292     this.addEvents({
4293         // raw events
4294         /**
4295          * @event click
4296          * The raw click event for the entire grid.
4297          * @param {Roo.EventObject} e
4298          */
4299         "click" : true,
4300          /**
4301             * @event changed
4302             * Fires when the active item active state changes
4303             * @param {Roo.bootstrap.NavSidebarItem} this
4304             * @param {boolean} state the new state
4305              
4306          */
4307         'changed': true
4308     });
4309    
4310 };
4311
4312 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4313     
4314     
4315     getAutoCreate : function(){
4316         
4317         
4318         var a = {
4319                 tag: 'a',
4320                 href : this.href || '#',
4321                 cls: '',
4322                 html : '',
4323                 cn : []
4324         };
4325         var cfg = {
4326             tag: 'li',
4327             cls: '',
4328             cn: [ a ]
4329         }
4330         var span = {
4331             tag: 'span',
4332             html : this.html || ''
4333         }
4334         
4335         
4336         if (this.active) {
4337             cfg.cls += ' active';
4338         }
4339         
4340         // left icon..
4341         if (this.glyphicon || this.icon) {
4342             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4343             a.cn.push({ tag : 'i', cls : c }) ;
4344         }
4345         // html..
4346         a.cn.push(span);
4347         // then badge..
4348         if (this.badge !== '') {
4349             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4350         }
4351         // fi
4352         if (this.menu) {
4353             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4354             a.cls += 'dropdown-toggle treeview' ;
4355             
4356         }
4357         
4358         
4359         
4360         return cfg;
4361          
4362            
4363     }
4364    
4365      
4366  
4367 });
4368  
4369
4370  /*
4371  * - LGPL
4372  *
4373  * row
4374  * 
4375  */
4376
4377 /**
4378  * @class Roo.bootstrap.Row
4379  * @extends Roo.bootstrap.Component
4380  * Bootstrap Row class (contains columns...)
4381  * 
4382  * @constructor
4383  * Create a new Row
4384  * @param {Object} config The config object
4385  */
4386
4387 Roo.bootstrap.Row = function(config){
4388     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4389 };
4390
4391 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4392     
4393     getAutoCreate : function(){
4394        return {
4395             cls: 'row clearfix'
4396        };
4397     }
4398     
4399     
4400 });
4401
4402  
4403
4404  /*
4405  * - LGPL
4406  *
4407  * element
4408  * 
4409  */
4410
4411 /**
4412  * @class Roo.bootstrap.Element
4413  * @extends Roo.bootstrap.Component
4414  * Bootstrap Element class
4415  * @cfg {String} html contents of the element
4416  * @cfg {String} tag tag of the element
4417  * @cfg {String} cls class of the element
4418  * @cfg {Boolean} preventDefault (true|false) default false
4419  * @cfg {Boolean} clickable (true|false) default false
4420  * 
4421  * @constructor
4422  * Create a new Element
4423  * @param {Object} config The config object
4424  */
4425
4426 Roo.bootstrap.Element = function(config){
4427     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4428     
4429     this.addEvents({
4430         // raw events
4431         /**
4432          * @event click
4433          * When a element is chick
4434          * @param {Roo.bootstrap.Element} this
4435          * @param {Roo.EventObject} e
4436          */
4437         "click" : true
4438     });
4439 };
4440
4441 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4442     
4443     tag: 'div',
4444     cls: '',
4445     html: '',
4446     preventDefault: false, 
4447     clickable: false,
4448     
4449     getAutoCreate : function(){
4450         
4451         var cfg = {
4452             tag: this.tag,
4453             cls: this.cls,
4454             html: this.html
4455         }
4456         
4457         return cfg;
4458     },
4459     
4460     initEvents: function() 
4461     {
4462         Roo.bootstrap.Element.superclass.initEvents.call(this);
4463         
4464         if(this.clickable){
4465             this.el.on('click', this.onClick, this);
4466         }
4467         
4468     },
4469     
4470     onClick : function(e)
4471     {
4472         if(this.preventDefault){
4473             e.preventDefault();
4474         }
4475         
4476         this.fireEvent('click', this, e);
4477     },
4478     
4479     getValue : function()
4480     {
4481         return this.el.dom.innerHTML;
4482     },
4483     
4484     setValue : function(value)
4485     {
4486         this.el.dom.innerHTML = value;
4487     }
4488    
4489 });
4490
4491  
4492
4493  /*
4494  * - LGPL
4495  *
4496  * pagination
4497  * 
4498  */
4499
4500 /**
4501  * @class Roo.bootstrap.Pagination
4502  * @extends Roo.bootstrap.Component
4503  * Bootstrap Pagination class
4504  * @cfg {String} size xs | sm | md | lg
4505  * @cfg {Boolean} inverse false | true
4506  * 
4507  * @constructor
4508  * Create a new Pagination
4509  * @param {Object} config The config object
4510  */
4511
4512 Roo.bootstrap.Pagination = function(config){
4513     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4514 };
4515
4516 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4517     
4518     cls: false,
4519     size: false,
4520     inverse: false,
4521     
4522     getAutoCreate : function(){
4523         var cfg = {
4524             tag: 'ul',
4525                 cls: 'pagination'
4526         };
4527         if (this.inverse) {
4528             cfg.cls += ' inverse';
4529         }
4530         if (this.html) {
4531             cfg.html=this.html;
4532         }
4533         if (this.cls) {
4534             cfg.cls += " " + this.cls;
4535         }
4536         return cfg;
4537     }
4538    
4539 });
4540
4541  
4542
4543  /*
4544  * - LGPL
4545  *
4546  * Pagination item
4547  * 
4548  */
4549
4550
4551 /**
4552  * @class Roo.bootstrap.PaginationItem
4553  * @extends Roo.bootstrap.Component
4554  * Bootstrap PaginationItem class
4555  * @cfg {String} html text
4556  * @cfg {String} href the link
4557  * @cfg {Boolean} preventDefault (true | false) default true
4558  * @cfg {Boolean} active (true | false) default false
4559  * @cfg {Boolean} disabled default false
4560  * 
4561  * 
4562  * @constructor
4563  * Create a new PaginationItem
4564  * @param {Object} config The config object
4565  */
4566
4567
4568 Roo.bootstrap.PaginationItem = function(config){
4569     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4570     this.addEvents({
4571         // raw events
4572         /**
4573          * @event click
4574          * The raw click event for the entire grid.
4575          * @param {Roo.EventObject} e
4576          */
4577         "click" : true
4578     });
4579 };
4580
4581 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4582     
4583     href : false,
4584     html : false,
4585     preventDefault: true,
4586     active : false,
4587     cls : false,
4588     disabled: false,
4589     
4590     getAutoCreate : function(){
4591         var cfg= {
4592             tag: 'li',
4593             cn: [
4594                 {
4595                     tag : 'a',
4596                     href : this.href ? this.href : '#',
4597                     html : this.html ? this.html : ''
4598                 }
4599             ]
4600         };
4601         
4602         if(this.cls){
4603             cfg.cls = this.cls;
4604         }
4605         
4606         if(this.disabled){
4607             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4608         }
4609         
4610         if(this.active){
4611             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4612         }
4613         
4614         return cfg;
4615     },
4616     
4617     initEvents: function() {
4618         
4619         this.el.on('click', this.onClick, this);
4620         
4621     },
4622     onClick : function(e)
4623     {
4624         Roo.log('PaginationItem on click ');
4625         if(this.preventDefault){
4626             e.preventDefault();
4627         }
4628         
4629         if(this.disabled){
4630             return;
4631         }
4632         
4633         this.fireEvent('click', this, e);
4634     }
4635    
4636 });
4637
4638  
4639
4640  /*
4641  * - LGPL
4642  *
4643  * slider
4644  * 
4645  */
4646
4647
4648 /**
4649  * @class Roo.bootstrap.Slider
4650  * @extends Roo.bootstrap.Component
4651  * Bootstrap Slider class
4652  *    
4653  * @constructor
4654  * Create a new Slider
4655  * @param {Object} config The config object
4656  */
4657
4658 Roo.bootstrap.Slider = function(config){
4659     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4660 };
4661
4662 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4663     
4664     getAutoCreate : function(){
4665         
4666         var cfg = {
4667             tag: 'div',
4668             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4669             cn: [
4670                 {
4671                     tag: 'a',
4672                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4673                 }
4674             ]
4675         }
4676         
4677         return cfg;
4678     }
4679    
4680 });
4681
4682  /*
4683  * Based on:
4684  * Ext JS Library 1.1.1
4685  * Copyright(c) 2006-2007, Ext JS, LLC.
4686  *
4687  * Originally Released Under LGPL - original licence link has changed is not relivant.
4688  *
4689  * Fork - LGPL
4690  * <script type="text/javascript">
4691  */
4692  
4693
4694 /**
4695  * @class Roo.grid.ColumnModel
4696  * @extends Roo.util.Observable
4697  * This is the default implementation of a ColumnModel used by the Grid. It defines
4698  * the columns in the grid.
4699  * <br>Usage:<br>
4700  <pre><code>
4701  var colModel = new Roo.grid.ColumnModel([
4702         {header: "Ticker", width: 60, sortable: true, locked: true},
4703         {header: "Company Name", width: 150, sortable: true},
4704         {header: "Market Cap.", width: 100, sortable: true},
4705         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4706         {header: "Employees", width: 100, sortable: true, resizable: false}
4707  ]);
4708  </code></pre>
4709  * <p>
4710  
4711  * The config options listed for this class are options which may appear in each
4712  * individual column definition.
4713  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4714  * @constructor
4715  * @param {Object} config An Array of column config objects. See this class's
4716  * config objects for details.
4717 */
4718 Roo.grid.ColumnModel = function(config){
4719         /**
4720      * The config passed into the constructor
4721      */
4722     this.config = config;
4723     this.lookup = {};
4724
4725     // if no id, create one
4726     // if the column does not have a dataIndex mapping,
4727     // map it to the order it is in the config
4728     for(var i = 0, len = config.length; i < len; i++){
4729         var c = config[i];
4730         if(typeof c.dataIndex == "undefined"){
4731             c.dataIndex = i;
4732         }
4733         if(typeof c.renderer == "string"){
4734             c.renderer = Roo.util.Format[c.renderer];
4735         }
4736         if(typeof c.id == "undefined"){
4737             c.id = Roo.id();
4738         }
4739         if(c.editor && c.editor.xtype){
4740             c.editor  = Roo.factory(c.editor, Roo.grid);
4741         }
4742         if(c.editor && c.editor.isFormField){
4743             c.editor = new Roo.grid.GridEditor(c.editor);
4744         }
4745         this.lookup[c.id] = c;
4746     }
4747
4748     /**
4749      * The width of columns which have no width specified (defaults to 100)
4750      * @type Number
4751      */
4752     this.defaultWidth = 100;
4753
4754     /**
4755      * Default sortable of columns which have no sortable specified (defaults to false)
4756      * @type Boolean
4757      */
4758     this.defaultSortable = false;
4759
4760     this.addEvents({
4761         /**
4762              * @event widthchange
4763              * Fires when the width of a column changes.
4764              * @param {ColumnModel} this
4765              * @param {Number} columnIndex The column index
4766              * @param {Number} newWidth The new width
4767              */
4768             "widthchange": true,
4769         /**
4770              * @event headerchange
4771              * Fires when the text of a header changes.
4772              * @param {ColumnModel} this
4773              * @param {Number} columnIndex The column index
4774              * @param {Number} newText The new header text
4775              */
4776             "headerchange": true,
4777         /**
4778              * @event hiddenchange
4779              * Fires when a column is hidden or "unhidden".
4780              * @param {ColumnModel} this
4781              * @param {Number} columnIndex The column index
4782              * @param {Boolean} hidden true if hidden, false otherwise
4783              */
4784             "hiddenchange": true,
4785             /**
4786          * @event columnmoved
4787          * Fires when a column is moved.
4788          * @param {ColumnModel} this
4789          * @param {Number} oldIndex
4790          * @param {Number} newIndex
4791          */
4792         "columnmoved" : true,
4793         /**
4794          * @event columlockchange
4795          * Fires when a column's locked state is changed
4796          * @param {ColumnModel} this
4797          * @param {Number} colIndex
4798          * @param {Boolean} locked true if locked
4799          */
4800         "columnlockchange" : true
4801     });
4802     Roo.grid.ColumnModel.superclass.constructor.call(this);
4803 };
4804 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4805     /**
4806      * @cfg {String} header The header text to display in the Grid view.
4807      */
4808     /**
4809      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4810      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4811      * specified, the column's index is used as an index into the Record's data Array.
4812      */
4813     /**
4814      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4815      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4816      */
4817     /**
4818      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4819      * Defaults to the value of the {@link #defaultSortable} property.
4820      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4821      */
4822     /**
4823      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4824      */
4825     /**
4826      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4827      */
4828     /**
4829      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4830      */
4831     /**
4832      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4833      */
4834     /**
4835      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4836      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4837      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4838      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4839      */
4840        /**
4841      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4842      */
4843     /**
4844      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4845      */
4846     /**
4847      * @cfg {String} cursor (Optional)
4848      */
4849     /**
4850      * @cfg {String} tooltip (Optional)
4851      */
4852     /**
4853      * Returns the id of the column at the specified index.
4854      * @param {Number} index The column index
4855      * @return {String} the id
4856      */
4857     getColumnId : function(index){
4858         return this.config[index].id;
4859     },
4860
4861     /**
4862      * Returns the column for a specified id.
4863      * @param {String} id The column id
4864      * @return {Object} the column
4865      */
4866     getColumnById : function(id){
4867         return this.lookup[id];
4868     },
4869
4870     
4871     /**
4872      * Returns the column for a specified dataIndex.
4873      * @param {String} dataIndex The column dataIndex
4874      * @return {Object|Boolean} the column or false if not found
4875      */
4876     getColumnByDataIndex: function(dataIndex){
4877         var index = this.findColumnIndex(dataIndex);
4878         return index > -1 ? this.config[index] : false;
4879     },
4880     
4881     /**
4882      * Returns the index for a specified column id.
4883      * @param {String} id The column id
4884      * @return {Number} the index, or -1 if not found
4885      */
4886     getIndexById : function(id){
4887         for(var i = 0, len = this.config.length; i < len; i++){
4888             if(this.config[i].id == id){
4889                 return i;
4890             }
4891         }
4892         return -1;
4893     },
4894     
4895     /**
4896      * Returns the index for a specified column dataIndex.
4897      * @param {String} dataIndex The column dataIndex
4898      * @return {Number} the index, or -1 if not found
4899      */
4900     
4901     findColumnIndex : function(dataIndex){
4902         for(var i = 0, len = this.config.length; i < len; i++){
4903             if(this.config[i].dataIndex == dataIndex){
4904                 return i;
4905             }
4906         }
4907         return -1;
4908     },
4909     
4910     
4911     moveColumn : function(oldIndex, newIndex){
4912         var c = this.config[oldIndex];
4913         this.config.splice(oldIndex, 1);
4914         this.config.splice(newIndex, 0, c);
4915         this.dataMap = null;
4916         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4917     },
4918
4919     isLocked : function(colIndex){
4920         return this.config[colIndex].locked === true;
4921     },
4922
4923     setLocked : function(colIndex, value, suppressEvent){
4924         if(this.isLocked(colIndex) == value){
4925             return;
4926         }
4927         this.config[colIndex].locked = value;
4928         if(!suppressEvent){
4929             this.fireEvent("columnlockchange", this, colIndex, value);
4930         }
4931     },
4932
4933     getTotalLockedWidth : function(){
4934         var totalWidth = 0;
4935         for(var i = 0; i < this.config.length; i++){
4936             if(this.isLocked(i) && !this.isHidden(i)){
4937                 this.totalWidth += this.getColumnWidth(i);
4938             }
4939         }
4940         return totalWidth;
4941     },
4942
4943     getLockedCount : function(){
4944         for(var i = 0, len = this.config.length; i < len; i++){
4945             if(!this.isLocked(i)){
4946                 return i;
4947             }
4948         }
4949     },
4950
4951     /**
4952      * Returns the number of columns.
4953      * @return {Number}
4954      */
4955     getColumnCount : function(visibleOnly){
4956         if(visibleOnly === true){
4957             var c = 0;
4958             for(var i = 0, len = this.config.length; i < len; i++){
4959                 if(!this.isHidden(i)){
4960                     c++;
4961                 }
4962             }
4963             return c;
4964         }
4965         return this.config.length;
4966     },
4967
4968     /**
4969      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4970      * @param {Function} fn
4971      * @param {Object} scope (optional)
4972      * @return {Array} result
4973      */
4974     getColumnsBy : function(fn, scope){
4975         var r = [];
4976         for(var i = 0, len = this.config.length; i < len; i++){
4977             var c = this.config[i];
4978             if(fn.call(scope||this, c, i) === true){
4979                 r[r.length] = c;
4980             }
4981         }
4982         return r;
4983     },
4984
4985     /**
4986      * Returns true if the specified column is sortable.
4987      * @param {Number} col The column index
4988      * @return {Boolean}
4989      */
4990     isSortable : function(col){
4991         if(typeof this.config[col].sortable == "undefined"){
4992             return this.defaultSortable;
4993         }
4994         return this.config[col].sortable;
4995     },
4996
4997     /**
4998      * Returns the rendering (formatting) function defined for the column.
4999      * @param {Number} col The column index.
5000      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5001      */
5002     getRenderer : function(col){
5003         if(!this.config[col].renderer){
5004             return Roo.grid.ColumnModel.defaultRenderer;
5005         }
5006         return this.config[col].renderer;
5007     },
5008
5009     /**
5010      * Sets the rendering (formatting) function for a column.
5011      * @param {Number} col The column index
5012      * @param {Function} fn The function to use to process the cell's raw data
5013      * to return HTML markup for the grid view. The render function is called with
5014      * the following parameters:<ul>
5015      * <li>Data value.</li>
5016      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5017      * <li>css A CSS style string to apply to the table cell.</li>
5018      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5019      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5020      * <li>Row index</li>
5021      * <li>Column index</li>
5022      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5023      */
5024     setRenderer : function(col, fn){
5025         this.config[col].renderer = fn;
5026     },
5027
5028     /**
5029      * Returns the width for the specified column.
5030      * @param {Number} col The column index
5031      * @return {Number}
5032      */
5033     getColumnWidth : function(col){
5034         return this.config[col].width * 1 || this.defaultWidth;
5035     },
5036
5037     /**
5038      * Sets the width for a column.
5039      * @param {Number} col The column index
5040      * @param {Number} width The new width
5041      */
5042     setColumnWidth : function(col, width, suppressEvent){
5043         this.config[col].width = width;
5044         this.totalWidth = null;
5045         if(!suppressEvent){
5046              this.fireEvent("widthchange", this, col, width);
5047         }
5048     },
5049
5050     /**
5051      * Returns the total width of all columns.
5052      * @param {Boolean} includeHidden True to include hidden column widths
5053      * @return {Number}
5054      */
5055     getTotalWidth : function(includeHidden){
5056         if(!this.totalWidth){
5057             this.totalWidth = 0;
5058             for(var i = 0, len = this.config.length; i < len; i++){
5059                 if(includeHidden || !this.isHidden(i)){
5060                     this.totalWidth += this.getColumnWidth(i);
5061                 }
5062             }
5063         }
5064         return this.totalWidth;
5065     },
5066
5067     /**
5068      * Returns the header for the specified column.
5069      * @param {Number} col The column index
5070      * @return {String}
5071      */
5072     getColumnHeader : function(col){
5073         return this.config[col].header;
5074     },
5075
5076     /**
5077      * Sets the header for a column.
5078      * @param {Number} col The column index
5079      * @param {String} header The new header
5080      */
5081     setColumnHeader : function(col, header){
5082         this.config[col].header = header;
5083         this.fireEvent("headerchange", this, col, header);
5084     },
5085
5086     /**
5087      * Returns the tooltip for the specified column.
5088      * @param {Number} col The column index
5089      * @return {String}
5090      */
5091     getColumnTooltip : function(col){
5092             return this.config[col].tooltip;
5093     },
5094     /**
5095      * Sets the tooltip for a column.
5096      * @param {Number} col The column index
5097      * @param {String} tooltip The new tooltip
5098      */
5099     setColumnTooltip : function(col, tooltip){
5100             this.config[col].tooltip = tooltip;
5101     },
5102
5103     /**
5104      * Returns the dataIndex for the specified column.
5105      * @param {Number} col The column index
5106      * @return {Number}
5107      */
5108     getDataIndex : function(col){
5109         return this.config[col].dataIndex;
5110     },
5111
5112     /**
5113      * Sets the dataIndex for a column.
5114      * @param {Number} col The column index
5115      * @param {Number} dataIndex The new dataIndex
5116      */
5117     setDataIndex : function(col, dataIndex){
5118         this.config[col].dataIndex = dataIndex;
5119     },
5120
5121     
5122     
5123     /**
5124      * Returns true if the cell is editable.
5125      * @param {Number} colIndex The column index
5126      * @param {Number} rowIndex The row index
5127      * @return {Boolean}
5128      */
5129     isCellEditable : function(colIndex, rowIndex){
5130         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5131     },
5132
5133     /**
5134      * Returns the editor defined for the cell/column.
5135      * return false or null to disable editing.
5136      * @param {Number} colIndex The column index
5137      * @param {Number} rowIndex The row index
5138      * @return {Object}
5139      */
5140     getCellEditor : function(colIndex, rowIndex){
5141         return this.config[colIndex].editor;
5142     },
5143
5144     /**
5145      * Sets if a column is editable.
5146      * @param {Number} col The column index
5147      * @param {Boolean} editable True if the column is editable
5148      */
5149     setEditable : function(col, editable){
5150         this.config[col].editable = editable;
5151     },
5152
5153
5154     /**
5155      * Returns true if the column is hidden.
5156      * @param {Number} colIndex The column index
5157      * @return {Boolean}
5158      */
5159     isHidden : function(colIndex){
5160         return this.config[colIndex].hidden;
5161     },
5162
5163
5164     /**
5165      * Returns true if the column width cannot be changed
5166      */
5167     isFixed : function(colIndex){
5168         return this.config[colIndex].fixed;
5169     },
5170
5171     /**
5172      * Returns true if the column can be resized
5173      * @return {Boolean}
5174      */
5175     isResizable : function(colIndex){
5176         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5177     },
5178     /**
5179      * Sets if a column is hidden.
5180      * @param {Number} colIndex The column index
5181      * @param {Boolean} hidden True if the column is hidden
5182      */
5183     setHidden : function(colIndex, hidden){
5184         this.config[colIndex].hidden = hidden;
5185         this.totalWidth = null;
5186         this.fireEvent("hiddenchange", this, colIndex, hidden);
5187     },
5188
5189     /**
5190      * Sets the editor for a column.
5191      * @param {Number} col The column index
5192      * @param {Object} editor The editor object
5193      */
5194     setEditor : function(col, editor){
5195         this.config[col].editor = editor;
5196     }
5197 });
5198
5199 Roo.grid.ColumnModel.defaultRenderer = function(value){
5200         if(typeof value == "string" && value.length < 1){
5201             return "&#160;";
5202         }
5203         return value;
5204 };
5205
5206 // Alias for backwards compatibility
5207 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5208 /*
5209  * Based on:
5210  * Ext JS Library 1.1.1
5211  * Copyright(c) 2006-2007, Ext JS, LLC.
5212  *
5213  * Originally Released Under LGPL - original licence link has changed is not relivant.
5214  *
5215  * Fork - LGPL
5216  * <script type="text/javascript">
5217  */
5218  
5219 /**
5220  * @class Roo.LoadMask
5221  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5222  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5223  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5224  * element's UpdateManager load indicator and will be destroyed after the initial load.
5225  * @constructor
5226  * Create a new LoadMask
5227  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5228  * @param {Object} config The config object
5229  */
5230 Roo.LoadMask = function(el, config){
5231     this.el = Roo.get(el);
5232     Roo.apply(this, config);
5233     if(this.store){
5234         this.store.on('beforeload', this.onBeforeLoad, this);
5235         this.store.on('load', this.onLoad, this);
5236         this.store.on('loadexception', this.onLoadException, this);
5237         this.removeMask = false;
5238     }else{
5239         var um = this.el.getUpdateManager();
5240         um.showLoadIndicator = false; // disable the default indicator
5241         um.on('beforeupdate', this.onBeforeLoad, this);
5242         um.on('update', this.onLoad, this);
5243         um.on('failure', this.onLoad, this);
5244         this.removeMask = true;
5245     }
5246 };
5247
5248 Roo.LoadMask.prototype = {
5249     /**
5250      * @cfg {Boolean} removeMask
5251      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5252      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5253      */
5254     /**
5255      * @cfg {String} msg
5256      * The text to display in a centered loading message box (defaults to 'Loading...')
5257      */
5258     msg : 'Loading...',
5259     /**
5260      * @cfg {String} msgCls
5261      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5262      */
5263     msgCls : 'x-mask-loading',
5264
5265     /**
5266      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5267      * @type Boolean
5268      */
5269     disabled: false,
5270
5271     /**
5272      * Disables the mask to prevent it from being displayed
5273      */
5274     disable : function(){
5275        this.disabled = true;
5276     },
5277
5278     /**
5279      * Enables the mask so that it can be displayed
5280      */
5281     enable : function(){
5282         this.disabled = false;
5283     },
5284     
5285     onLoadException : function()
5286     {
5287         Roo.log(arguments);
5288         
5289         if (typeof(arguments[3]) != 'undefined') {
5290             Roo.MessageBox.alert("Error loading",arguments[3]);
5291         } 
5292         /*
5293         try {
5294             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5295                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5296             }   
5297         } catch(e) {
5298             
5299         }
5300         */
5301     
5302         
5303         
5304         this.el.unmask(this.removeMask);
5305     },
5306     // private
5307     onLoad : function()
5308     {
5309         this.el.unmask(this.removeMask);
5310     },
5311
5312     // private
5313     onBeforeLoad : function(){
5314         if(!this.disabled){
5315             this.el.mask(this.msg, this.msgCls);
5316         }
5317     },
5318
5319     // private
5320     destroy : function(){
5321         if(this.store){
5322             this.store.un('beforeload', this.onBeforeLoad, this);
5323             this.store.un('load', this.onLoad, this);
5324             this.store.un('loadexception', this.onLoadException, this);
5325         }else{
5326             var um = this.el.getUpdateManager();
5327             um.un('beforeupdate', this.onBeforeLoad, this);
5328             um.un('update', this.onLoad, this);
5329             um.un('failure', this.onLoad, this);
5330         }
5331     }
5332 };/*
5333  * - LGPL
5334  *
5335  * table
5336  * 
5337  */
5338
5339 /**
5340  * @class Roo.bootstrap.Table
5341  * @extends Roo.bootstrap.Component
5342  * Bootstrap Table class
5343  * @cfg {String} cls table class
5344  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5345  * @cfg {String} bgcolor Specifies the background color for a table
5346  * @cfg {Number} border Specifies whether the table cells should have borders or not
5347  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5348  * @cfg {Number} cellspacing Specifies the space between cells
5349  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5350  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5351  * @cfg {String} sortable Specifies that the table should be sortable
5352  * @cfg {String} summary Specifies a summary of the content of a table
5353  * @cfg {Number} width Specifies the width of a table
5354  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5355  * 
5356  * @cfg {boolean} striped Should the rows be alternative striped
5357  * @cfg {boolean} bordered Add borders to the table
5358  * @cfg {boolean} hover Add hover highlighting
5359  * @cfg {boolean} condensed Format condensed
5360  * @cfg {boolean} responsive Format condensed
5361  * @cfg {Boolean} loadMask (true|false) default false
5362  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5363  * @cfg {Boolean} thead (true|false) generate thead, default true
5364  * @cfg {Boolean} RowSelection (true|false) default false
5365  * @cfg {Boolean} CellSelection (true|false) default false
5366  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5367  
5368  * 
5369  * @constructor
5370  * Create a new Table
5371  * @param {Object} config The config object
5372  */
5373
5374 Roo.bootstrap.Table = function(config){
5375     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5376     
5377     if (this.sm) {
5378         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5379         this.sm = this.selModel;
5380         this.sm.xmodule = this.xmodule || false;
5381     }
5382     if (this.cm && typeof(this.cm.config) == 'undefined') {
5383         this.colModel = new Roo.grid.ColumnModel(this.cm);
5384         this.cm = this.colModel;
5385         this.cm.xmodule = this.xmodule || false;
5386     }
5387     if (this.store) {
5388         this.store= Roo.factory(this.store, Roo.data);
5389         this.ds = this.store;
5390         this.ds.xmodule = this.xmodule || false;
5391          
5392     }
5393     if (this.footer && this.store) {
5394         this.footer.dataSource = this.ds;
5395         this.footer = Roo.factory(this.footer);
5396     }
5397     
5398     /** @private */
5399     this.addEvents({
5400         /**
5401          * @event cellclick
5402          * Fires when a cell is clicked
5403          * @param {Roo.bootstrap.Table} this
5404          * @param {Roo.Element} el
5405          * @param {Number} rowIndex
5406          * @param {Number} columnIndex
5407          * @param {Roo.EventObject} e
5408          */
5409         "cellclick" : true,
5410         /**
5411          * @event celldblclick
5412          * Fires when a cell is double clicked
5413          * @param {Roo.bootstrap.Table} this
5414          * @param {Roo.Element} el
5415          * @param {Number} rowIndex
5416          * @param {Number} columnIndex
5417          * @param {Roo.EventObject} e
5418          */
5419         "celldblclick" : true,
5420         /**
5421          * @event rowclick
5422          * Fires when a row is clicked
5423          * @param {Roo.bootstrap.Table} this
5424          * @param {Roo.Element} el
5425          * @param {Number} rowIndex
5426          * @param {Roo.EventObject} e
5427          */
5428         "rowclick" : true,
5429         /**
5430          * @event rowdblclick
5431          * Fires when a row is double clicked
5432          * @param {Roo.bootstrap.Table} this
5433          * @param {Roo.Element} el
5434          * @param {Number} rowIndex
5435          * @param {Roo.EventObject} e
5436          */
5437         "rowdblclick" : true,
5438         /**
5439          * @event mouseover
5440          * Fires when a mouseover occur
5441          * @param {Roo.bootstrap.Table} this
5442          * @param {Roo.Element} el
5443          * @param {Number} rowIndex
5444          * @param {Number} columnIndex
5445          * @param {Roo.EventObject} e
5446          */
5447         "mouseover" : true,
5448         /**
5449          * @event mouseout
5450          * Fires when a mouseout occur
5451          * @param {Roo.bootstrap.Table} this
5452          * @param {Roo.Element} el
5453          * @param {Number} rowIndex
5454          * @param {Number} columnIndex
5455          * @param {Roo.EventObject} e
5456          */
5457         "mouseout" : true,
5458         /**
5459          * @event rowclass
5460          * Fires when a row is rendered, so you can change add a style to it.
5461          * @param {Roo.bootstrap.Table} this
5462          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5463          */
5464         'rowclass' : true,
5465           /**
5466          * @event rowsrendered
5467          * Fires when all the  rows have been rendered
5468          * @param {Roo.bootstrap.Table} this
5469          */
5470         'rowsrendered' : true
5471         
5472     });
5473 };
5474
5475 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5476     
5477     cls: false,
5478     align: false,
5479     bgcolor: false,
5480     border: false,
5481     cellpadding: false,
5482     cellspacing: false,
5483     frame: false,
5484     rules: false,
5485     sortable: false,
5486     summary: false,
5487     width: false,
5488     striped : false,
5489     bordered: false,
5490     hover:  false,
5491     condensed : false,
5492     responsive : false,
5493     sm : false,
5494     cm : false,
5495     store : false,
5496     loadMask : false,
5497     tfoot : true,
5498     thead : true,
5499     RowSelection : false,
5500     CellSelection : false,
5501     layout : false,
5502     
5503     // Roo.Element - the tbody
5504     mainBody: false, 
5505     
5506     getAutoCreate : function(){
5507         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5508         
5509         cfg = {
5510             tag: 'table',
5511             cls : 'table',
5512             cn : []
5513         }
5514             
5515         if (this.striped) {
5516             cfg.cls += ' table-striped';
5517         }
5518         
5519         if (this.hover) {
5520             cfg.cls += ' table-hover';
5521         }
5522         if (this.bordered) {
5523             cfg.cls += ' table-bordered';
5524         }
5525         if (this.condensed) {
5526             cfg.cls += ' table-condensed';
5527         }
5528         if (this.responsive) {
5529             cfg.cls += ' table-responsive';
5530         }
5531         
5532         if (this.cls) {
5533             cfg.cls+=  ' ' +this.cls;
5534         }
5535         
5536         // this lot should be simplifed...
5537         
5538         if (this.align) {
5539             cfg.align=this.align;
5540         }
5541         if (this.bgcolor) {
5542             cfg.bgcolor=this.bgcolor;
5543         }
5544         if (this.border) {
5545             cfg.border=this.border;
5546         }
5547         if (this.cellpadding) {
5548             cfg.cellpadding=this.cellpadding;
5549         }
5550         if (this.cellspacing) {
5551             cfg.cellspacing=this.cellspacing;
5552         }
5553         if (this.frame) {
5554             cfg.frame=this.frame;
5555         }
5556         if (this.rules) {
5557             cfg.rules=this.rules;
5558         }
5559         if (this.sortable) {
5560             cfg.sortable=this.sortable;
5561         }
5562         if (this.summary) {
5563             cfg.summary=this.summary;
5564         }
5565         if (this.width) {
5566             cfg.width=this.width;
5567         }
5568         if (this.layout) {
5569             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5570         }
5571         
5572         if(this.store || this.cm){
5573             if(this.thead){
5574                 cfg.cn.push(this.renderHeader());
5575             }
5576             
5577             cfg.cn.push(this.renderBody());
5578             
5579             if(this.tfoot){
5580                 cfg.cn.push(this.renderFooter());
5581             }
5582             
5583             cfg.cls+=  ' TableGrid';
5584         }
5585         
5586         return { cn : [ cfg ] };
5587     },
5588     
5589     initEvents : function()
5590     {   
5591         if(!this.store || !this.cm){
5592             return;
5593         }
5594         
5595         //Roo.log('initEvents with ds!!!!');
5596         
5597         this.mainBody = this.el.select('tbody', true).first();
5598         
5599         
5600         var _this = this;
5601         
5602         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5603             e.on('click', _this.sort, _this);
5604         });
5605         
5606         this.el.on("click", this.onClick, this);
5607         this.el.on("dblclick", this.onDblClick, this);
5608         
5609         // why is this done????? = it breaks dialogs??
5610         //this.parent().el.setStyle('position', 'relative');
5611         
5612         
5613         if (this.footer) {
5614             this.footer.parentId = this.id;
5615             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5616         }
5617         
5618         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5619         
5620         this.store.on('load', this.onLoad, this);
5621         this.store.on('beforeload', this.onBeforeLoad, this);
5622         this.store.on('update', this.onUpdate, this);
5623         this.store.on('add', this.onAdd, this);
5624         
5625     },
5626     
5627     onMouseover : function(e, el)
5628     {
5629         var cell = Roo.get(el);
5630         
5631         if(!cell){
5632             return;
5633         }
5634         
5635         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5636             cell = cell.findParent('td', false, true);
5637         }
5638         
5639         var row = cell.findParent('tr', false, true);
5640         var cellIndex = cell.dom.cellIndex;
5641         var rowIndex = row.dom.rowIndex - 1; // start from 0
5642         
5643         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5644         
5645     },
5646     
5647     onMouseout : function(e, el)
5648     {
5649         var cell = Roo.get(el);
5650         
5651         if(!cell){
5652             return;
5653         }
5654         
5655         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5656             cell = cell.findParent('td', false, true);
5657         }
5658         
5659         var row = cell.findParent('tr', false, true);
5660         var cellIndex = cell.dom.cellIndex;
5661         var rowIndex = row.dom.rowIndex - 1; // start from 0
5662         
5663         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5664         
5665     },
5666     
5667     onClick : function(e, el)
5668     {
5669         var cell = Roo.get(el);
5670         
5671         if(!cell || (!this.CellSelection && !this.RowSelection)){
5672             return;
5673         }
5674         
5675         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5676             cell = cell.findParent('td', false, true);
5677         }
5678         
5679         if(!cell || typeof(cell) == 'undefined'){
5680             return;
5681         }
5682         
5683         var row = cell.findParent('tr', false, true);
5684         
5685         if(!row || typeof(row) == 'undefined'){
5686             return;
5687         }
5688         
5689         var cellIndex = cell.dom.cellIndex;
5690         var rowIndex = this.getRowIndex(row);
5691         
5692         if(this.CellSelection){
5693             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5694         }
5695         
5696         if(this.RowSelection){
5697             this.fireEvent('rowclick', this, row, rowIndex, e);
5698         }
5699         
5700         
5701     },
5702     
5703     onDblClick : function(e,el)
5704     {
5705         var cell = Roo.get(el);
5706         
5707         if(!cell || (!this.CellSelection && !this.RowSelection)){
5708             return;
5709         }
5710         
5711         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5712             cell = cell.findParent('td', false, true);
5713         }
5714         
5715         if(!cell || typeof(cell) == 'undefined'){
5716             return;
5717         }
5718         
5719         var row = cell.findParent('tr', false, true);
5720         
5721         if(!row || typeof(row) == 'undefined'){
5722             return;
5723         }
5724         
5725         var cellIndex = cell.dom.cellIndex;
5726         var rowIndex = this.getRowIndex(row);
5727         
5728         if(this.CellSelection){
5729             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5730         }
5731         
5732         if(this.RowSelection){
5733             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5734         }
5735     },
5736     
5737     sort : function(e,el)
5738     {
5739         var col = Roo.get(el);
5740         
5741         if(!col.hasClass('sortable')){
5742             return;
5743         }
5744         
5745         var sort = col.attr('sort');
5746         var dir = 'ASC';
5747         
5748         if(col.hasClass('glyphicon-arrow-up')){
5749             dir = 'DESC';
5750         }
5751         
5752         this.store.sortInfo = {field : sort, direction : dir};
5753         
5754         if (this.footer) {
5755             Roo.log("calling footer first");
5756             this.footer.onClick('first');
5757         } else {
5758         
5759             this.store.load({ params : { start : 0 } });
5760         }
5761     },
5762     
5763     renderHeader : function()
5764     {
5765         var header = {
5766             tag: 'thead',
5767             cn : []
5768         };
5769         
5770         var cm = this.cm;
5771         
5772         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5773             
5774             var config = cm.config[i];
5775                     
5776             var c = {
5777                 tag: 'th',
5778                 style : '',
5779                 html: cm.getColumnHeader(i)
5780             };
5781             
5782             if(typeof(config.tooltip) != 'undefined'){
5783                 c.tooltip = config.tooltip;
5784             }
5785             
5786             if(typeof(config.colspan) != 'undefined'){
5787                 c.colspan = config.colspan;
5788             }
5789             
5790             if(typeof(config.hidden) != 'undefined' && config.hidden){
5791                 c.style += ' display:none;';
5792             }
5793             
5794             if(typeof(config.dataIndex) != 'undefined'){
5795                 c.sort = config.dataIndex;
5796             }
5797             
5798             if(typeof(config.sortable) != 'undefined' && config.sortable){
5799                 c.cls = 'sortable';
5800             }
5801             
5802             if(typeof(config.align) != 'undefined' && config.align.length){
5803                 c.style += ' text-align:' + config.align + ';';
5804             }
5805             
5806             if(typeof(config.width) != 'undefined'){
5807                 c.style += ' width:' + config.width + 'px;';
5808             }
5809             
5810             if(typeof(config.cls) != 'undefined'){
5811                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5812             }
5813             
5814             header.cn.push(c)
5815         }
5816         
5817         return header;
5818     },
5819     
5820     renderBody : function()
5821     {
5822         var body = {
5823             tag: 'tbody',
5824             cn : [
5825                 {
5826                     tag: 'tr',
5827                     cn : [
5828                         {
5829                             tag : 'td',
5830                             colspan :  this.cm.getColumnCount()
5831                         }
5832                     ]
5833                 }
5834             ]
5835         };
5836         
5837         return body;
5838     },
5839     
5840     renderFooter : function()
5841     {
5842         var footer = {
5843             tag: 'tfoot',
5844             cn : [
5845                 {
5846                     tag: 'tr',
5847                     cn : [
5848                         {
5849                             tag : 'td',
5850                             colspan :  this.cm.getColumnCount()
5851                         }
5852                     ]
5853                 }
5854             ]
5855         };
5856         
5857         return footer;
5858     },
5859     
5860     
5861     
5862     onLoad : function()
5863     {
5864         Roo.log('ds onload');
5865         this.clear();
5866         
5867         var _this = this;
5868         var cm = this.cm;
5869         var ds = this.store;
5870         
5871         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5872             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5873             
5874             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5875                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5876             }
5877             
5878             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5879                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5880             }
5881         });
5882         
5883         var tbody =  this.mainBody;
5884               
5885         if(ds.getCount() > 0){
5886             ds.data.each(function(d,rowIndex){
5887                 var row =  this.renderRow(cm, ds, rowIndex);
5888                 
5889                 tbody.createChild(row);
5890                 
5891                 var _this = this;
5892                 
5893                 if(row.cellObjects.length){
5894                     Roo.each(row.cellObjects, function(r){
5895                         _this.renderCellObject(r);
5896                     })
5897                 }
5898                 
5899             }, this);
5900         }
5901         
5902         Roo.each(this.el.select('tbody td', true).elements, function(e){
5903             e.on('mouseover', _this.onMouseover, _this);
5904         });
5905         
5906         Roo.each(this.el.select('tbody td', true).elements, function(e){
5907             e.on('mouseout', _this.onMouseout, _this);
5908         });
5909         this.fireEvent('rowsrendered', this);
5910         //if(this.loadMask){
5911         //    this.maskEl.hide();
5912         //}
5913     },
5914     
5915     
5916     onUpdate : function(ds,record)
5917     {
5918         this.refreshRow(record);
5919     },
5920     
5921     onRemove : function(ds, record, index, isUpdate){
5922         if(isUpdate !== true){
5923             this.fireEvent("beforerowremoved", this, index, record);
5924         }
5925         var bt = this.mainBody.dom;
5926         
5927         var rows = this.el.select('tbody > tr', true).elements;
5928         
5929         if(typeof(rows[index]) != 'undefined'){
5930             bt.removeChild(rows[index].dom);
5931         }
5932         
5933 //        if(bt.rows[index]){
5934 //            bt.removeChild(bt.rows[index]);
5935 //        }
5936         
5937         if(isUpdate !== true){
5938             //this.stripeRows(index);
5939             //this.syncRowHeights(index, index);
5940             //this.layout();
5941             this.fireEvent("rowremoved", this, index, record);
5942         }
5943     },
5944     
5945     onAdd : function(ds, records, rowIndex)
5946     {
5947         //Roo.log('on Add called');
5948         // - note this does not handle multiple adding very well..
5949         var bt = this.mainBody.dom;
5950         for (var i =0 ; i < records.length;i++) {
5951             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5952             //Roo.log(records[i]);
5953             //Roo.log(this.store.getAt(rowIndex+i));
5954             this.insertRow(this.store, rowIndex + i, false);
5955             return;
5956         }
5957         
5958     },
5959     
5960     
5961     refreshRow : function(record){
5962         var ds = this.store, index;
5963         if(typeof record == 'number'){
5964             index = record;
5965             record = ds.getAt(index);
5966         }else{
5967             index = ds.indexOf(record);
5968         }
5969         this.insertRow(ds, index, true);
5970         this.onRemove(ds, record, index+1, true);
5971         //this.syncRowHeights(index, index);
5972         //this.layout();
5973         this.fireEvent("rowupdated", this, index, record);
5974     },
5975     
5976     insertRow : function(dm, rowIndex, isUpdate){
5977         
5978         if(!isUpdate){
5979             this.fireEvent("beforerowsinserted", this, rowIndex);
5980         }
5981             //var s = this.getScrollState();
5982         var row = this.renderRow(this.cm, this.store, rowIndex);
5983         // insert before rowIndex..
5984         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5985         
5986         var _this = this;
5987                 
5988         if(row.cellObjects.length){
5989             Roo.each(row.cellObjects, function(r){
5990                 _this.renderCellObject(r);
5991             })
5992         }
5993             
5994         if(!isUpdate){
5995             this.fireEvent("rowsinserted", this, rowIndex);
5996             //this.syncRowHeights(firstRow, lastRow);
5997             //this.stripeRows(firstRow);
5998             //this.layout();
5999         }
6000         
6001     },
6002     
6003     
6004     getRowDom : function(rowIndex)
6005     {
6006         var rows = this.el.select('tbody > tr', true).elements;
6007         
6008         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6009         
6010     },
6011     // returns the object tree for a tr..
6012   
6013     
6014     renderRow : function(cm, ds, rowIndex) 
6015     {
6016         
6017         var d = ds.getAt(rowIndex);
6018         
6019         var row = {
6020             tag : 'tr',
6021             cn : []
6022         };
6023             
6024         var cellObjects = [];
6025         
6026         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6027             var config = cm.config[i];
6028             
6029             var renderer = cm.getRenderer(i);
6030             var value = '';
6031             var id = false;
6032             
6033             if(typeof(renderer) !== 'undefined'){
6034                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6035             }
6036             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6037             // and are rendered into the cells after the row is rendered - using the id for the element.
6038             
6039             if(typeof(value) === 'object'){
6040                 id = Roo.id();
6041                 cellObjects.push({
6042                     container : id,
6043                     cfg : value 
6044                 })
6045             }
6046             
6047             var rowcfg = {
6048                 record: d,
6049                 rowIndex : rowIndex,
6050                 colIndex : i,
6051                 rowClass : ''
6052             }
6053
6054             this.fireEvent('rowclass', this, rowcfg);
6055             
6056             var td = {
6057                 tag: 'td',
6058                 cls : rowcfg.rowClass,
6059                 style: '',
6060                 html: (typeof(value) === 'object') ? '' : value
6061             };
6062             
6063             if (id) {
6064                 td.id = id;
6065             }
6066             
6067             if(typeof(config.colspan) != 'undefined'){
6068                 td.colspan = config.colspan;
6069             }
6070             
6071             if(typeof(config.hidden) != 'undefined' && config.hidden){
6072                 td.style += ' display:none;';
6073             }
6074             
6075             if(typeof(config.align) != 'undefined' && config.align.length){
6076                 td.style += ' text-align:' + config.align + ';';
6077             }
6078             
6079             if(typeof(config.width) != 'undefined'){
6080                 td.style += ' width:' +  config.width + 'px;';
6081             }
6082             
6083             if(typeof(config.cursor) != 'undefined'){
6084                 td.style += ' cursor:' +  config.cursor + ';';
6085             }
6086             
6087             if(typeof(config.cls) != 'undefined'){
6088                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6089             }
6090              
6091             row.cn.push(td);
6092            
6093         }
6094         
6095         row.cellObjects = cellObjects;
6096         
6097         return row;
6098           
6099     },
6100     
6101     
6102     
6103     onBeforeLoad : function()
6104     {
6105         //Roo.log('ds onBeforeLoad');
6106         
6107         //this.clear();
6108         
6109         //if(this.loadMask){
6110         //    this.maskEl.show();
6111         //}
6112     },
6113      /**
6114      * Remove all rows
6115      */
6116     clear : function()
6117     {
6118         this.el.select('tbody', true).first().dom.innerHTML = '';
6119     },
6120     /**
6121      * Show or hide a row.
6122      * @param {Number} rowIndex to show or hide
6123      * @param {Boolean} state hide
6124      */
6125     setRowVisibility : function(rowIndex, state)
6126     {
6127         var bt = this.mainBody.dom;
6128         
6129         var rows = this.el.select('tbody > tr', true).elements;
6130         
6131         if(typeof(rows[rowIndex]) == 'undefined'){
6132             return;
6133         }
6134         rows[rowIndex].dom.style.display = state ? '' : 'none';
6135     },
6136     
6137     
6138     getSelectionModel : function(){
6139         if(!this.selModel){
6140             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6141         }
6142         return this.selModel;
6143     },
6144     /*
6145      * Render the Roo.bootstrap object from renderder
6146      */
6147     renderCellObject : function(r)
6148     {
6149         var _this = this;
6150         
6151         var t = r.cfg.render(r.container);
6152         
6153         if(r.cfg.cn){
6154             Roo.each(r.cfg.cn, function(c){
6155                 var child = {
6156                     container: t.getChildContainer(),
6157                     cfg: c
6158                 }
6159                 _this.renderCellObject(child);
6160             })
6161         }
6162     },
6163     
6164     getRowIndex : function(row)
6165     {
6166         var rowIndex = -1;
6167         
6168         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6169             if(el != row){
6170                 return;
6171             }
6172             
6173             rowIndex = index;
6174         });
6175         
6176         return rowIndex;
6177     }
6178    
6179 });
6180
6181  
6182
6183  /*
6184  * - LGPL
6185  *
6186  * table cell
6187  * 
6188  */
6189
6190 /**
6191  * @class Roo.bootstrap.TableCell
6192  * @extends Roo.bootstrap.Component
6193  * Bootstrap TableCell class
6194  * @cfg {String} html cell contain text
6195  * @cfg {String} cls cell class
6196  * @cfg {String} tag cell tag (td|th) default td
6197  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6198  * @cfg {String} align Aligns the content in a cell
6199  * @cfg {String} axis Categorizes cells
6200  * @cfg {String} bgcolor Specifies the background color of a cell
6201  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6202  * @cfg {Number} colspan Specifies the number of columns a cell should span
6203  * @cfg {String} headers Specifies one or more header cells a cell is related to
6204  * @cfg {Number} height Sets the height of a cell
6205  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6206  * @cfg {Number} rowspan Sets the number of rows a cell should span
6207  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6208  * @cfg {String} valign Vertical aligns the content in a cell
6209  * @cfg {Number} width Specifies the width of a cell
6210  * 
6211  * @constructor
6212  * Create a new TableCell
6213  * @param {Object} config The config object
6214  */
6215
6216 Roo.bootstrap.TableCell = function(config){
6217     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6218 };
6219
6220 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6221     
6222     html: false,
6223     cls: false,
6224     tag: false,
6225     abbr: false,
6226     align: false,
6227     axis: false,
6228     bgcolor: false,
6229     charoff: false,
6230     colspan: false,
6231     headers: false,
6232     height: false,
6233     nowrap: false,
6234     rowspan: false,
6235     scope: false,
6236     valign: false,
6237     width: false,
6238     
6239     
6240     getAutoCreate : function(){
6241         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6242         
6243         cfg = {
6244             tag: 'td'
6245         }
6246         
6247         if(this.tag){
6248             cfg.tag = this.tag;
6249         }
6250         
6251         if (this.html) {
6252             cfg.html=this.html
6253         }
6254         if (this.cls) {
6255             cfg.cls=this.cls
6256         }
6257         if (this.abbr) {
6258             cfg.abbr=this.abbr
6259         }
6260         if (this.align) {
6261             cfg.align=this.align
6262         }
6263         if (this.axis) {
6264             cfg.axis=this.axis
6265         }
6266         if (this.bgcolor) {
6267             cfg.bgcolor=this.bgcolor
6268         }
6269         if (this.charoff) {
6270             cfg.charoff=this.charoff
6271         }
6272         if (this.colspan) {
6273             cfg.colspan=this.colspan
6274         }
6275         if (this.headers) {
6276             cfg.headers=this.headers
6277         }
6278         if (this.height) {
6279             cfg.height=this.height
6280         }
6281         if (this.nowrap) {
6282             cfg.nowrap=this.nowrap
6283         }
6284         if (this.rowspan) {
6285             cfg.rowspan=this.rowspan
6286         }
6287         if (this.scope) {
6288             cfg.scope=this.scope
6289         }
6290         if (this.valign) {
6291             cfg.valign=this.valign
6292         }
6293         if (this.width) {
6294             cfg.width=this.width
6295         }
6296         
6297         
6298         return cfg;
6299     }
6300    
6301 });
6302
6303  
6304
6305  /*
6306  * - LGPL
6307  *
6308  * table row
6309  * 
6310  */
6311
6312 /**
6313  * @class Roo.bootstrap.TableRow
6314  * @extends Roo.bootstrap.Component
6315  * Bootstrap TableRow class
6316  * @cfg {String} cls row class
6317  * @cfg {String} align Aligns the content in a table row
6318  * @cfg {String} bgcolor Specifies a background color for a table row
6319  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6320  * @cfg {String} valign Vertical aligns the content in a table row
6321  * 
6322  * @constructor
6323  * Create a new TableRow
6324  * @param {Object} config The config object
6325  */
6326
6327 Roo.bootstrap.TableRow = function(config){
6328     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6329 };
6330
6331 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6332     
6333     cls: false,
6334     align: false,
6335     bgcolor: false,
6336     charoff: false,
6337     valign: false,
6338     
6339     getAutoCreate : function(){
6340         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6341         
6342         cfg = {
6343             tag: 'tr'
6344         }
6345             
6346         if(this.cls){
6347             cfg.cls = this.cls;
6348         }
6349         if(this.align){
6350             cfg.align = this.align;
6351         }
6352         if(this.bgcolor){
6353             cfg.bgcolor = this.bgcolor;
6354         }
6355         if(this.charoff){
6356             cfg.charoff = this.charoff;
6357         }
6358         if(this.valign){
6359             cfg.valign = this.valign;
6360         }
6361         
6362         return cfg;
6363     }
6364    
6365 });
6366
6367  
6368
6369  /*
6370  * - LGPL
6371  *
6372  * table body
6373  * 
6374  */
6375
6376 /**
6377  * @class Roo.bootstrap.TableBody
6378  * @extends Roo.bootstrap.Component
6379  * Bootstrap TableBody class
6380  * @cfg {String} cls element class
6381  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6382  * @cfg {String} align Aligns the content inside the element
6383  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6384  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6385  * 
6386  * @constructor
6387  * Create a new TableBody
6388  * @param {Object} config The config object
6389  */
6390
6391 Roo.bootstrap.TableBody = function(config){
6392     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6393 };
6394
6395 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6396     
6397     cls: false,
6398     tag: false,
6399     align: false,
6400     charoff: false,
6401     valign: false,
6402     
6403     getAutoCreate : function(){
6404         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6405         
6406         cfg = {
6407             tag: 'tbody'
6408         }
6409             
6410         if (this.cls) {
6411             cfg.cls=this.cls
6412         }
6413         if(this.tag){
6414             cfg.tag = this.tag;
6415         }
6416         
6417         if(this.align){
6418             cfg.align = this.align;
6419         }
6420         if(this.charoff){
6421             cfg.charoff = this.charoff;
6422         }
6423         if(this.valign){
6424             cfg.valign = this.valign;
6425         }
6426         
6427         return cfg;
6428     }
6429     
6430     
6431 //    initEvents : function()
6432 //    {
6433 //        
6434 //        if(!this.store){
6435 //            return;
6436 //        }
6437 //        
6438 //        this.store = Roo.factory(this.store, Roo.data);
6439 //        this.store.on('load', this.onLoad, this);
6440 //        
6441 //        this.store.load();
6442 //        
6443 //    },
6444 //    
6445 //    onLoad: function () 
6446 //    {   
6447 //        this.fireEvent('load', this);
6448 //    }
6449 //    
6450 //   
6451 });
6452
6453  
6454
6455  /*
6456  * Based on:
6457  * Ext JS Library 1.1.1
6458  * Copyright(c) 2006-2007, Ext JS, LLC.
6459  *
6460  * Originally Released Under LGPL - original licence link has changed is not relivant.
6461  *
6462  * Fork - LGPL
6463  * <script type="text/javascript">
6464  */
6465
6466 // as we use this in bootstrap.
6467 Roo.namespace('Roo.form');
6468  /**
6469  * @class Roo.form.Action
6470  * Internal Class used to handle form actions
6471  * @constructor
6472  * @param {Roo.form.BasicForm} el The form element or its id
6473  * @param {Object} config Configuration options
6474  */
6475
6476  
6477  
6478 // define the action interface
6479 Roo.form.Action = function(form, options){
6480     this.form = form;
6481     this.options = options || {};
6482 };
6483 /**
6484  * Client Validation Failed
6485  * @const 
6486  */
6487 Roo.form.Action.CLIENT_INVALID = 'client';
6488 /**
6489  * Server Validation Failed
6490  * @const 
6491  */
6492 Roo.form.Action.SERVER_INVALID = 'server';
6493  /**
6494  * Connect to Server Failed
6495  * @const 
6496  */
6497 Roo.form.Action.CONNECT_FAILURE = 'connect';
6498 /**
6499  * Reading Data from Server Failed
6500  * @const 
6501  */
6502 Roo.form.Action.LOAD_FAILURE = 'load';
6503
6504 Roo.form.Action.prototype = {
6505     type : 'default',
6506     failureType : undefined,
6507     response : undefined,
6508     result : undefined,
6509
6510     // interface method
6511     run : function(options){
6512
6513     },
6514
6515     // interface method
6516     success : function(response){
6517
6518     },
6519
6520     // interface method
6521     handleResponse : function(response){
6522
6523     },
6524
6525     // default connection failure
6526     failure : function(response){
6527         
6528         this.response = response;
6529         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6530         this.form.afterAction(this, false);
6531     },
6532
6533     processResponse : function(response){
6534         this.response = response;
6535         if(!response.responseText){
6536             return true;
6537         }
6538         this.result = this.handleResponse(response);
6539         return this.result;
6540     },
6541
6542     // utility functions used internally
6543     getUrl : function(appendParams){
6544         var url = this.options.url || this.form.url || this.form.el.dom.action;
6545         if(appendParams){
6546             var p = this.getParams();
6547             if(p){
6548                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6549             }
6550         }
6551         return url;
6552     },
6553
6554     getMethod : function(){
6555         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6556     },
6557
6558     getParams : function(){
6559         var bp = this.form.baseParams;
6560         var p = this.options.params;
6561         if(p){
6562             if(typeof p == "object"){
6563                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6564             }else if(typeof p == 'string' && bp){
6565                 p += '&' + Roo.urlEncode(bp);
6566             }
6567         }else if(bp){
6568             p = Roo.urlEncode(bp);
6569         }
6570         return p;
6571     },
6572
6573     createCallback : function(){
6574         return {
6575             success: this.success,
6576             failure: this.failure,
6577             scope: this,
6578             timeout: (this.form.timeout*1000),
6579             upload: this.form.fileUpload ? this.success : undefined
6580         };
6581     }
6582 };
6583
6584 Roo.form.Action.Submit = function(form, options){
6585     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6586 };
6587
6588 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6589     type : 'submit',
6590
6591     haveProgress : false,
6592     uploadComplete : false,
6593     
6594     // uploadProgress indicator.
6595     uploadProgress : function()
6596     {
6597         if (!this.form.progressUrl) {
6598             return;
6599         }
6600         
6601         if (!this.haveProgress) {
6602             Roo.MessageBox.progress("Uploading", "Uploading");
6603         }
6604         if (this.uploadComplete) {
6605            Roo.MessageBox.hide();
6606            return;
6607         }
6608         
6609         this.haveProgress = true;
6610    
6611         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6612         
6613         var c = new Roo.data.Connection();
6614         c.request({
6615             url : this.form.progressUrl,
6616             params: {
6617                 id : uid
6618             },
6619             method: 'GET',
6620             success : function(req){
6621                //console.log(data);
6622                 var rdata = false;
6623                 var edata;
6624                 try  {
6625                    rdata = Roo.decode(req.responseText)
6626                 } catch (e) {
6627                     Roo.log("Invalid data from server..");
6628                     Roo.log(edata);
6629                     return;
6630                 }
6631                 if (!rdata || !rdata.success) {
6632                     Roo.log(rdata);
6633                     Roo.MessageBox.alert(Roo.encode(rdata));
6634                     return;
6635                 }
6636                 var data = rdata.data;
6637                 
6638                 if (this.uploadComplete) {
6639                    Roo.MessageBox.hide();
6640                    return;
6641                 }
6642                    
6643                 if (data){
6644                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6645                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6646                     );
6647                 }
6648                 this.uploadProgress.defer(2000,this);
6649             },
6650        
6651             failure: function(data) {
6652                 Roo.log('progress url failed ');
6653                 Roo.log(data);
6654             },
6655             scope : this
6656         });
6657            
6658     },
6659     
6660     
6661     run : function()
6662     {
6663         // run get Values on the form, so it syncs any secondary forms.
6664         this.form.getValues();
6665         
6666         var o = this.options;
6667         var method = this.getMethod();
6668         var isPost = method == 'POST';
6669         if(o.clientValidation === false || this.form.isValid()){
6670             
6671             if (this.form.progressUrl) {
6672                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6673                     (new Date() * 1) + '' + Math.random());
6674                     
6675             } 
6676             
6677             
6678             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6679                 form:this.form.el.dom,
6680                 url:this.getUrl(!isPost),
6681                 method: method,
6682                 params:isPost ? this.getParams() : null,
6683                 isUpload: this.form.fileUpload
6684             }));
6685             
6686             this.uploadProgress();
6687
6688         }else if (o.clientValidation !== false){ // client validation failed
6689             this.failureType = Roo.form.Action.CLIENT_INVALID;
6690             this.form.afterAction(this, false);
6691         }
6692     },
6693
6694     success : function(response)
6695     {
6696         this.uploadComplete= true;
6697         if (this.haveProgress) {
6698             Roo.MessageBox.hide();
6699         }
6700         
6701         
6702         var result = this.processResponse(response);
6703         if(result === true || result.success){
6704             this.form.afterAction(this, true);
6705             return;
6706         }
6707         if(result.errors){
6708             this.form.markInvalid(result.errors);
6709             this.failureType = Roo.form.Action.SERVER_INVALID;
6710         }
6711         this.form.afterAction(this, false);
6712     },
6713     failure : function(response)
6714     {
6715         this.uploadComplete= true;
6716         if (this.haveProgress) {
6717             Roo.MessageBox.hide();
6718         }
6719         
6720         this.response = response;
6721         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6722         this.form.afterAction(this, false);
6723     },
6724     
6725     handleResponse : function(response){
6726         if(this.form.errorReader){
6727             var rs = this.form.errorReader.read(response);
6728             var errors = [];
6729             if(rs.records){
6730                 for(var i = 0, len = rs.records.length; i < len; i++) {
6731                     var r = rs.records[i];
6732                     errors[i] = r.data;
6733                 }
6734             }
6735             if(errors.length < 1){
6736                 errors = null;
6737             }
6738             return {
6739                 success : rs.success,
6740                 errors : errors
6741             };
6742         }
6743         var ret = false;
6744         try {
6745             ret = Roo.decode(response.responseText);
6746         } catch (e) {
6747             ret = {
6748                 success: false,
6749                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6750                 errors : []
6751             };
6752         }
6753         return ret;
6754         
6755     }
6756 });
6757
6758
6759 Roo.form.Action.Load = function(form, options){
6760     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6761     this.reader = this.form.reader;
6762 };
6763
6764 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6765     type : 'load',
6766
6767     run : function(){
6768         
6769         Roo.Ajax.request(Roo.apply(
6770                 this.createCallback(), {
6771                     method:this.getMethod(),
6772                     url:this.getUrl(false),
6773                     params:this.getParams()
6774         }));
6775     },
6776
6777     success : function(response){
6778         
6779         var result = this.processResponse(response);
6780         if(result === true || !result.success || !result.data){
6781             this.failureType = Roo.form.Action.LOAD_FAILURE;
6782             this.form.afterAction(this, false);
6783             return;
6784         }
6785         this.form.clearInvalid();
6786         this.form.setValues(result.data);
6787         this.form.afterAction(this, true);
6788     },
6789
6790     handleResponse : function(response){
6791         if(this.form.reader){
6792             var rs = this.form.reader.read(response);
6793             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6794             return {
6795                 success : rs.success,
6796                 data : data
6797             };
6798         }
6799         return Roo.decode(response.responseText);
6800     }
6801 });
6802
6803 Roo.form.Action.ACTION_TYPES = {
6804     'load' : Roo.form.Action.Load,
6805     'submit' : Roo.form.Action.Submit
6806 };/*
6807  * - LGPL
6808  *
6809  * form
6810  * 
6811  */
6812
6813 /**
6814  * @class Roo.bootstrap.Form
6815  * @extends Roo.bootstrap.Component
6816  * Bootstrap Form class
6817  * @cfg {String} method  GET | POST (default POST)
6818  * @cfg {String} labelAlign top | left (default top)
6819  * @cfg {String} align left  | right - for navbars
6820  * @cfg {Boolean} loadMask load mask when submit (default true)
6821
6822  * 
6823  * @constructor
6824  * Create a new Form
6825  * @param {Object} config The config object
6826  */
6827
6828
6829 Roo.bootstrap.Form = function(config){
6830     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6831     this.addEvents({
6832         /**
6833          * @event clientvalidation
6834          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6835          * @param {Form} this
6836          * @param {Boolean} valid true if the form has passed client-side validation
6837          */
6838         clientvalidation: true,
6839         /**
6840          * @event beforeaction
6841          * Fires before any action is performed. Return false to cancel the action.
6842          * @param {Form} this
6843          * @param {Action} action The action to be performed
6844          */
6845         beforeaction: true,
6846         /**
6847          * @event actionfailed
6848          * Fires when an action fails.
6849          * @param {Form} this
6850          * @param {Action} action The action that failed
6851          */
6852         actionfailed : true,
6853         /**
6854          * @event actioncomplete
6855          * Fires when an action is completed.
6856          * @param {Form} this
6857          * @param {Action} action The action that completed
6858          */
6859         actioncomplete : true
6860     });
6861     
6862 };
6863
6864 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6865       
6866      /**
6867      * @cfg {String} method
6868      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6869      */
6870     method : 'POST',
6871     /**
6872      * @cfg {String} url
6873      * The URL to use for form actions if one isn't supplied in the action options.
6874      */
6875     /**
6876      * @cfg {Boolean} fileUpload
6877      * Set to true if this form is a file upload.
6878      */
6879      
6880     /**
6881      * @cfg {Object} baseParams
6882      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6883      */
6884       
6885     /**
6886      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6887      */
6888     timeout: 30,
6889     /**
6890      * @cfg {Sting} align (left|right) for navbar forms
6891      */
6892     align : 'left',
6893
6894     // private
6895     activeAction : null,
6896  
6897     /**
6898      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6899      * element by passing it or its id or mask the form itself by passing in true.
6900      * @type Mixed
6901      */
6902     waitMsgTarget : false,
6903     
6904     loadMask : true,
6905     
6906     getAutoCreate : function(){
6907         
6908         var cfg = {
6909             tag: 'form',
6910             method : this.method || 'POST',
6911             id : this.id || Roo.id(),
6912             cls : ''
6913         }
6914         if (this.parent().xtype.match(/^Nav/)) {
6915             cfg.cls = 'navbar-form navbar-' + this.align;
6916             
6917         }
6918         
6919         if (this.labelAlign == 'left' ) {
6920             cfg.cls += ' form-horizontal';
6921         }
6922         
6923         
6924         return cfg;
6925     },
6926     initEvents : function()
6927     {
6928         this.el.on('submit', this.onSubmit, this);
6929         // this was added as random key presses on the form where triggering form submit.
6930         this.el.on('keypress', function(e) {
6931             if (e.getCharCode() != 13) {
6932                 return true;
6933             }
6934             // we might need to allow it for textareas.. and some other items.
6935             // check e.getTarget().
6936             
6937             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6938                 return true;
6939             }
6940         
6941             Roo.log("keypress blocked");
6942             
6943             e.preventDefault();
6944             return false;
6945         });
6946         
6947     },
6948     // private
6949     onSubmit : function(e){
6950         e.stopEvent();
6951     },
6952     
6953      /**
6954      * Returns true if client-side validation on the form is successful.
6955      * @return Boolean
6956      */
6957     isValid : function(){
6958         var items = this.getItems();
6959         var valid = true;
6960         items.each(function(f){
6961            if(!f.validate()){
6962                valid = false;
6963                
6964            }
6965         });
6966         return valid;
6967     },
6968     /**
6969      * Returns true if any fields in this form have changed since their original load.
6970      * @return Boolean
6971      */
6972     isDirty : function(){
6973         var dirty = false;
6974         var items = this.getItems();
6975         items.each(function(f){
6976            if(f.isDirty()){
6977                dirty = true;
6978                return false;
6979            }
6980            return true;
6981         });
6982         return dirty;
6983     },
6984      /**
6985      * Performs a predefined action (submit or load) or custom actions you define on this form.
6986      * @param {String} actionName The name of the action type
6987      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6988      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6989      * accept other config options):
6990      * <pre>
6991 Property          Type             Description
6992 ----------------  ---------------  ----------------------------------------------------------------------------------
6993 url               String           The url for the action (defaults to the form's url)
6994 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6995 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6996 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6997                                    validate the form on the client (defaults to false)
6998      * </pre>
6999      * @return {BasicForm} this
7000      */
7001     doAction : function(action, options){
7002         if(typeof action == 'string'){
7003             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7004         }
7005         if(this.fireEvent('beforeaction', this, action) !== false){
7006             this.beforeAction(action);
7007             action.run.defer(100, action);
7008         }
7009         return this;
7010     },
7011     
7012     // private
7013     beforeAction : function(action){
7014         var o = action.options;
7015         
7016         if(this.loadMask){
7017             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7018         }
7019         // not really supported yet.. ??
7020         
7021         //if(this.waitMsgTarget === true){
7022         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7023         //}else if(this.waitMsgTarget){
7024         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7025         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7026         //}else {
7027         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7028        // }
7029          
7030     },
7031
7032     // private
7033     afterAction : function(action, success){
7034         this.activeAction = null;
7035         var o = action.options;
7036         
7037         //if(this.waitMsgTarget === true){
7038             this.el.unmask();
7039         //}else if(this.waitMsgTarget){
7040         //    this.waitMsgTarget.unmask();
7041         //}else{
7042         //    Roo.MessageBox.updateProgress(1);
7043         //    Roo.MessageBox.hide();
7044        // }
7045         // 
7046         if(success){
7047             if(o.reset){
7048                 this.reset();
7049             }
7050             Roo.callback(o.success, o.scope, [this, action]);
7051             this.fireEvent('actioncomplete', this, action);
7052             
7053         }else{
7054             
7055             // failure condition..
7056             // we have a scenario where updates need confirming.
7057             // eg. if a locking scenario exists..
7058             // we look for { errors : { needs_confirm : true }} in the response.
7059             if (
7060                 (typeof(action.result) != 'undefined')  &&
7061                 (typeof(action.result.errors) != 'undefined')  &&
7062                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7063            ){
7064                 var _t = this;
7065                 Roo.log("not supported yet");
7066                  /*
7067                 
7068                 Roo.MessageBox.confirm(
7069                     "Change requires confirmation",
7070                     action.result.errorMsg,
7071                     function(r) {
7072                         if (r != 'yes') {
7073                             return;
7074                         }
7075                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7076                     }
7077                     
7078                 );
7079                 */
7080                 
7081                 
7082                 return;
7083             }
7084             
7085             Roo.callback(o.failure, o.scope, [this, action]);
7086             // show an error message if no failed handler is set..
7087             if (!this.hasListener('actionfailed')) {
7088                 Roo.log("need to add dialog support");
7089                 /*
7090                 Roo.MessageBox.alert("Error",
7091                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7092                         action.result.errorMsg :
7093                         "Saving Failed, please check your entries or try again"
7094                 );
7095                 */
7096             }
7097             
7098             this.fireEvent('actionfailed', this, action);
7099         }
7100         
7101     },
7102     /**
7103      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7104      * @param {String} id The value to search for
7105      * @return Field
7106      */
7107     findField : function(id){
7108         var items = this.getItems();
7109         var field = items.get(id);
7110         if(!field){
7111              items.each(function(f){
7112                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7113                     field = f;
7114                     return false;
7115                 }
7116                 return true;
7117             });
7118         }
7119         return field || null;
7120     },
7121      /**
7122      * Mark fields in this form invalid in bulk.
7123      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7124      * @return {BasicForm} this
7125      */
7126     markInvalid : function(errors){
7127         if(errors instanceof Array){
7128             for(var i = 0, len = errors.length; i < len; i++){
7129                 var fieldError = errors[i];
7130                 var f = this.findField(fieldError.id);
7131                 if(f){
7132                     f.markInvalid(fieldError.msg);
7133                 }
7134             }
7135         }else{
7136             var field, id;
7137             for(id in errors){
7138                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7139                     field.markInvalid(errors[id]);
7140                 }
7141             }
7142         }
7143         //Roo.each(this.childForms || [], function (f) {
7144         //    f.markInvalid(errors);
7145         //});
7146         
7147         return this;
7148     },
7149
7150     /**
7151      * Set values for fields in this form in bulk.
7152      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7153      * @return {BasicForm} this
7154      */
7155     setValues : function(values){
7156         if(values instanceof Array){ // array of objects
7157             for(var i = 0, len = values.length; i < len; i++){
7158                 var v = values[i];
7159                 var f = this.findField(v.id);
7160                 if(f){
7161                     f.setValue(v.value);
7162                     if(this.trackResetOnLoad){
7163                         f.originalValue = f.getValue();
7164                     }
7165                 }
7166             }
7167         }else{ // object hash
7168             var field, id;
7169             for(id in values){
7170                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7171                     
7172                     if (field.setFromData && 
7173                         field.valueField && 
7174                         field.displayField &&
7175                         // combos' with local stores can 
7176                         // be queried via setValue()
7177                         // to set their value..
7178                         (field.store && !field.store.isLocal)
7179                         ) {
7180                         // it's a combo
7181                         var sd = { };
7182                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7183                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7184                         field.setFromData(sd);
7185                         
7186                     } else {
7187                         field.setValue(values[id]);
7188                     }
7189                     
7190                     
7191                     if(this.trackResetOnLoad){
7192                         field.originalValue = field.getValue();
7193                     }
7194                 }
7195             }
7196         }
7197          
7198         //Roo.each(this.childForms || [], function (f) {
7199         //    f.setValues(values);
7200         //});
7201                 
7202         return this;
7203     },
7204
7205     /**
7206      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7207      * they are returned as an array.
7208      * @param {Boolean} asString
7209      * @return {Object}
7210      */
7211     getValues : function(asString){
7212         //if (this.childForms) {
7213             // copy values from the child forms
7214         //    Roo.each(this.childForms, function (f) {
7215         //        this.setValues(f.getValues());
7216         //    }, this);
7217         //}
7218         
7219         
7220         
7221         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7222         if(asString === true){
7223             return fs;
7224         }
7225         return Roo.urlDecode(fs);
7226     },
7227     
7228     /**
7229      * Returns the fields in this form as an object with key/value pairs. 
7230      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7231      * @return {Object}
7232      */
7233     getFieldValues : function(with_hidden)
7234     {
7235         var items = this.getItems();
7236         var ret = {};
7237         items.each(function(f){
7238             if (!f.getName()) {
7239                 return;
7240             }
7241             var v = f.getValue();
7242             if (f.inputType =='radio') {
7243                 if (typeof(ret[f.getName()]) == 'undefined') {
7244                     ret[f.getName()] = ''; // empty..
7245                 }
7246                 
7247                 if (!f.el.dom.checked) {
7248                     return;
7249                     
7250                 }
7251                 v = f.el.dom.value;
7252                 
7253             }
7254             
7255             // not sure if this supported any more..
7256             if ((typeof(v) == 'object') && f.getRawValue) {
7257                 v = f.getRawValue() ; // dates..
7258             }
7259             // combo boxes where name != hiddenName...
7260             if (f.name != f.getName()) {
7261                 ret[f.name] = f.getRawValue();
7262             }
7263             ret[f.getName()] = v;
7264         });
7265         
7266         return ret;
7267     },
7268
7269     /**
7270      * Clears all invalid messages in this form.
7271      * @return {BasicForm} this
7272      */
7273     clearInvalid : function(){
7274         var items = this.getItems();
7275         
7276         items.each(function(f){
7277            f.clearInvalid();
7278         });
7279         
7280         
7281         
7282         return this;
7283     },
7284
7285     /**
7286      * Resets this form.
7287      * @return {BasicForm} this
7288      */
7289     reset : function(){
7290         var items = this.getItems();
7291         items.each(function(f){
7292             f.reset();
7293         });
7294         
7295         Roo.each(this.childForms || [], function (f) {
7296             f.reset();
7297         });
7298        
7299         
7300         return this;
7301     },
7302     getItems : function()
7303     {
7304         var r=new Roo.util.MixedCollection(false, function(o){
7305             return o.id || (o.id = Roo.id());
7306         });
7307         var iter = function(el) {
7308             if (el.inputEl) {
7309                 r.add(el);
7310             }
7311             if (!el.items) {
7312                 return;
7313             }
7314             Roo.each(el.items,function(e) {
7315                 iter(e);
7316             });
7317             
7318             
7319         };
7320         
7321         iter(this);
7322         return r;
7323         
7324         
7325         
7326         
7327     }
7328     
7329 });
7330
7331  
7332 /*
7333  * Based on:
7334  * Ext JS Library 1.1.1
7335  * Copyright(c) 2006-2007, Ext JS, LLC.
7336  *
7337  * Originally Released Under LGPL - original licence link has changed is not relivant.
7338  *
7339  * Fork - LGPL
7340  * <script type="text/javascript">
7341  */
7342 /**
7343  * @class Roo.form.VTypes
7344  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7345  * @singleton
7346  */
7347 Roo.form.VTypes = function(){
7348     // closure these in so they are only created once.
7349     var alpha = /^[a-zA-Z_]+$/;
7350     var alphanum = /^[a-zA-Z0-9_]+$/;
7351     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7352     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7353
7354     // All these messages and functions are configurable
7355     return {
7356         /**
7357          * The function used to validate email addresses
7358          * @param {String} value The email address
7359          */
7360         'email' : function(v){
7361             return email.test(v);
7362         },
7363         /**
7364          * The error text to display when the email validation function returns false
7365          * @type String
7366          */
7367         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7368         /**
7369          * The keystroke filter mask to be applied on email input
7370          * @type RegExp
7371          */
7372         'emailMask' : /[a-z0-9_\.\-@]/i,
7373
7374         /**
7375          * The function used to validate URLs
7376          * @param {String} value The URL
7377          */
7378         'url' : function(v){
7379             return url.test(v);
7380         },
7381         /**
7382          * The error text to display when the url validation function returns false
7383          * @type String
7384          */
7385         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7386         
7387         /**
7388          * The function used to validate alpha values
7389          * @param {String} value The value
7390          */
7391         'alpha' : function(v){
7392             return alpha.test(v);
7393         },
7394         /**
7395          * The error text to display when the alpha validation function returns false
7396          * @type String
7397          */
7398         'alphaText' : 'This field should only contain letters and _',
7399         /**
7400          * The keystroke filter mask to be applied on alpha input
7401          * @type RegExp
7402          */
7403         'alphaMask' : /[a-z_]/i,
7404
7405         /**
7406          * The function used to validate alphanumeric values
7407          * @param {String} value The value
7408          */
7409         'alphanum' : function(v){
7410             return alphanum.test(v);
7411         },
7412         /**
7413          * The error text to display when the alphanumeric validation function returns false
7414          * @type String
7415          */
7416         'alphanumText' : 'This field should only contain letters, numbers and _',
7417         /**
7418          * The keystroke filter mask to be applied on alphanumeric input
7419          * @type RegExp
7420          */
7421         'alphanumMask' : /[a-z0-9_]/i
7422     };
7423 }();/*
7424  * - LGPL
7425  *
7426  * Input
7427  * 
7428  */
7429
7430 /**
7431  * @class Roo.bootstrap.Input
7432  * @extends Roo.bootstrap.Component
7433  * Bootstrap Input class
7434  * @cfg {Boolean} disabled is it disabled
7435  * @cfg {String} fieldLabel - the label associated
7436  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7437  * @cfg {String} name name of the input
7438  * @cfg {string} fieldLabel - the label associated
7439  * @cfg {string}  inputType - input / file submit ...
7440  * @cfg {string} placeholder - placeholder to put in text.
7441  * @cfg {string}  before - input group add on before
7442  * @cfg {string} after - input group add on after
7443  * @cfg {string} size - (lg|sm) or leave empty..
7444  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7445  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7446  * @cfg {Number} md colspan out of 12 for computer-sized screens
7447  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7448  * @cfg {string} value default value of the input
7449  * @cfg {Number} labelWidth set the width of label (0-12)
7450  * @cfg {String} labelAlign (top|left)
7451  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7452  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7453
7454  * @cfg {String} align (left|center|right) Default left
7455  * 
7456  * 
7457  * 
7458  * @constructor
7459  * Create a new Input
7460  * @param {Object} config The config object
7461  */
7462
7463 Roo.bootstrap.Input = function(config){
7464     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7465    
7466         this.addEvents({
7467             /**
7468              * @event focus
7469              * Fires when this field receives input focus.
7470              * @param {Roo.form.Field} this
7471              */
7472             focus : true,
7473             /**
7474              * @event blur
7475              * Fires when this field loses input focus.
7476              * @param {Roo.form.Field} this
7477              */
7478             blur : true,
7479             /**
7480              * @event specialkey
7481              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7482              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7483              * @param {Roo.form.Field} this
7484              * @param {Roo.EventObject} e The event object
7485              */
7486             specialkey : true,
7487             /**
7488              * @event change
7489              * Fires just before the field blurs if the field value has changed.
7490              * @param {Roo.form.Field} this
7491              * @param {Mixed} newValue The new value
7492              * @param {Mixed} oldValue The original value
7493              */
7494             change : true,
7495             /**
7496              * @event invalid
7497              * Fires after the field has been marked as invalid.
7498              * @param {Roo.form.Field} this
7499              * @param {String} msg The validation message
7500              */
7501             invalid : true,
7502             /**
7503              * @event valid
7504              * Fires after the field has been validated with no errors.
7505              * @param {Roo.form.Field} this
7506              */
7507             valid : true,
7508              /**
7509              * @event keyup
7510              * Fires after the key up
7511              * @param {Roo.form.Field} this
7512              * @param {Roo.EventObject}  e The event Object
7513              */
7514             keyup : true
7515         });
7516 };
7517
7518 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7519      /**
7520      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7521       automatic validation (defaults to "keyup").
7522      */
7523     validationEvent : "keyup",
7524      /**
7525      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7526      */
7527     validateOnBlur : true,
7528     /**
7529      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7530      */
7531     validationDelay : 250,
7532      /**
7533      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7534      */
7535     focusClass : "x-form-focus",  // not needed???
7536     
7537        
7538     /**
7539      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7540      */
7541     invalidClass : "has-warning",
7542     
7543     /**
7544      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7545      */
7546     validClass : "has-success",
7547     
7548     /**
7549      * @cfg {Boolean} hasFeedback (true|false) default true
7550      */
7551     hasFeedback : true,
7552     
7553     /**
7554      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7555      */
7556     invalidFeedbackClass : "glyphicon-warning-sign",
7557     
7558     /**
7559      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7560      */
7561     validFeedbackClass : "glyphicon-ok",
7562     
7563     /**
7564      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7565      */
7566     selectOnFocus : false,
7567     
7568      /**
7569      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7570      */
7571     maskRe : null,
7572        /**
7573      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7574      */
7575     vtype : null,
7576     
7577       /**
7578      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7579      */
7580     disableKeyFilter : false,
7581     
7582        /**
7583      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7584      */
7585     disabled : false,
7586      /**
7587      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7588      */
7589     allowBlank : true,
7590     /**
7591      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7592      */
7593     blankText : "This field is required",
7594     
7595      /**
7596      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7597      */
7598     minLength : 0,
7599     /**
7600      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7601      */
7602     maxLength : Number.MAX_VALUE,
7603     /**
7604      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7605      */
7606     minLengthText : "The minimum length for this field is {0}",
7607     /**
7608      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7609      */
7610     maxLengthText : "The maximum length for this field is {0}",
7611   
7612     
7613     /**
7614      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7615      * If available, this function will be called only after the basic validators all return true, and will be passed the
7616      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7617      */
7618     validator : null,
7619     /**
7620      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7621      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7622      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7623      */
7624     regex : null,
7625     /**
7626      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7627      */
7628     regexText : "",
7629     
7630     autocomplete: false,
7631     
7632     
7633     fieldLabel : '',
7634     inputType : 'text',
7635     
7636     name : false,
7637     placeholder: false,
7638     before : false,
7639     after : false,
7640     size : false,
7641     hasFocus : false,
7642     preventMark: false,
7643     isFormField : true,
7644     value : '',
7645     labelWidth : 2,
7646     labelAlign : false,
7647     readOnly : false,
7648     align : false,
7649     formatedValue : false,
7650     
7651     parentLabelAlign : function()
7652     {
7653         var parent = this;
7654         while (parent.parent()) {
7655             parent = parent.parent();
7656             if (typeof(parent.labelAlign) !='undefined') {
7657                 return parent.labelAlign;
7658             }
7659         }
7660         return 'left';
7661         
7662     },
7663     
7664     getAutoCreate : function(){
7665         
7666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7667         
7668         var id = Roo.id();
7669         
7670         var cfg = {};
7671         
7672         if(this.inputType != 'hidden'){
7673             cfg.cls = 'form-group' //input-group
7674         }
7675         
7676         var input =  {
7677             tag: 'input',
7678             id : id,
7679             type : this.inputType,
7680             value : this.value,
7681             cls : 'form-control',
7682             placeholder : this.placeholder || '',
7683             autocomplete : this.autocomplete || 'new-password'
7684         };
7685         
7686         
7687         if(this.align){
7688             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7689         }
7690         
7691         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7692             input.maxLength = this.maxLength;
7693         }
7694         
7695         if (this.disabled) {
7696             input.disabled=true;
7697         }
7698         
7699         if (this.readOnly) {
7700             input.readonly=true;
7701         }
7702         
7703         if (this.name) {
7704             input.name = this.name;
7705         }
7706         if (this.size) {
7707             input.cls += ' input-' + this.size;
7708         }
7709         var settings=this;
7710         ['xs','sm','md','lg'].map(function(size){
7711             if (settings[size]) {
7712                 cfg.cls += ' col-' + size + '-' + settings[size];
7713             }
7714         });
7715         
7716         var inputblock = input;
7717         
7718         var feedback = {
7719             tag: 'span',
7720             cls: 'glyphicon form-control-feedback'
7721         };
7722             
7723         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7724             
7725             inputblock = {
7726                 cls : 'has-feedback',
7727                 cn :  [
7728                     input,
7729                     feedback
7730                 ] 
7731             };  
7732         }
7733         
7734         if (this.before || this.after) {
7735             
7736             inputblock = {
7737                 cls : 'input-group',
7738                 cn :  [] 
7739             };
7740             
7741             if (this.before && typeof(this.before) == 'string') {
7742                 
7743                 inputblock.cn.push({
7744                     tag :'span',
7745                     cls : 'roo-input-before input-group-addon',
7746                     html : this.before
7747                 });
7748             }
7749             if (this.before && typeof(this.before) == 'object') {
7750                 this.before = Roo.factory(this.before);
7751                 Roo.log(this.before);
7752                 inputblock.cn.push({
7753                     tag :'span',
7754                     cls : 'roo-input-before input-group-' +
7755                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7756                 });
7757             }
7758             
7759             inputblock.cn.push(input);
7760             
7761             if (this.after && typeof(this.after) == 'string') {
7762                 inputblock.cn.push({
7763                     tag :'span',
7764                     cls : 'roo-input-after input-group-addon',
7765                     html : this.after
7766                 });
7767             }
7768             if (this.after && typeof(this.after) == 'object') {
7769                 this.after = Roo.factory(this.after);
7770                 Roo.log(this.after);
7771                 inputblock.cn.push({
7772                     tag :'span',
7773                     cls : 'roo-input-after input-group-' +
7774                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7775                 });
7776             }
7777             
7778             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7779                 inputblock.cls += ' has-feedback';
7780                 inputblock.cn.push(feedback);
7781             }
7782         };
7783         
7784         if (align ==='left' && this.fieldLabel.length) {
7785                 Roo.log("left and has label");
7786                 cfg.cn = [
7787                     
7788                     {
7789                         tag: 'label',
7790                         'for' :  id,
7791                         cls : 'control-label col-sm-' + this.labelWidth,
7792                         html : this.fieldLabel
7793                         
7794                     },
7795                     {
7796                         cls : "col-sm-" + (12 - this.labelWidth), 
7797                         cn: [
7798                             inputblock
7799                         ]
7800                     }
7801                     
7802                 ];
7803         } else if ( this.fieldLabel.length) {
7804                 Roo.log(" label");
7805                  cfg.cn = [
7806                    
7807                     {
7808                         tag: 'label',
7809                         //cls : 'input-group-addon',
7810                         html : this.fieldLabel
7811                         
7812                     },
7813                     
7814                     inputblock
7815                     
7816                 ];
7817
7818         } else {
7819             
7820                 Roo.log(" no label && no align");
7821                 cfg.cn = [
7822                     
7823                         inputblock
7824                     
7825                 ];
7826                 
7827                 
7828         };
7829         Roo.log('input-parentType: ' + this.parentType);
7830         
7831         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7832            cfg.cls += ' navbar-form';
7833            Roo.log(cfg);
7834         }
7835         
7836         return cfg;
7837         
7838     },
7839     /**
7840      * return the real input element.
7841      */
7842     inputEl: function ()
7843     {
7844         return this.el.select('input.form-control',true).first();
7845     },
7846     
7847     tooltipEl : function()
7848     {
7849         return this.inputEl();
7850     },
7851     
7852     setDisabled : function(v)
7853     {
7854         var i  = this.inputEl().dom;
7855         if (!v) {
7856             i.removeAttribute('disabled');
7857             return;
7858             
7859         }
7860         i.setAttribute('disabled','true');
7861     },
7862     initEvents : function()
7863     {
7864           
7865         this.inputEl().on("keydown" , this.fireKey,  this);
7866         this.inputEl().on("focus", this.onFocus,  this);
7867         this.inputEl().on("blur", this.onBlur,  this);
7868         
7869         this.inputEl().relayEvent('keyup', this);
7870
7871         // reference to original value for reset
7872         this.originalValue = this.getValue();
7873         //Roo.form.TextField.superclass.initEvents.call(this);
7874         if(this.validationEvent == 'keyup'){
7875             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7876             this.inputEl().on('keyup', this.filterValidation, this);
7877         }
7878         else if(this.validationEvent !== false){
7879             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7880         }
7881         
7882         if(this.selectOnFocus){
7883             this.on("focus", this.preFocus, this);
7884             
7885         }
7886         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7887             this.inputEl().on("keypress", this.filterKeys, this);
7888         }
7889        /* if(this.grow){
7890             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7891             this.el.on("click", this.autoSize,  this);
7892         }
7893         */
7894         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7895             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7896         }
7897         
7898         if (typeof(this.before) == 'object') {
7899             this.before.render(this.el.select('.roo-input-before',true).first());
7900         }
7901         if (typeof(this.after) == 'object') {
7902             this.after.render(this.el.select('.roo-input-after',true).first());
7903         }
7904         
7905         
7906     },
7907     filterValidation : function(e){
7908         if(!e.isNavKeyPress()){
7909             this.validationTask.delay(this.validationDelay);
7910         }
7911     },
7912      /**
7913      * Validates the field value
7914      * @return {Boolean} True if the value is valid, else false
7915      */
7916     validate : function(){
7917         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7918         if(this.disabled || this.validateValue(this.getRawValue())){
7919             this.markValid();
7920             return true;
7921         }
7922         
7923         this.markInvalid();
7924         return false;
7925     },
7926     
7927     
7928     /**
7929      * Validates a value according to the field's validation rules and marks the field as invalid
7930      * if the validation fails
7931      * @param {Mixed} value The value to validate
7932      * @return {Boolean} True if the value is valid, else false
7933      */
7934     validateValue : function(value){
7935         if(value.length < 1)  { // if it's blank
7936             if(this.allowBlank){
7937                 return true;
7938             }
7939             return false;
7940         }
7941         
7942         if(value.length < this.minLength){
7943             return false;
7944         }
7945         if(value.length > this.maxLength){
7946             return false;
7947         }
7948         if(this.vtype){
7949             var vt = Roo.form.VTypes;
7950             if(!vt[this.vtype](value, this)){
7951                 return false;
7952             }
7953         }
7954         if(typeof this.validator == "function"){
7955             var msg = this.validator(value);
7956             if(msg !== true){
7957                 return false;
7958             }
7959         }
7960         
7961         if(this.regex && !this.regex.test(value)){
7962             return false;
7963         }
7964         
7965         return true;
7966     },
7967
7968     
7969     
7970      // private
7971     fireKey : function(e){
7972         //Roo.log('field ' + e.getKey());
7973         if(e.isNavKeyPress()){
7974             this.fireEvent("specialkey", this, e);
7975         }
7976     },
7977     focus : function (selectText){
7978         if(this.rendered){
7979             this.inputEl().focus();
7980             if(selectText === true){
7981                 this.inputEl().dom.select();
7982             }
7983         }
7984         return this;
7985     } ,
7986     
7987     onFocus : function(){
7988         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7989            // this.el.addClass(this.focusClass);
7990         }
7991         if(!this.hasFocus){
7992             this.hasFocus = true;
7993             this.startValue = this.getValue();
7994             this.fireEvent("focus", this);
7995         }
7996     },
7997     
7998     beforeBlur : Roo.emptyFn,
7999
8000     
8001     // private
8002     onBlur : function(){
8003         this.beforeBlur();
8004         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8005             //this.el.removeClass(this.focusClass);
8006         }
8007         this.hasFocus = false;
8008         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8009             this.validate();
8010         }
8011         var v = this.getValue();
8012         if(String(v) !== String(this.startValue)){
8013             this.fireEvent('change', this, v, this.startValue);
8014         }
8015         this.fireEvent("blur", this);
8016     },
8017     
8018     /**
8019      * Resets the current field value to the originally loaded value and clears any validation messages
8020      */
8021     reset : function(){
8022         this.setValue(this.originalValue);
8023         this.validate();
8024     },
8025      /**
8026      * Returns the name of the field
8027      * @return {Mixed} name The name field
8028      */
8029     getName: function(){
8030         return this.name;
8031     },
8032      /**
8033      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8034      * @return {Mixed} value The field value
8035      */
8036     getValue : function(){
8037         
8038         var v = this.inputEl().getValue();
8039         
8040         return v;
8041     },
8042     /**
8043      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8044      * @return {Mixed} value The field value
8045      */
8046     getRawValue : function(){
8047         var v = this.inputEl().getValue();
8048         
8049         return v;
8050     },
8051     
8052     /**
8053      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8054      * @param {Mixed} value The value to set
8055      */
8056     setRawValue : function(v){
8057         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8058     },
8059     
8060     selectText : function(start, end){
8061         var v = this.getRawValue();
8062         if(v.length > 0){
8063             start = start === undefined ? 0 : start;
8064             end = end === undefined ? v.length : end;
8065             var d = this.inputEl().dom;
8066             if(d.setSelectionRange){
8067                 d.setSelectionRange(start, end);
8068             }else if(d.createTextRange){
8069                 var range = d.createTextRange();
8070                 range.moveStart("character", start);
8071                 range.moveEnd("character", v.length-end);
8072                 range.select();
8073             }
8074         }
8075     },
8076     
8077     /**
8078      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8079      * @param {Mixed} value The value to set
8080      */
8081     setValue : function(v){
8082         this.value = v;
8083         if(this.rendered){
8084             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8085             this.validate();
8086         }
8087     },
8088     
8089     /*
8090     processValue : function(value){
8091         if(this.stripCharsRe){
8092             var newValue = value.replace(this.stripCharsRe, '');
8093             if(newValue !== value){
8094                 this.setRawValue(newValue);
8095                 return newValue;
8096             }
8097         }
8098         return value;
8099     },
8100   */
8101     preFocus : function(){
8102         
8103         if(this.selectOnFocus){
8104             this.inputEl().dom.select();
8105         }
8106     },
8107     filterKeys : function(e){
8108         var k = e.getKey();
8109         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8110             return;
8111         }
8112         var c = e.getCharCode(), cc = String.fromCharCode(c);
8113         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8114             return;
8115         }
8116         if(!this.maskRe.test(cc)){
8117             e.stopEvent();
8118         }
8119     },
8120      /**
8121      * Clear any invalid styles/messages for this field
8122      */
8123     clearInvalid : function(){
8124         
8125         if(!this.el || this.preventMark){ // not rendered
8126             return;
8127         }
8128         this.el.removeClass(this.invalidClass);
8129         
8130         this.fireEvent('valid', this);
8131     },
8132     
8133      /**
8134      * Mark this field as valid
8135      */
8136     markValid : function(){
8137         if(!this.el  || this.preventMark){ // not rendered
8138             return;
8139         }
8140         
8141         this.el.removeClass([this.invalidClass, this.validClass]);
8142         
8143         if(this.disabled || this.allowBlank){
8144             return;
8145         }
8146         
8147         this.el.addClass(this.validClass);
8148         
8149         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8150             
8151             var feedback = this.el.select('.form-control-feedback', true).first();
8152             
8153             if(feedback){
8154                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8155                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8156             }
8157             
8158         }
8159         
8160         this.fireEvent('valid', this);
8161     },
8162     
8163      /**
8164      * Mark this field as invalid
8165      * @param {String} msg The validation message
8166      */
8167     markInvalid : function(msg){
8168         if(!this.el  || this.preventMark){ // not rendered
8169             return;
8170         }
8171         
8172         this.el.removeClass([this.invalidClass, this.validClass]);
8173         
8174         if(this.disabled || this.allowBlank){
8175             return;
8176         }
8177         
8178         this.el.addClass(this.invalidClass);
8179         
8180         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8181             
8182             var feedback = this.el.select('.form-control-feedback', true).first();
8183             
8184             if(feedback){
8185                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8186                 
8187                 if(this.getValue().length){
8188                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8189                 }
8190                 
8191             }
8192             
8193         }
8194         
8195         this.fireEvent('invalid', this, msg);
8196     },
8197     // private
8198     SafariOnKeyDown : function(event)
8199     {
8200         // this is a workaround for a password hang bug on chrome/ webkit.
8201         
8202         var isSelectAll = false;
8203         
8204         if(this.inputEl().dom.selectionEnd > 0){
8205             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8206         }
8207         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8208             event.preventDefault();
8209             this.setValue('');
8210             return;
8211         }
8212         
8213         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8214             
8215             event.preventDefault();
8216             // this is very hacky as keydown always get's upper case.
8217             //
8218             var cc = String.fromCharCode(event.getCharCode());
8219             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8220             
8221         }
8222     },
8223     adjustWidth : function(tag, w){
8224         tag = tag.toLowerCase();
8225         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8226             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8227                 if(tag == 'input'){
8228                     return w + 2;
8229                 }
8230                 if(tag == 'textarea'){
8231                     return w-2;
8232                 }
8233             }else if(Roo.isOpera){
8234                 if(tag == 'input'){
8235                     return w + 2;
8236                 }
8237                 if(tag == 'textarea'){
8238                     return w-2;
8239                 }
8240             }
8241         }
8242         return w;
8243     }
8244     
8245 });
8246
8247  
8248 /*
8249  * - LGPL
8250  *
8251  * Input
8252  * 
8253  */
8254
8255 /**
8256  * @class Roo.bootstrap.TextArea
8257  * @extends Roo.bootstrap.Input
8258  * Bootstrap TextArea class
8259  * @cfg {Number} cols Specifies the visible width of a text area
8260  * @cfg {Number} rows Specifies the visible number of lines in a text area
8261  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8262  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8263  * @cfg {string} html text
8264  * 
8265  * @constructor
8266  * Create a new TextArea
8267  * @param {Object} config The config object
8268  */
8269
8270 Roo.bootstrap.TextArea = function(config){
8271     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8272    
8273 };
8274
8275 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8276      
8277     cols : false,
8278     rows : 5,
8279     readOnly : false,
8280     warp : 'soft',
8281     resize : false,
8282     value: false,
8283     html: false,
8284     
8285     getAutoCreate : function(){
8286         
8287         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8288         
8289         var id = Roo.id();
8290         
8291         var cfg = {};
8292         
8293         var input =  {
8294             tag: 'textarea',
8295             id : id,
8296             warp : this.warp,
8297             rows : this.rows,
8298             value : this.value || '',
8299             html: this.html || '',
8300             cls : 'form-control',
8301             placeholder : this.placeholder || '' 
8302             
8303         };
8304         
8305         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8306             input.maxLength = this.maxLength;
8307         }
8308         
8309         if(this.resize){
8310             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8311         }
8312         
8313         if(this.cols){
8314             input.cols = this.cols;
8315         }
8316         
8317         if (this.readOnly) {
8318             input.readonly = true;
8319         }
8320         
8321         if (this.name) {
8322             input.name = this.name;
8323         }
8324         
8325         if (this.size) {
8326             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8327         }
8328         
8329         var settings=this;
8330         ['xs','sm','md','lg'].map(function(size){
8331             if (settings[size]) {
8332                 cfg.cls += ' col-' + size + '-' + settings[size];
8333             }
8334         });
8335         
8336         var inputblock = input;
8337         
8338         if(this.hasFeedback && !this.allowBlank){
8339             
8340             var feedback = {
8341                 tag: 'span',
8342                 cls: 'glyphicon form-control-feedback'
8343             };
8344
8345             inputblock = {
8346                 cls : 'has-feedback',
8347                 cn :  [
8348                     input,
8349                     feedback
8350                 ] 
8351             };  
8352         }
8353         
8354         
8355         if (this.before || this.after) {
8356             
8357             inputblock = {
8358                 cls : 'input-group',
8359                 cn :  [] 
8360             };
8361             if (this.before) {
8362                 inputblock.cn.push({
8363                     tag :'span',
8364                     cls : 'input-group-addon',
8365                     html : this.before
8366                 });
8367             }
8368             
8369             inputblock.cn.push(input);
8370             
8371             if(this.hasFeedback && !this.allowBlank){
8372                 inputblock.cls += ' has-feedback';
8373                 inputblock.cn.push(feedback);
8374             }
8375             
8376             if (this.after) {
8377                 inputblock.cn.push({
8378                     tag :'span',
8379                     cls : 'input-group-addon',
8380                     html : this.after
8381                 });
8382             }
8383             
8384         }
8385         
8386         if (align ==='left' && this.fieldLabel.length) {
8387                 Roo.log("left and has label");
8388                 cfg.cn = [
8389                     
8390                     {
8391                         tag: 'label',
8392                         'for' :  id,
8393                         cls : 'control-label col-sm-' + this.labelWidth,
8394                         html : this.fieldLabel
8395                         
8396                     },
8397                     {
8398                         cls : "col-sm-" + (12 - this.labelWidth), 
8399                         cn: [
8400                             inputblock
8401                         ]
8402                     }
8403                     
8404                 ];
8405         } else if ( this.fieldLabel.length) {
8406                 Roo.log(" label");
8407                  cfg.cn = [
8408                    
8409                     {
8410                         tag: 'label',
8411                         //cls : 'input-group-addon',
8412                         html : this.fieldLabel
8413                         
8414                     },
8415                     
8416                     inputblock
8417                     
8418                 ];
8419
8420         } else {
8421             
8422                    Roo.log(" no label && no align");
8423                 cfg.cn = [
8424                     
8425                         inputblock
8426                     
8427                 ];
8428                 
8429                 
8430         }
8431         
8432         if (this.disabled) {
8433             input.disabled=true;
8434         }
8435         
8436         return cfg;
8437         
8438     },
8439     /**
8440      * return the real textarea element.
8441      */
8442     inputEl: function ()
8443     {
8444         return this.el.select('textarea.form-control',true).first();
8445     }
8446 });
8447
8448  
8449 /*
8450  * - LGPL
8451  *
8452  * trigger field - base class for combo..
8453  * 
8454  */
8455  
8456 /**
8457  * @class Roo.bootstrap.TriggerField
8458  * @extends Roo.bootstrap.Input
8459  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8460  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8461  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8462  * for which you can provide a custom implementation.  For example:
8463  * <pre><code>
8464 var trigger = new Roo.bootstrap.TriggerField();
8465 trigger.onTriggerClick = myTriggerFn;
8466 trigger.applyTo('my-field');
8467 </code></pre>
8468  *
8469  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8470  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8471  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8472  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8473  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8474
8475  * @constructor
8476  * Create a new TriggerField.
8477  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8478  * to the base TextField)
8479  */
8480 Roo.bootstrap.TriggerField = function(config){
8481     this.mimicing = false;
8482     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8483 };
8484
8485 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8486     /**
8487      * @cfg {String} triggerClass A CSS class to apply to the trigger
8488      */
8489      /**
8490      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8491      */
8492     hideTrigger:false,
8493
8494     /**
8495      * @cfg {Boolean} removable (true|false) special filter default false
8496      */
8497     removable : false,
8498     
8499     /** @cfg {Boolean} grow @hide */
8500     /** @cfg {Number} growMin @hide */
8501     /** @cfg {Number} growMax @hide */
8502
8503     /**
8504      * @hide 
8505      * @method
8506      */
8507     autoSize: Roo.emptyFn,
8508     // private
8509     monitorTab : true,
8510     // private
8511     deferHeight : true,
8512
8513     
8514     actionMode : 'wrap',
8515     
8516     caret : false,
8517     
8518     
8519     getAutoCreate : function(){
8520        
8521         var align = this.labelAlign || this.parentLabelAlign();
8522         
8523         var id = Roo.id();
8524         
8525         var cfg = {
8526             cls: 'form-group' //input-group
8527         };
8528         
8529         
8530         var input =  {
8531             tag: 'input',
8532             id : id,
8533             type : this.inputType,
8534             cls : 'form-control',
8535             autocomplete: 'new-password',
8536             placeholder : this.placeholder || '' 
8537             
8538         };
8539         if (this.name) {
8540             input.name = this.name;
8541         }
8542         if (this.size) {
8543             input.cls += ' input-' + this.size;
8544         }
8545         
8546         if (this.disabled) {
8547             input.disabled=true;
8548         }
8549         
8550         var inputblock = input;
8551         
8552         if(this.hasFeedback && !this.allowBlank){
8553             
8554             var feedback = {
8555                 tag: 'span',
8556                 cls: 'glyphicon form-control-feedback'
8557             };
8558             
8559             if(this.removable && !this.editable && !this.tickable){
8560                 inputblock = {
8561                     cls : 'has-feedback',
8562                     cn :  [
8563                         inputblock,
8564                         {
8565                             tag: 'button',
8566                             html : 'x',
8567                             cls : 'roo-combo-removable-btn close'
8568                         },
8569                         feedback
8570                     ] 
8571                 };
8572             } else {
8573                 inputblock = {
8574                     cls : 'has-feedback',
8575                     cn :  [
8576                         inputblock,
8577                         feedback
8578                     ] 
8579                 };
8580             }
8581               
8582         } else {
8583             if(this.removable && !this.editable && !this.tickable){
8584                 inputblock = {
8585                     cls : 'roo-removable',
8586                     cn :  [
8587                         inputblock,
8588                         {
8589                             tag: 'button',
8590                             html : 'x',
8591                             cls : 'roo-combo-removable-btn close'
8592                         }
8593                     ] 
8594                 };
8595             }
8596         }
8597         
8598         if (this.before || this.after) {
8599             
8600             inputblock = {
8601                 cls : 'input-group',
8602                 cn :  [] 
8603             };
8604             if (this.before) {
8605                 inputblock.cn.push({
8606                     tag :'span',
8607                     cls : 'input-group-addon',
8608                     html : this.before
8609                 });
8610             }
8611             
8612             inputblock.cn.push(input);
8613             
8614             if(this.hasFeedback && !this.allowBlank){
8615                 inputblock.cls += ' has-feedback';
8616                 inputblock.cn.push(feedback);
8617             }
8618             
8619             if (this.after) {
8620                 inputblock.cn.push({
8621                     tag :'span',
8622                     cls : 'input-group-addon',
8623                     html : this.after
8624                 });
8625             }
8626             
8627         };
8628         
8629         var box = {
8630             tag: 'div',
8631             cn: [
8632                 {
8633                     tag: 'input',
8634                     type : 'hidden',
8635                     cls: 'form-hidden-field'
8636                 },
8637                 inputblock
8638             ]
8639             
8640         };
8641         
8642         if(this.multiple){
8643             Roo.log('multiple');
8644             
8645             box = {
8646                 tag: 'div',
8647                 cn: [
8648                     {
8649                         tag: 'input',
8650                         type : 'hidden',
8651                         cls: 'form-hidden-field'
8652                     },
8653                     {
8654                         tag: 'ul',
8655                         cls: 'select2-choices',
8656                         cn:[
8657                             {
8658                                 tag: 'li',
8659                                 cls: 'select2-search-field',
8660                                 cn: [
8661
8662                                     inputblock
8663                                 ]
8664                             }
8665                         ]
8666                     }
8667                 ]
8668             }
8669         };
8670         
8671         var combobox = {
8672             cls: 'select2-container input-group',
8673             cn: [
8674                 box
8675 //                {
8676 //                    tag: 'ul',
8677 //                    cls: 'typeahead typeahead-long dropdown-menu',
8678 //                    style: 'display:none'
8679 //                }
8680             ]
8681         };
8682         
8683         if(!this.multiple && this.showToggleBtn){
8684             
8685             var caret = {
8686                         tag: 'span',
8687                         cls: 'caret'
8688              };
8689             if (this.caret != false) {
8690                 caret = {
8691                      tag: 'i',
8692                      cls: 'fa fa-' + this.caret
8693                 };
8694                 
8695             }
8696             
8697             combobox.cn.push({
8698                 tag :'span',
8699                 cls : 'input-group-addon btn dropdown-toggle',
8700                 cn : [
8701                     caret,
8702                     {
8703                         tag: 'span',
8704                         cls: 'combobox-clear',
8705                         cn  : [
8706                             {
8707                                 tag : 'i',
8708                                 cls: 'icon-remove'
8709                             }
8710                         ]
8711                     }
8712                 ]
8713
8714             })
8715         }
8716         
8717         if(this.multiple){
8718             combobox.cls += ' select2-container-multi';
8719         }
8720         
8721         if (align ==='left' && this.fieldLabel.length) {
8722             
8723                 Roo.log("left and has label");
8724                 cfg.cn = [
8725                     
8726                     {
8727                         tag: 'label',
8728                         'for' :  id,
8729                         cls : 'control-label col-sm-' + this.labelWidth,
8730                         html : this.fieldLabel
8731                         
8732                     },
8733                     {
8734                         cls : "col-sm-" + (12 - this.labelWidth), 
8735                         cn: [
8736                             combobox
8737                         ]
8738                     }
8739                     
8740                 ];
8741         } else if ( this.fieldLabel.length) {
8742                 Roo.log(" label");
8743                  cfg.cn = [
8744                    
8745                     {
8746                         tag: 'label',
8747                         //cls : 'input-group-addon',
8748                         html : this.fieldLabel
8749                         
8750                     },
8751                     
8752                     combobox
8753                     
8754                 ];
8755
8756         } else {
8757             
8758                 Roo.log(" no label && no align");
8759                 cfg = combobox
8760                      
8761                 
8762         }
8763          
8764         var settings=this;
8765         ['xs','sm','md','lg'].map(function(size){
8766             if (settings[size]) {
8767                 cfg.cls += ' col-' + size + '-' + settings[size];
8768             }
8769         });
8770         
8771         return cfg;
8772         
8773     },
8774     
8775     
8776     
8777     // private
8778     onResize : function(w, h){
8779 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8780 //        if(typeof w == 'number'){
8781 //            var x = w - this.trigger.getWidth();
8782 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8783 //            this.trigger.setStyle('left', x+'px');
8784 //        }
8785     },
8786
8787     // private
8788     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8789
8790     // private
8791     getResizeEl : function(){
8792         return this.inputEl();
8793     },
8794
8795     // private
8796     getPositionEl : function(){
8797         return this.inputEl();
8798     },
8799
8800     // private
8801     alignErrorIcon : function(){
8802         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8803     },
8804
8805     // private
8806     initEvents : function(){
8807         
8808         this.createList();
8809         
8810         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8811         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8812         if(!this.multiple && this.showToggleBtn){
8813             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8814             if(this.hideTrigger){
8815                 this.trigger.setDisplayed(false);
8816             }
8817             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8818         }
8819         
8820         if(this.multiple){
8821             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8822         }
8823         
8824         if(this.removable && !this.editable && !this.tickable){
8825             var close = this.closeTriggerEl();
8826             
8827             if(close){
8828                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8829                 close.on('click', this.removeBtnClick, this, close);
8830             }
8831         }
8832         
8833         //this.trigger.addClassOnOver('x-form-trigger-over');
8834         //this.trigger.addClassOnClick('x-form-trigger-click');
8835         
8836         //if(!this.width){
8837         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8838         //}
8839     },
8840     
8841     closeTriggerEl : function()
8842     {
8843         var close = this.el.select('.roo-combo-removable-btn', true).first();
8844         return close ? close : false;
8845     },
8846     
8847     removeBtnClick : function(e, h, el)
8848     {
8849         e.preventDefault();
8850         
8851         this.fireEvent("remove", this);
8852     },
8853     
8854     createList : function()
8855     {
8856         this.list = Roo.get(document.body).createChild({
8857             tag: 'ul',
8858             cls: 'typeahead typeahead-long dropdown-menu',
8859             style: 'display:none'
8860         });
8861         
8862         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8863         
8864     },
8865
8866     // private
8867     initTrigger : function(){
8868        
8869     },
8870
8871     // private
8872     onDestroy : function(){
8873         if(this.trigger){
8874             this.trigger.removeAllListeners();
8875           //  this.trigger.remove();
8876         }
8877         //if(this.wrap){
8878         //    this.wrap.remove();
8879         //}
8880         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8881     },
8882
8883     // private
8884     onFocus : function(){
8885         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8886         /*
8887         if(!this.mimicing){
8888             this.wrap.addClass('x-trigger-wrap-focus');
8889             this.mimicing = true;
8890             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8891             if(this.monitorTab){
8892                 this.el.on("keydown", this.checkTab, this);
8893             }
8894         }
8895         */
8896     },
8897
8898     // private
8899     checkTab : function(e){
8900         if(e.getKey() == e.TAB){
8901             this.triggerBlur();
8902         }
8903     },
8904
8905     // private
8906     onBlur : function(){
8907         // do nothing
8908     },
8909
8910     // private
8911     mimicBlur : function(e, t){
8912         /*
8913         if(!this.wrap.contains(t) && this.validateBlur()){
8914             this.triggerBlur();
8915         }
8916         */
8917     },
8918
8919     // private
8920     triggerBlur : function(){
8921         this.mimicing = false;
8922         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8923         if(this.monitorTab){
8924             this.el.un("keydown", this.checkTab, this);
8925         }
8926         //this.wrap.removeClass('x-trigger-wrap-focus');
8927         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8928     },
8929
8930     // private
8931     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8932     validateBlur : function(e, t){
8933         return true;
8934     },
8935
8936     // private
8937     onDisable : function(){
8938         this.inputEl().dom.disabled = true;
8939         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8940         //if(this.wrap){
8941         //    this.wrap.addClass('x-item-disabled');
8942         //}
8943     },
8944
8945     // private
8946     onEnable : function(){
8947         this.inputEl().dom.disabled = false;
8948         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8949         //if(this.wrap){
8950         //    this.el.removeClass('x-item-disabled');
8951         //}
8952     },
8953
8954     // private
8955     onShow : function(){
8956         var ae = this.getActionEl();
8957         
8958         if(ae){
8959             ae.dom.style.display = '';
8960             ae.dom.style.visibility = 'visible';
8961         }
8962     },
8963
8964     // private
8965     
8966     onHide : function(){
8967         var ae = this.getActionEl();
8968         ae.dom.style.display = 'none';
8969     },
8970
8971     /**
8972      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8973      * by an implementing function.
8974      * @method
8975      * @param {EventObject} e
8976      */
8977     onTriggerClick : Roo.emptyFn
8978 });
8979  /*
8980  * Based on:
8981  * Ext JS Library 1.1.1
8982  * Copyright(c) 2006-2007, Ext JS, LLC.
8983  *
8984  * Originally Released Under LGPL - original licence link has changed is not relivant.
8985  *
8986  * Fork - LGPL
8987  * <script type="text/javascript">
8988  */
8989
8990
8991 /**
8992  * @class Roo.data.SortTypes
8993  * @singleton
8994  * Defines the default sorting (casting?) comparison functions used when sorting data.
8995  */
8996 Roo.data.SortTypes = {
8997     /**
8998      * Default sort that does nothing
8999      * @param {Mixed} s The value being converted
9000      * @return {Mixed} The comparison value
9001      */
9002     none : function(s){
9003         return s;
9004     },
9005     
9006     /**
9007      * The regular expression used to strip tags
9008      * @type {RegExp}
9009      * @property
9010      */
9011     stripTagsRE : /<\/?[^>]+>/gi,
9012     
9013     /**
9014      * Strips all HTML tags to sort on text only
9015      * @param {Mixed} s The value being converted
9016      * @return {String} The comparison value
9017      */
9018     asText : function(s){
9019         return String(s).replace(this.stripTagsRE, "");
9020     },
9021     
9022     /**
9023      * Strips all HTML tags to sort on text only - Case insensitive
9024      * @param {Mixed} s The value being converted
9025      * @return {String} The comparison value
9026      */
9027     asUCText : function(s){
9028         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9029     },
9030     
9031     /**
9032      * Case insensitive string
9033      * @param {Mixed} s The value being converted
9034      * @return {String} The comparison value
9035      */
9036     asUCString : function(s) {
9037         return String(s).toUpperCase();
9038     },
9039     
9040     /**
9041      * Date sorting
9042      * @param {Mixed} s The value being converted
9043      * @return {Number} The comparison value
9044      */
9045     asDate : function(s) {
9046         if(!s){
9047             return 0;
9048         }
9049         if(s instanceof Date){
9050             return s.getTime();
9051         }
9052         return Date.parse(String(s));
9053     },
9054     
9055     /**
9056      * Float sorting
9057      * @param {Mixed} s The value being converted
9058      * @return {Float} The comparison value
9059      */
9060     asFloat : function(s) {
9061         var val = parseFloat(String(s).replace(/,/g, ""));
9062         if(isNaN(val)) val = 0;
9063         return val;
9064     },
9065     
9066     /**
9067      * Integer sorting
9068      * @param {Mixed} s The value being converted
9069      * @return {Number} The comparison value
9070      */
9071     asInt : function(s) {
9072         var val = parseInt(String(s).replace(/,/g, ""));
9073         if(isNaN(val)) val = 0;
9074         return val;
9075     }
9076 };/*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088 * @class Roo.data.Record
9089  * Instances of this class encapsulate both record <em>definition</em> information, and record
9090  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9091  * to access Records cached in an {@link Roo.data.Store} object.<br>
9092  * <p>
9093  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9094  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9095  * objects.<br>
9096  * <p>
9097  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9098  * @constructor
9099  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9100  * {@link #create}. The parameters are the same.
9101  * @param {Array} data An associative Array of data values keyed by the field name.
9102  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9103  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9104  * not specified an integer id is generated.
9105  */
9106 Roo.data.Record = function(data, id){
9107     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9108     this.data = data;
9109 };
9110
9111 /**
9112  * Generate a constructor for a specific record layout.
9113  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9114  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9115  * Each field definition object may contain the following properties: <ul>
9116  * <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,
9117  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9118  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9119  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9120  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9121  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9122  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9123  * this may be omitted.</p></li>
9124  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9125  * <ul><li>auto (Default, implies no conversion)</li>
9126  * <li>string</li>
9127  * <li>int</li>
9128  * <li>float</li>
9129  * <li>boolean</li>
9130  * <li>date</li></ul></p></li>
9131  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9132  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9133  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9134  * by the Reader into an object that will be stored in the Record. It is passed the
9135  * following parameters:<ul>
9136  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9137  * </ul></p></li>
9138  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9139  * </ul>
9140  * <br>usage:<br><pre><code>
9141 var TopicRecord = Roo.data.Record.create(
9142     {name: 'title', mapping: 'topic_title'},
9143     {name: 'author', mapping: 'username'},
9144     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9145     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9146     {name: 'lastPoster', mapping: 'user2'},
9147     {name: 'excerpt', mapping: 'post_text'}
9148 );
9149
9150 var myNewRecord = new TopicRecord({
9151     title: 'Do my job please',
9152     author: 'noobie',
9153     totalPosts: 1,
9154     lastPost: new Date(),
9155     lastPoster: 'Animal',
9156     excerpt: 'No way dude!'
9157 });
9158 myStore.add(myNewRecord);
9159 </code></pre>
9160  * @method create
9161  * @static
9162  */
9163 Roo.data.Record.create = function(o){
9164     var f = function(){
9165         f.superclass.constructor.apply(this, arguments);
9166     };
9167     Roo.extend(f, Roo.data.Record);
9168     var p = f.prototype;
9169     p.fields = new Roo.util.MixedCollection(false, function(field){
9170         return field.name;
9171     });
9172     for(var i = 0, len = o.length; i < len; i++){
9173         p.fields.add(new Roo.data.Field(o[i]));
9174     }
9175     f.getField = function(name){
9176         return p.fields.get(name);  
9177     };
9178     return f;
9179 };
9180
9181 Roo.data.Record.AUTO_ID = 1000;
9182 Roo.data.Record.EDIT = 'edit';
9183 Roo.data.Record.REJECT = 'reject';
9184 Roo.data.Record.COMMIT = 'commit';
9185
9186 Roo.data.Record.prototype = {
9187     /**
9188      * Readonly flag - true if this record has been modified.
9189      * @type Boolean
9190      */
9191     dirty : false,
9192     editing : false,
9193     error: null,
9194     modified: null,
9195
9196     // private
9197     join : function(store){
9198         this.store = store;
9199     },
9200
9201     /**
9202      * Set the named field to the specified value.
9203      * @param {String} name The name of the field to set.
9204      * @param {Object} value The value to set the field to.
9205      */
9206     set : function(name, value){
9207         if(this.data[name] == value){
9208             return;
9209         }
9210         this.dirty = true;
9211         if(!this.modified){
9212             this.modified = {};
9213         }
9214         if(typeof this.modified[name] == 'undefined'){
9215             this.modified[name] = this.data[name];
9216         }
9217         this.data[name] = value;
9218         if(!this.editing && this.store){
9219             this.store.afterEdit(this);
9220         }       
9221     },
9222
9223     /**
9224      * Get the value of the named field.
9225      * @param {String} name The name of the field to get the value of.
9226      * @return {Object} The value of the field.
9227      */
9228     get : function(name){
9229         return this.data[name]; 
9230     },
9231
9232     // private
9233     beginEdit : function(){
9234         this.editing = true;
9235         this.modified = {}; 
9236     },
9237
9238     // private
9239     cancelEdit : function(){
9240         this.editing = false;
9241         delete this.modified;
9242     },
9243
9244     // private
9245     endEdit : function(){
9246         this.editing = false;
9247         if(this.dirty && this.store){
9248             this.store.afterEdit(this);
9249         }
9250     },
9251
9252     /**
9253      * Usually called by the {@link Roo.data.Store} which owns the Record.
9254      * Rejects all changes made to the Record since either creation, or the last commit operation.
9255      * Modified fields are reverted to their original values.
9256      * <p>
9257      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9258      * of reject operations.
9259      */
9260     reject : function(){
9261         var m = this.modified;
9262         for(var n in m){
9263             if(typeof m[n] != "function"){
9264                 this.data[n] = m[n];
9265             }
9266         }
9267         this.dirty = false;
9268         delete this.modified;
9269         this.editing = false;
9270         if(this.store){
9271             this.store.afterReject(this);
9272         }
9273     },
9274
9275     /**
9276      * Usually called by the {@link Roo.data.Store} which owns the Record.
9277      * Commits all changes made to the Record since either creation, or the last commit operation.
9278      * <p>
9279      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9280      * of commit operations.
9281      */
9282     commit : function(){
9283         this.dirty = false;
9284         delete this.modified;
9285         this.editing = false;
9286         if(this.store){
9287             this.store.afterCommit(this);
9288         }
9289     },
9290
9291     // private
9292     hasError : function(){
9293         return this.error != null;
9294     },
9295
9296     // private
9297     clearError : function(){
9298         this.error = null;
9299     },
9300
9301     /**
9302      * Creates a copy of this record.
9303      * @param {String} id (optional) A new record id if you don't want to use this record's id
9304      * @return {Record}
9305      */
9306     copy : function(newId) {
9307         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9308     }
9309 };/*
9310  * Based on:
9311  * Ext JS Library 1.1.1
9312  * Copyright(c) 2006-2007, Ext JS, LLC.
9313  *
9314  * Originally Released Under LGPL - original licence link has changed is not relivant.
9315  *
9316  * Fork - LGPL
9317  * <script type="text/javascript">
9318  */
9319
9320
9321
9322 /**
9323  * @class Roo.data.Store
9324  * @extends Roo.util.Observable
9325  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9326  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9327  * <p>
9328  * 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
9329  * has no knowledge of the format of the data returned by the Proxy.<br>
9330  * <p>
9331  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9332  * instances from the data object. These records are cached and made available through accessor functions.
9333  * @constructor
9334  * Creates a new Store.
9335  * @param {Object} config A config object containing the objects needed for the Store to access data,
9336  * and read the data into Records.
9337  */
9338 Roo.data.Store = function(config){
9339     this.data = new Roo.util.MixedCollection(false);
9340     this.data.getKey = function(o){
9341         return o.id;
9342     };
9343     this.baseParams = {};
9344     // private
9345     this.paramNames = {
9346         "start" : "start",
9347         "limit" : "limit",
9348         "sort" : "sort",
9349         "dir" : "dir",
9350         "multisort" : "_multisort"
9351     };
9352
9353     if(config && config.data){
9354         this.inlineData = config.data;
9355         delete config.data;
9356     }
9357
9358     Roo.apply(this, config);
9359     
9360     if(this.reader){ // reader passed
9361         this.reader = Roo.factory(this.reader, Roo.data);
9362         this.reader.xmodule = this.xmodule || false;
9363         if(!this.recordType){
9364             this.recordType = this.reader.recordType;
9365         }
9366         if(this.reader.onMetaChange){
9367             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9368         }
9369     }
9370
9371     if(this.recordType){
9372         this.fields = this.recordType.prototype.fields;
9373     }
9374     this.modified = [];
9375
9376     this.addEvents({
9377         /**
9378          * @event datachanged
9379          * Fires when the data cache has changed, and a widget which is using this Store
9380          * as a Record cache should refresh its view.
9381          * @param {Store} this
9382          */
9383         datachanged : true,
9384         /**
9385          * @event metachange
9386          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9387          * @param {Store} this
9388          * @param {Object} meta The JSON metadata
9389          */
9390         metachange : true,
9391         /**
9392          * @event add
9393          * Fires when Records have been added to the Store
9394          * @param {Store} this
9395          * @param {Roo.data.Record[]} records The array of Records added
9396          * @param {Number} index The index at which the record(s) were added
9397          */
9398         add : true,
9399         /**
9400          * @event remove
9401          * Fires when a Record has been removed from the Store
9402          * @param {Store} this
9403          * @param {Roo.data.Record} record The Record that was removed
9404          * @param {Number} index The index at which the record was removed
9405          */
9406         remove : true,
9407         /**
9408          * @event update
9409          * Fires when a Record has been updated
9410          * @param {Store} this
9411          * @param {Roo.data.Record} record The Record that was updated
9412          * @param {String} operation The update operation being performed.  Value may be one of:
9413          * <pre><code>
9414  Roo.data.Record.EDIT
9415  Roo.data.Record.REJECT
9416  Roo.data.Record.COMMIT
9417          * </code></pre>
9418          */
9419         update : true,
9420         /**
9421          * @event clear
9422          * Fires when the data cache has been cleared.
9423          * @param {Store} this
9424          */
9425         clear : true,
9426         /**
9427          * @event beforeload
9428          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9429          * the load action will be canceled.
9430          * @param {Store} this
9431          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9432          */
9433         beforeload : true,
9434         /**
9435          * @event beforeloadadd
9436          * Fires after a new set of Records has been loaded.
9437          * @param {Store} this
9438          * @param {Roo.data.Record[]} records The Records that were loaded
9439          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9440          */
9441         beforeloadadd : true,
9442         /**
9443          * @event load
9444          * Fires after a new set of Records has been loaded, before they are added to the store.
9445          * @param {Store} this
9446          * @param {Roo.data.Record[]} records The Records that were loaded
9447          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9448          * @params {Object} return from reader
9449          */
9450         load : true,
9451         /**
9452          * @event loadexception
9453          * Fires if an exception occurs in the Proxy during loading.
9454          * Called with the signature of the Proxy's "loadexception" event.
9455          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9456          * 
9457          * @param {Proxy} 
9458          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9459          * @param {Object} load options 
9460          * @param {Object} jsonData from your request (normally this contains the Exception)
9461          */
9462         loadexception : true
9463     });
9464     
9465     if(this.proxy){
9466         this.proxy = Roo.factory(this.proxy, Roo.data);
9467         this.proxy.xmodule = this.xmodule || false;
9468         this.relayEvents(this.proxy,  ["loadexception"]);
9469     }
9470     this.sortToggle = {};
9471     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9472
9473     Roo.data.Store.superclass.constructor.call(this);
9474
9475     if(this.inlineData){
9476         this.loadData(this.inlineData);
9477         delete this.inlineData;
9478     }
9479 };
9480
9481 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9482      /**
9483     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9484     * without a remote query - used by combo/forms at present.
9485     */
9486     
9487     /**
9488     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9489     */
9490     /**
9491     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9492     */
9493     /**
9494     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9495     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9496     */
9497     /**
9498     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9499     * on any HTTP request
9500     */
9501     /**
9502     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9503     */
9504     /**
9505     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9506     */
9507     multiSort: false,
9508     /**
9509     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9510     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9511     */
9512     remoteSort : false,
9513
9514     /**
9515     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9516      * loaded or when a record is removed. (defaults to false).
9517     */
9518     pruneModifiedRecords : false,
9519
9520     // private
9521     lastOptions : null,
9522
9523     /**
9524      * Add Records to the Store and fires the add event.
9525      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9526      */
9527     add : function(records){
9528         records = [].concat(records);
9529         for(var i = 0, len = records.length; i < len; i++){
9530             records[i].join(this);
9531         }
9532         var index = this.data.length;
9533         this.data.addAll(records);
9534         this.fireEvent("add", this, records, index);
9535     },
9536
9537     /**
9538      * Remove a Record from the Store and fires the remove event.
9539      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9540      */
9541     remove : function(record){
9542         var index = this.data.indexOf(record);
9543         this.data.removeAt(index);
9544         if(this.pruneModifiedRecords){
9545             this.modified.remove(record);
9546         }
9547         this.fireEvent("remove", this, record, index);
9548     },
9549
9550     /**
9551      * Remove all Records from the Store and fires the clear event.
9552      */
9553     removeAll : function(){
9554         this.data.clear();
9555         if(this.pruneModifiedRecords){
9556             this.modified = [];
9557         }
9558         this.fireEvent("clear", this);
9559     },
9560
9561     /**
9562      * Inserts Records to the Store at the given index and fires the add event.
9563      * @param {Number} index The start index at which to insert the passed Records.
9564      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9565      */
9566     insert : function(index, records){
9567         records = [].concat(records);
9568         for(var i = 0, len = records.length; i < len; i++){
9569             this.data.insert(index, records[i]);
9570             records[i].join(this);
9571         }
9572         this.fireEvent("add", this, records, index);
9573     },
9574
9575     /**
9576      * Get the index within the cache of the passed Record.
9577      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9578      * @return {Number} The index of the passed Record. Returns -1 if not found.
9579      */
9580     indexOf : function(record){
9581         return this.data.indexOf(record);
9582     },
9583
9584     /**
9585      * Get the index within the cache of the Record with the passed id.
9586      * @param {String} id The id of the Record to find.
9587      * @return {Number} The index of the Record. Returns -1 if not found.
9588      */
9589     indexOfId : function(id){
9590         return this.data.indexOfKey(id);
9591     },
9592
9593     /**
9594      * Get the Record with the specified id.
9595      * @param {String} id The id of the Record to find.
9596      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9597      */
9598     getById : function(id){
9599         return this.data.key(id);
9600     },
9601
9602     /**
9603      * Get the Record at the specified index.
9604      * @param {Number} index The index of the Record to find.
9605      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9606      */
9607     getAt : function(index){
9608         return this.data.itemAt(index);
9609     },
9610
9611     /**
9612      * Returns a range of Records between specified indices.
9613      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9614      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9615      * @return {Roo.data.Record[]} An array of Records
9616      */
9617     getRange : function(start, end){
9618         return this.data.getRange(start, end);
9619     },
9620
9621     // private
9622     storeOptions : function(o){
9623         o = Roo.apply({}, o);
9624         delete o.callback;
9625         delete o.scope;
9626         this.lastOptions = o;
9627     },
9628
9629     /**
9630      * Loads the Record cache from the configured Proxy using the configured Reader.
9631      * <p>
9632      * If using remote paging, then the first load call must specify the <em>start</em>
9633      * and <em>limit</em> properties in the options.params property to establish the initial
9634      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9635      * <p>
9636      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9637      * and this call will return before the new data has been loaded. Perform any post-processing
9638      * in a callback function, or in a "load" event handler.</strong>
9639      * <p>
9640      * @param {Object} options An object containing properties which control loading options:<ul>
9641      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9642      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9643      * passed the following arguments:<ul>
9644      * <li>r : Roo.data.Record[]</li>
9645      * <li>options: Options object from the load call</li>
9646      * <li>success: Boolean success indicator</li></ul></li>
9647      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9648      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9649      * </ul>
9650      */
9651     load : function(options){
9652         options = options || {};
9653         if(this.fireEvent("beforeload", this, options) !== false){
9654             this.storeOptions(options);
9655             var p = Roo.apply(options.params || {}, this.baseParams);
9656             // if meta was not loaded from remote source.. try requesting it.
9657             if (!this.reader.metaFromRemote) {
9658                 p._requestMeta = 1;
9659             }
9660             if(this.sortInfo && this.remoteSort){
9661                 var pn = this.paramNames;
9662                 p[pn["sort"]] = this.sortInfo.field;
9663                 p[pn["dir"]] = this.sortInfo.direction;
9664             }
9665             if (this.multiSort) {
9666                 var pn = this.paramNames;
9667                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9668             }
9669             
9670             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9671         }
9672     },
9673
9674     /**
9675      * Reloads the Record cache from the configured Proxy using the configured Reader and
9676      * the options from the last load operation performed.
9677      * @param {Object} options (optional) An object containing properties which may override the options
9678      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9679      * the most recently used options are reused).
9680      */
9681     reload : function(options){
9682         this.load(Roo.applyIf(options||{}, this.lastOptions));
9683     },
9684
9685     // private
9686     // Called as a callback by the Reader during a load operation.
9687     loadRecords : function(o, options, success){
9688         if(!o || success === false){
9689             if(success !== false){
9690                 this.fireEvent("load", this, [], options, o);
9691             }
9692             if(options.callback){
9693                 options.callback.call(options.scope || this, [], options, false);
9694             }
9695             return;
9696         }
9697         // if data returned failure - throw an exception.
9698         if (o.success === false) {
9699             // show a message if no listener is registered.
9700             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9701                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9702             }
9703             // loadmask wil be hooked into this..
9704             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9705             return;
9706         }
9707         var r = o.records, t = o.totalRecords || r.length;
9708         
9709         this.fireEvent("beforeloadadd", this, r, options, o);
9710         
9711         if(!options || options.add !== true){
9712             if(this.pruneModifiedRecords){
9713                 this.modified = [];
9714             }
9715             for(var i = 0, len = r.length; i < len; i++){
9716                 r[i].join(this);
9717             }
9718             if(this.snapshot){
9719                 this.data = this.snapshot;
9720                 delete this.snapshot;
9721             }
9722             this.data.clear();
9723             this.data.addAll(r);
9724             this.totalLength = t;
9725             this.applySort();
9726             this.fireEvent("datachanged", this);
9727         }else{
9728             this.totalLength = Math.max(t, this.data.length+r.length);
9729             this.add(r);
9730         }
9731         this.fireEvent("load", this, r, options, o);
9732         if(options.callback){
9733             options.callback.call(options.scope || this, r, options, true);
9734         }
9735     },
9736
9737
9738     /**
9739      * Loads data from a passed data block. A Reader which understands the format of the data
9740      * must have been configured in the constructor.
9741      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9742      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9743      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9744      */
9745     loadData : function(o, append){
9746         var r = this.reader.readRecords(o);
9747         this.loadRecords(r, {add: append}, true);
9748     },
9749
9750     /**
9751      * Gets the number of cached records.
9752      * <p>
9753      * <em>If using paging, this may not be the total size of the dataset. If the data object
9754      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9755      * the data set size</em>
9756      */
9757     getCount : function(){
9758         return this.data.length || 0;
9759     },
9760
9761     /**
9762      * Gets the total number of records in the dataset as returned by the server.
9763      * <p>
9764      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9765      * the dataset size</em>
9766      */
9767     getTotalCount : function(){
9768         return this.totalLength || 0;
9769     },
9770
9771     /**
9772      * Returns the sort state of the Store as an object with two properties:
9773      * <pre><code>
9774  field {String} The name of the field by which the Records are sorted
9775  direction {String} The sort order, "ASC" or "DESC"
9776      * </code></pre>
9777      */
9778     getSortState : function(){
9779         return this.sortInfo;
9780     },
9781
9782     // private
9783     applySort : function(){
9784         if(this.sortInfo && !this.remoteSort){
9785             var s = this.sortInfo, f = s.field;
9786             var st = this.fields.get(f).sortType;
9787             var fn = function(r1, r2){
9788                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9789                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9790             };
9791             this.data.sort(s.direction, fn);
9792             if(this.snapshot && this.snapshot != this.data){
9793                 this.snapshot.sort(s.direction, fn);
9794             }
9795         }
9796     },
9797
9798     /**
9799      * Sets the default sort column and order to be used by the next load operation.
9800      * @param {String} fieldName The name of the field to sort by.
9801      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9802      */
9803     setDefaultSort : function(field, dir){
9804         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9805     },
9806
9807     /**
9808      * Sort the Records.
9809      * If remote sorting is used, the sort is performed on the server, and the cache is
9810      * reloaded. If local sorting is used, the cache is sorted internally.
9811      * @param {String} fieldName The name of the field to sort by.
9812      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9813      */
9814     sort : function(fieldName, dir){
9815         var f = this.fields.get(fieldName);
9816         if(!dir){
9817             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9818             
9819             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9820                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9821             }else{
9822                 dir = f.sortDir;
9823             }
9824         }
9825         this.sortToggle[f.name] = dir;
9826         this.sortInfo = {field: f.name, direction: dir};
9827         if(!this.remoteSort){
9828             this.applySort();
9829             this.fireEvent("datachanged", this);
9830         }else{
9831             this.load(this.lastOptions);
9832         }
9833     },
9834
9835     /**
9836      * Calls the specified function for each of the Records in the cache.
9837      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9838      * Returning <em>false</em> aborts and exits the iteration.
9839      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9840      */
9841     each : function(fn, scope){
9842         this.data.each(fn, scope);
9843     },
9844
9845     /**
9846      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9847      * (e.g., during paging).
9848      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9849      */
9850     getModifiedRecords : function(){
9851         return this.modified;
9852     },
9853
9854     // private
9855     createFilterFn : function(property, value, anyMatch){
9856         if(!value.exec){ // not a regex
9857             value = String(value);
9858             if(value.length == 0){
9859                 return false;
9860             }
9861             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9862         }
9863         return function(r){
9864             return value.test(r.data[property]);
9865         };
9866     },
9867
9868     /**
9869      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9870      * @param {String} property A field on your records
9871      * @param {Number} start The record index to start at (defaults to 0)
9872      * @param {Number} end The last record index to include (defaults to length - 1)
9873      * @return {Number} The sum
9874      */
9875     sum : function(property, start, end){
9876         var rs = this.data.items, v = 0;
9877         start = start || 0;
9878         end = (end || end === 0) ? end : rs.length-1;
9879
9880         for(var i = start; i <= end; i++){
9881             v += (rs[i].data[property] || 0);
9882         }
9883         return v;
9884     },
9885
9886     /**
9887      * Filter the records by a specified property.
9888      * @param {String} field A field on your records
9889      * @param {String/RegExp} value Either a string that the field
9890      * should start with or a RegExp to test against the field
9891      * @param {Boolean} anyMatch True to match any part not just the beginning
9892      */
9893     filter : function(property, value, anyMatch){
9894         var fn = this.createFilterFn(property, value, anyMatch);
9895         return fn ? this.filterBy(fn) : this.clearFilter();
9896     },
9897
9898     /**
9899      * Filter by a function. The specified function will be called with each
9900      * record in this data source. If the function returns true the record is included,
9901      * otherwise it is filtered.
9902      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9903      * @param {Object} scope (optional) The scope of the function (defaults to this)
9904      */
9905     filterBy : function(fn, scope){
9906         this.snapshot = this.snapshot || this.data;
9907         this.data = this.queryBy(fn, scope||this);
9908         this.fireEvent("datachanged", this);
9909     },
9910
9911     /**
9912      * Query the records by a specified property.
9913      * @param {String} field A field on your records
9914      * @param {String/RegExp} value Either a string that the field
9915      * should start with or a RegExp to test against the field
9916      * @param {Boolean} anyMatch True to match any part not just the beginning
9917      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9918      */
9919     query : function(property, value, anyMatch){
9920         var fn = this.createFilterFn(property, value, anyMatch);
9921         return fn ? this.queryBy(fn) : this.data.clone();
9922     },
9923
9924     /**
9925      * Query by a function. The specified function will be called with each
9926      * record in this data source. If the function returns true the record is included
9927      * in the results.
9928      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9929      * @param {Object} scope (optional) The scope of the function (defaults to this)
9930       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9931      **/
9932     queryBy : function(fn, scope){
9933         var data = this.snapshot || this.data;
9934         return data.filterBy(fn, scope||this);
9935     },
9936
9937     /**
9938      * Collects unique values for a particular dataIndex from this store.
9939      * @param {String} dataIndex The property to collect
9940      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9941      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9942      * @return {Array} An array of the unique values
9943      **/
9944     collect : function(dataIndex, allowNull, bypassFilter){
9945         var d = (bypassFilter === true && this.snapshot) ?
9946                 this.snapshot.items : this.data.items;
9947         var v, sv, r = [], l = {};
9948         for(var i = 0, len = d.length; i < len; i++){
9949             v = d[i].data[dataIndex];
9950             sv = String(v);
9951             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9952                 l[sv] = true;
9953                 r[r.length] = v;
9954             }
9955         }
9956         return r;
9957     },
9958
9959     /**
9960      * Revert to a view of the Record cache with no filtering applied.
9961      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9962      */
9963     clearFilter : function(suppressEvent){
9964         if(this.snapshot && this.snapshot != this.data){
9965             this.data = this.snapshot;
9966             delete this.snapshot;
9967             if(suppressEvent !== true){
9968                 this.fireEvent("datachanged", this);
9969             }
9970         }
9971     },
9972
9973     // private
9974     afterEdit : function(record){
9975         if(this.modified.indexOf(record) == -1){
9976             this.modified.push(record);
9977         }
9978         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9979     },
9980     
9981     // private
9982     afterReject : function(record){
9983         this.modified.remove(record);
9984         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9985     },
9986
9987     // private
9988     afterCommit : function(record){
9989         this.modified.remove(record);
9990         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9991     },
9992
9993     /**
9994      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9995      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9996      */
9997     commitChanges : function(){
9998         var m = this.modified.slice(0);
9999         this.modified = [];
10000         for(var i = 0, len = m.length; i < len; i++){
10001             m[i].commit();
10002         }
10003     },
10004
10005     /**
10006      * Cancel outstanding changes on all changed records.
10007      */
10008     rejectChanges : function(){
10009         var m = this.modified.slice(0);
10010         this.modified = [];
10011         for(var i = 0, len = m.length; i < len; i++){
10012             m[i].reject();
10013         }
10014     },
10015
10016     onMetaChange : function(meta, rtype, o){
10017         this.recordType = rtype;
10018         this.fields = rtype.prototype.fields;
10019         delete this.snapshot;
10020         this.sortInfo = meta.sortInfo || this.sortInfo;
10021         this.modified = [];
10022         this.fireEvent('metachange', this, this.reader.meta);
10023     },
10024     
10025     moveIndex : function(data, type)
10026     {
10027         var index = this.indexOf(data);
10028         
10029         var newIndex = index + type;
10030         
10031         this.remove(data);
10032         
10033         this.insert(newIndex, data);
10034         
10035     }
10036 });/*
10037  * Based on:
10038  * Ext JS Library 1.1.1
10039  * Copyright(c) 2006-2007, Ext JS, LLC.
10040  *
10041  * Originally Released Under LGPL - original licence link has changed is not relivant.
10042  *
10043  * Fork - LGPL
10044  * <script type="text/javascript">
10045  */
10046
10047 /**
10048  * @class Roo.data.SimpleStore
10049  * @extends Roo.data.Store
10050  * Small helper class to make creating Stores from Array data easier.
10051  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10052  * @cfg {Array} fields An array of field definition objects, or field name strings.
10053  * @cfg {Array} data The multi-dimensional array of data
10054  * @constructor
10055  * @param {Object} config
10056  */
10057 Roo.data.SimpleStore = function(config){
10058     Roo.data.SimpleStore.superclass.constructor.call(this, {
10059         isLocal : true,
10060         reader: new Roo.data.ArrayReader({
10061                 id: config.id
10062             },
10063             Roo.data.Record.create(config.fields)
10064         ),
10065         proxy : new Roo.data.MemoryProxy(config.data)
10066     });
10067     this.load();
10068 };
10069 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10070  * Based on:
10071  * Ext JS Library 1.1.1
10072  * Copyright(c) 2006-2007, Ext JS, LLC.
10073  *
10074  * Originally Released Under LGPL - original licence link has changed is not relivant.
10075  *
10076  * Fork - LGPL
10077  * <script type="text/javascript">
10078  */
10079
10080 /**
10081 /**
10082  * @extends Roo.data.Store
10083  * @class Roo.data.JsonStore
10084  * Small helper class to make creating Stores for JSON data easier. <br/>
10085 <pre><code>
10086 var store = new Roo.data.JsonStore({
10087     url: 'get-images.php',
10088     root: 'images',
10089     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10090 });
10091 </code></pre>
10092  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10093  * JsonReader and HttpProxy (unless inline data is provided).</b>
10094  * @cfg {Array} fields An array of field definition objects, or field name strings.
10095  * @constructor
10096  * @param {Object} config
10097  */
10098 Roo.data.JsonStore = function(c){
10099     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10100         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10101         reader: new Roo.data.JsonReader(c, c.fields)
10102     }));
10103 };
10104 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10105  * Based on:
10106  * Ext JS Library 1.1.1
10107  * Copyright(c) 2006-2007, Ext JS, LLC.
10108  *
10109  * Originally Released Under LGPL - original licence link has changed is not relivant.
10110  *
10111  * Fork - LGPL
10112  * <script type="text/javascript">
10113  */
10114
10115  
10116 Roo.data.Field = function(config){
10117     if(typeof config == "string"){
10118         config = {name: config};
10119     }
10120     Roo.apply(this, config);
10121     
10122     if(!this.type){
10123         this.type = "auto";
10124     }
10125     
10126     var st = Roo.data.SortTypes;
10127     // named sortTypes are supported, here we look them up
10128     if(typeof this.sortType == "string"){
10129         this.sortType = st[this.sortType];
10130     }
10131     
10132     // set default sortType for strings and dates
10133     if(!this.sortType){
10134         switch(this.type){
10135             case "string":
10136                 this.sortType = st.asUCString;
10137                 break;
10138             case "date":
10139                 this.sortType = st.asDate;
10140                 break;
10141             default:
10142                 this.sortType = st.none;
10143         }
10144     }
10145
10146     // define once
10147     var stripRe = /[\$,%]/g;
10148
10149     // prebuilt conversion function for this field, instead of
10150     // switching every time we're reading a value
10151     if(!this.convert){
10152         var cv, dateFormat = this.dateFormat;
10153         switch(this.type){
10154             case "":
10155             case "auto":
10156             case undefined:
10157                 cv = function(v){ return v; };
10158                 break;
10159             case "string":
10160                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10161                 break;
10162             case "int":
10163                 cv = function(v){
10164                     return v !== undefined && v !== null && v !== '' ?
10165                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10166                     };
10167                 break;
10168             case "float":
10169                 cv = function(v){
10170                     return v !== undefined && v !== null && v !== '' ?
10171                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10172                     };
10173                 break;
10174             case "bool":
10175             case "boolean":
10176                 cv = function(v){ return v === true || v === "true" || v == 1; };
10177                 break;
10178             case "date":
10179                 cv = function(v){
10180                     if(!v){
10181                         return '';
10182                     }
10183                     if(v instanceof Date){
10184                         return v;
10185                     }
10186                     if(dateFormat){
10187                         if(dateFormat == "timestamp"){
10188                             return new Date(v*1000);
10189                         }
10190                         return Date.parseDate(v, dateFormat);
10191                     }
10192                     var parsed = Date.parse(v);
10193                     return parsed ? new Date(parsed) : null;
10194                 };
10195              break;
10196             
10197         }
10198         this.convert = cv;
10199     }
10200 };
10201
10202 Roo.data.Field.prototype = {
10203     dateFormat: null,
10204     defaultValue: "",
10205     mapping: null,
10206     sortType : null,
10207     sortDir : "ASC"
10208 };/*
10209  * Based on:
10210  * Ext JS Library 1.1.1
10211  * Copyright(c) 2006-2007, Ext JS, LLC.
10212  *
10213  * Originally Released Under LGPL - original licence link has changed is not relivant.
10214  *
10215  * Fork - LGPL
10216  * <script type="text/javascript">
10217  */
10218  
10219 // Base class for reading structured data from a data source.  This class is intended to be
10220 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10221
10222 /**
10223  * @class Roo.data.DataReader
10224  * Base class for reading structured data from a data source.  This class is intended to be
10225  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10226  */
10227
10228 Roo.data.DataReader = function(meta, recordType){
10229     
10230     this.meta = meta;
10231     
10232     this.recordType = recordType instanceof Array ? 
10233         Roo.data.Record.create(recordType) : recordType;
10234 };
10235
10236 Roo.data.DataReader.prototype = {
10237      /**
10238      * Create an empty record
10239      * @param {Object} data (optional) - overlay some values
10240      * @return {Roo.data.Record} record created.
10241      */
10242     newRow :  function(d) {
10243         var da =  {};
10244         this.recordType.prototype.fields.each(function(c) {
10245             switch( c.type) {
10246                 case 'int' : da[c.name] = 0; break;
10247                 case 'date' : da[c.name] = new Date(); break;
10248                 case 'float' : da[c.name] = 0.0; break;
10249                 case 'boolean' : da[c.name] = false; break;
10250                 default : da[c.name] = ""; break;
10251             }
10252             
10253         });
10254         return new this.recordType(Roo.apply(da, d));
10255     }
10256     
10257 };/*
10258  * Based on:
10259  * Ext JS Library 1.1.1
10260  * Copyright(c) 2006-2007, Ext JS, LLC.
10261  *
10262  * Originally Released Under LGPL - original licence link has changed is not relivant.
10263  *
10264  * Fork - LGPL
10265  * <script type="text/javascript">
10266  */
10267
10268 /**
10269  * @class Roo.data.DataProxy
10270  * @extends Roo.data.Observable
10271  * This class is an abstract base class for implementations which provide retrieval of
10272  * unformatted data objects.<br>
10273  * <p>
10274  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10275  * (of the appropriate type which knows how to parse the data object) to provide a block of
10276  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10277  * <p>
10278  * Custom implementations must implement the load method as described in
10279  * {@link Roo.data.HttpProxy#load}.
10280  */
10281 Roo.data.DataProxy = function(){
10282     this.addEvents({
10283         /**
10284          * @event beforeload
10285          * Fires before a network request is made to retrieve a data object.
10286          * @param {Object} This DataProxy object.
10287          * @param {Object} params The params parameter to the load function.
10288          */
10289         beforeload : true,
10290         /**
10291          * @event load
10292          * Fires before the load method's callback is called.
10293          * @param {Object} This DataProxy object.
10294          * @param {Object} o The data object.
10295          * @param {Object} arg The callback argument object passed to the load function.
10296          */
10297         load : true,
10298         /**
10299          * @event loadexception
10300          * Fires if an Exception occurs during data retrieval.
10301          * @param {Object} This DataProxy object.
10302          * @param {Object} o The data object.
10303          * @param {Object} arg The callback argument object passed to the load function.
10304          * @param {Object} e The Exception.
10305          */
10306         loadexception : true
10307     });
10308     Roo.data.DataProxy.superclass.constructor.call(this);
10309 };
10310
10311 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10312
10313     /**
10314      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10315      */
10316 /*
10317  * Based on:
10318  * Ext JS Library 1.1.1
10319  * Copyright(c) 2006-2007, Ext JS, LLC.
10320  *
10321  * Originally Released Under LGPL - original licence link has changed is not relivant.
10322  *
10323  * Fork - LGPL
10324  * <script type="text/javascript">
10325  */
10326 /**
10327  * @class Roo.data.MemoryProxy
10328  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10329  * to the Reader when its load method is called.
10330  * @constructor
10331  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10332  */
10333 Roo.data.MemoryProxy = function(data){
10334     if (data.data) {
10335         data = data.data;
10336     }
10337     Roo.data.MemoryProxy.superclass.constructor.call(this);
10338     this.data = data;
10339 };
10340
10341 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10342     /**
10343      * Load data from the requested source (in this case an in-memory
10344      * data object passed to the constructor), read the data object into
10345      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10346      * process that block using the passed callback.
10347      * @param {Object} params This parameter is not used by the MemoryProxy class.
10348      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10349      * object into a block of Roo.data.Records.
10350      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10351      * The function must be passed <ul>
10352      * <li>The Record block object</li>
10353      * <li>The "arg" argument from the load function</li>
10354      * <li>A boolean success indicator</li>
10355      * </ul>
10356      * @param {Object} scope The scope in which to call the callback
10357      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10358      */
10359     load : function(params, reader, callback, scope, arg){
10360         params = params || {};
10361         var result;
10362         try {
10363             result = reader.readRecords(this.data);
10364         }catch(e){
10365             this.fireEvent("loadexception", this, arg, null, e);
10366             callback.call(scope, null, arg, false);
10367             return;
10368         }
10369         callback.call(scope, result, arg, true);
10370     },
10371     
10372     // private
10373     update : function(params, records){
10374         
10375     }
10376 });/*
10377  * Based on:
10378  * Ext JS Library 1.1.1
10379  * Copyright(c) 2006-2007, Ext JS, LLC.
10380  *
10381  * Originally Released Under LGPL - original licence link has changed is not relivant.
10382  *
10383  * Fork - LGPL
10384  * <script type="text/javascript">
10385  */
10386 /**
10387  * @class Roo.data.HttpProxy
10388  * @extends Roo.data.DataProxy
10389  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10390  * configured to reference a certain URL.<br><br>
10391  * <p>
10392  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10393  * from which the running page was served.<br><br>
10394  * <p>
10395  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10396  * <p>
10397  * Be aware that to enable the browser to parse an XML document, the server must set
10398  * the Content-Type header in the HTTP response to "text/xml".
10399  * @constructor
10400  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10401  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10402  * will be used to make the request.
10403  */
10404 Roo.data.HttpProxy = function(conn){
10405     Roo.data.HttpProxy.superclass.constructor.call(this);
10406     // is conn a conn config or a real conn?
10407     this.conn = conn;
10408     this.useAjax = !conn || !conn.events;
10409   
10410 };
10411
10412 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10413     // thse are take from connection...
10414     
10415     /**
10416      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10417      */
10418     /**
10419      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10420      * extra parameters to each request made by this object. (defaults to undefined)
10421      */
10422     /**
10423      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10424      *  to each request made by this object. (defaults to undefined)
10425      */
10426     /**
10427      * @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)
10428      */
10429     /**
10430      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10431      */
10432      /**
10433      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10434      * @type Boolean
10435      */
10436   
10437
10438     /**
10439      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10440      * @type Boolean
10441      */
10442     /**
10443      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10444      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10445      * a finer-grained basis than the DataProxy events.
10446      */
10447     getConnection : function(){
10448         return this.useAjax ? Roo.Ajax : this.conn;
10449     },
10450
10451     /**
10452      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10453      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10454      * process that block using the passed callback.
10455      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10456      * for the request to the remote server.
10457      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10458      * object into a block of Roo.data.Records.
10459      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10460      * The function must be passed <ul>
10461      * <li>The Record block object</li>
10462      * <li>The "arg" argument from the load function</li>
10463      * <li>A boolean success indicator</li>
10464      * </ul>
10465      * @param {Object} scope The scope in which to call the callback
10466      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10467      */
10468     load : function(params, reader, callback, scope, arg){
10469         if(this.fireEvent("beforeload", this, params) !== false){
10470             var  o = {
10471                 params : params || {},
10472                 request: {
10473                     callback : callback,
10474                     scope : scope,
10475                     arg : arg
10476                 },
10477                 reader: reader,
10478                 callback : this.loadResponse,
10479                 scope: this
10480             };
10481             if(this.useAjax){
10482                 Roo.applyIf(o, this.conn);
10483                 if(this.activeRequest){
10484                     Roo.Ajax.abort(this.activeRequest);
10485                 }
10486                 this.activeRequest = Roo.Ajax.request(o);
10487             }else{
10488                 this.conn.request(o);
10489             }
10490         }else{
10491             callback.call(scope||this, null, arg, false);
10492         }
10493     },
10494
10495     // private
10496     loadResponse : function(o, success, response){
10497         delete this.activeRequest;
10498         if(!success){
10499             this.fireEvent("loadexception", this, o, response);
10500             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10501             return;
10502         }
10503         var result;
10504         try {
10505             result = o.reader.read(response);
10506         }catch(e){
10507             this.fireEvent("loadexception", this, o, response, e);
10508             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10509             return;
10510         }
10511         
10512         this.fireEvent("load", this, o, o.request.arg);
10513         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10514     },
10515
10516     // private
10517     update : function(dataSet){
10518
10519     },
10520
10521     // private
10522     updateResponse : function(dataSet){
10523
10524     }
10525 });/*
10526  * Based on:
10527  * Ext JS Library 1.1.1
10528  * Copyright(c) 2006-2007, Ext JS, LLC.
10529  *
10530  * Originally Released Under LGPL - original licence link has changed is not relivant.
10531  *
10532  * Fork - LGPL
10533  * <script type="text/javascript">
10534  */
10535
10536 /**
10537  * @class Roo.data.ScriptTagProxy
10538  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10539  * other than the originating domain of the running page.<br><br>
10540  * <p>
10541  * <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
10542  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10543  * <p>
10544  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10545  * source code that is used as the source inside a &lt;script> tag.<br><br>
10546  * <p>
10547  * In order for the browser to process the returned data, the server must wrap the data object
10548  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10549  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10550  * depending on whether the callback name was passed:
10551  * <p>
10552  * <pre><code>
10553 boolean scriptTag = false;
10554 String cb = request.getParameter("callback");
10555 if (cb != null) {
10556     scriptTag = true;
10557     response.setContentType("text/javascript");
10558 } else {
10559     response.setContentType("application/x-json");
10560 }
10561 Writer out = response.getWriter();
10562 if (scriptTag) {
10563     out.write(cb + "(");
10564 }
10565 out.print(dataBlock.toJsonString());
10566 if (scriptTag) {
10567     out.write(");");
10568 }
10569 </pre></code>
10570  *
10571  * @constructor
10572  * @param {Object} config A configuration object.
10573  */
10574 Roo.data.ScriptTagProxy = function(config){
10575     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10576     Roo.apply(this, config);
10577     this.head = document.getElementsByTagName("head")[0];
10578 };
10579
10580 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10581
10582 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10583     /**
10584      * @cfg {String} url The URL from which to request the data object.
10585      */
10586     /**
10587      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10588      */
10589     timeout : 30000,
10590     /**
10591      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10592      * the server the name of the callback function set up by the load call to process the returned data object.
10593      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10594      * javascript output which calls this named function passing the data object as its only parameter.
10595      */
10596     callbackParam : "callback",
10597     /**
10598      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10599      * name to the request.
10600      */
10601     nocache : true,
10602
10603     /**
10604      * Load data from the configured URL, read the data object into
10605      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10606      * process that block using the passed callback.
10607      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10608      * for the request to the remote server.
10609      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10610      * object into a block of Roo.data.Records.
10611      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10612      * The function must be passed <ul>
10613      * <li>The Record block object</li>
10614      * <li>The "arg" argument from the load function</li>
10615      * <li>A boolean success indicator</li>
10616      * </ul>
10617      * @param {Object} scope The scope in which to call the callback
10618      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10619      */
10620     load : function(params, reader, callback, scope, arg){
10621         if(this.fireEvent("beforeload", this, params) !== false){
10622
10623             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10624
10625             var url = this.url;
10626             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10627             if(this.nocache){
10628                 url += "&_dc=" + (new Date().getTime());
10629             }
10630             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10631             var trans = {
10632                 id : transId,
10633                 cb : "stcCallback"+transId,
10634                 scriptId : "stcScript"+transId,
10635                 params : params,
10636                 arg : arg,
10637                 url : url,
10638                 callback : callback,
10639                 scope : scope,
10640                 reader : reader
10641             };
10642             var conn = this;
10643
10644             window[trans.cb] = function(o){
10645                 conn.handleResponse(o, trans);
10646             };
10647
10648             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10649
10650             if(this.autoAbort !== false){
10651                 this.abort();
10652             }
10653
10654             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10655
10656             var script = document.createElement("script");
10657             script.setAttribute("src", url);
10658             script.setAttribute("type", "text/javascript");
10659             script.setAttribute("id", trans.scriptId);
10660             this.head.appendChild(script);
10661
10662             this.trans = trans;
10663         }else{
10664             callback.call(scope||this, null, arg, false);
10665         }
10666     },
10667
10668     // private
10669     isLoading : function(){
10670         return this.trans ? true : false;
10671     },
10672
10673     /**
10674      * Abort the current server request.
10675      */
10676     abort : function(){
10677         if(this.isLoading()){
10678             this.destroyTrans(this.trans);
10679         }
10680     },
10681
10682     // private
10683     destroyTrans : function(trans, isLoaded){
10684         this.head.removeChild(document.getElementById(trans.scriptId));
10685         clearTimeout(trans.timeoutId);
10686         if(isLoaded){
10687             window[trans.cb] = undefined;
10688             try{
10689                 delete window[trans.cb];
10690             }catch(e){}
10691         }else{
10692             // if hasn't been loaded, wait for load to remove it to prevent script error
10693             window[trans.cb] = function(){
10694                 window[trans.cb] = undefined;
10695                 try{
10696                     delete window[trans.cb];
10697                 }catch(e){}
10698             };
10699         }
10700     },
10701
10702     // private
10703     handleResponse : function(o, trans){
10704         this.trans = false;
10705         this.destroyTrans(trans, true);
10706         var result;
10707         try {
10708             result = trans.reader.readRecords(o);
10709         }catch(e){
10710             this.fireEvent("loadexception", this, o, trans.arg, e);
10711             trans.callback.call(trans.scope||window, null, trans.arg, false);
10712             return;
10713         }
10714         this.fireEvent("load", this, o, trans.arg);
10715         trans.callback.call(trans.scope||window, result, trans.arg, true);
10716     },
10717
10718     // private
10719     handleFailure : function(trans){
10720         this.trans = false;
10721         this.destroyTrans(trans, false);
10722         this.fireEvent("loadexception", this, null, trans.arg);
10723         trans.callback.call(trans.scope||window, null, trans.arg, false);
10724     }
10725 });/*
10726  * Based on:
10727  * Ext JS Library 1.1.1
10728  * Copyright(c) 2006-2007, Ext JS, LLC.
10729  *
10730  * Originally Released Under LGPL - original licence link has changed is not relivant.
10731  *
10732  * Fork - LGPL
10733  * <script type="text/javascript">
10734  */
10735
10736 /**
10737  * @class Roo.data.JsonReader
10738  * @extends Roo.data.DataReader
10739  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10740  * based on mappings in a provided Roo.data.Record constructor.
10741  * 
10742  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10743  * in the reply previously. 
10744  * 
10745  * <p>
10746  * Example code:
10747  * <pre><code>
10748 var RecordDef = Roo.data.Record.create([
10749     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10750     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10751 ]);
10752 var myReader = new Roo.data.JsonReader({
10753     totalProperty: "results",    // The property which contains the total dataset size (optional)
10754     root: "rows",                // The property which contains an Array of row objects
10755     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10756 }, RecordDef);
10757 </code></pre>
10758  * <p>
10759  * This would consume a JSON file like this:
10760  * <pre><code>
10761 { 'results': 2, 'rows': [
10762     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10763     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10764 }
10765 </code></pre>
10766  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10767  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10768  * paged from the remote server.
10769  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10770  * @cfg {String} root name of the property which contains the Array of row objects.
10771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10772  * @cfg {Array} fields Array of field definition objects
10773  * @constructor
10774  * Create a new JsonReader
10775  * @param {Object} meta Metadata configuration options
10776  * @param {Object} recordType Either an Array of field definition objects,
10777  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10778  */
10779 Roo.data.JsonReader = function(meta, recordType){
10780     
10781     meta = meta || {};
10782     // set some defaults:
10783     Roo.applyIf(meta, {
10784         totalProperty: 'total',
10785         successProperty : 'success',
10786         root : 'data',
10787         id : 'id'
10788     });
10789     
10790     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10791 };
10792 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10793     
10794     /**
10795      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10796      * Used by Store query builder to append _requestMeta to params.
10797      * 
10798      */
10799     metaFromRemote : false,
10800     /**
10801      * This method is only used by a DataProxy which has retrieved data from a remote server.
10802      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10803      * @return {Object} data A data block which is used by an Roo.data.Store object as
10804      * a cache of Roo.data.Records.
10805      */
10806     read : function(response){
10807         var json = response.responseText;
10808        
10809         var o = /* eval:var:o */ eval("("+json+")");
10810         if(!o) {
10811             throw {message: "JsonReader.read: Json object not found"};
10812         }
10813         
10814         if(o.metaData){
10815             
10816             delete this.ef;
10817             this.metaFromRemote = true;
10818             this.meta = o.metaData;
10819             this.recordType = Roo.data.Record.create(o.metaData.fields);
10820             this.onMetaChange(this.meta, this.recordType, o);
10821         }
10822         return this.readRecords(o);
10823     },
10824
10825     // private function a store will implement
10826     onMetaChange : function(meta, recordType, o){
10827
10828     },
10829
10830     /**
10831          * @ignore
10832          */
10833     simpleAccess: function(obj, subsc) {
10834         return obj[subsc];
10835     },
10836
10837         /**
10838          * @ignore
10839          */
10840     getJsonAccessor: function(){
10841         var re = /[\[\.]/;
10842         return function(expr) {
10843             try {
10844                 return(re.test(expr))
10845                     ? new Function("obj", "return obj." + expr)
10846                     : function(obj){
10847                         return obj[expr];
10848                     };
10849             } catch(e){}
10850             return Roo.emptyFn;
10851         };
10852     }(),
10853
10854     /**
10855      * Create a data block containing Roo.data.Records from an XML document.
10856      * @param {Object} o An object which contains an Array of row objects in the property specified
10857      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10858      * which contains the total size of the dataset.
10859      * @return {Object} data A data block which is used by an Roo.data.Store object as
10860      * a cache of Roo.data.Records.
10861      */
10862     readRecords : function(o){
10863         /**
10864          * After any data loads, the raw JSON data is available for further custom processing.
10865          * @type Object
10866          */
10867         this.o = o;
10868         var s = this.meta, Record = this.recordType,
10869             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10870
10871 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10872         if (!this.ef) {
10873             if(s.totalProperty) {
10874                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10875                 }
10876                 if(s.successProperty) {
10877                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10878                 }
10879                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10880                 if (s.id) {
10881                         var g = this.getJsonAccessor(s.id);
10882                         this.getId = function(rec) {
10883                                 var r = g(rec);  
10884                                 return (r === undefined || r === "") ? null : r;
10885                         };
10886                 } else {
10887                         this.getId = function(){return null;};
10888                 }
10889             this.ef = [];
10890             for(var jj = 0; jj < fl; jj++){
10891                 f = fi[jj];
10892                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10893                 this.ef[jj] = this.getJsonAccessor(map);
10894             }
10895         }
10896
10897         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10898         if(s.totalProperty){
10899             var vt = parseInt(this.getTotal(o), 10);
10900             if(!isNaN(vt)){
10901                 totalRecords = vt;
10902             }
10903         }
10904         if(s.successProperty){
10905             var vs = this.getSuccess(o);
10906             if(vs === false || vs === 'false'){
10907                 success = false;
10908             }
10909         }
10910         var records = [];
10911         for(var i = 0; i < c; i++){
10912                 var n = root[i];
10913             var values = {};
10914             var id = this.getId(n);
10915             for(var j = 0; j < fl; j++){
10916                 f = fi[j];
10917             var v = this.ef[j](n);
10918             if (!f.convert) {
10919                 Roo.log('missing convert for ' + f.name);
10920                 Roo.log(f);
10921                 continue;
10922             }
10923             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10924             }
10925             var record = new Record(values, id);
10926             record.json = n;
10927             records[i] = record;
10928         }
10929         return {
10930             raw : o,
10931             success : success,
10932             records : records,
10933             totalRecords : totalRecords
10934         };
10935     }
10936 });/*
10937  * Based on:
10938  * Ext JS Library 1.1.1
10939  * Copyright(c) 2006-2007, Ext JS, LLC.
10940  *
10941  * Originally Released Under LGPL - original licence link has changed is not relivant.
10942  *
10943  * Fork - LGPL
10944  * <script type="text/javascript">
10945  */
10946
10947 /**
10948  * @class Roo.data.ArrayReader
10949  * @extends Roo.data.DataReader
10950  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10951  * Each element of that Array represents a row of data fields. The
10952  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10953  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10954  * <p>
10955  * Example code:.
10956  * <pre><code>
10957 var RecordDef = Roo.data.Record.create([
10958     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10959     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10960 ]);
10961 var myReader = new Roo.data.ArrayReader({
10962     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10963 }, RecordDef);
10964 </code></pre>
10965  * <p>
10966  * This would consume an Array like this:
10967  * <pre><code>
10968 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10969   </code></pre>
10970  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10971  * @constructor
10972  * Create a new JsonReader
10973  * @param {Object} meta Metadata configuration options.
10974  * @param {Object} recordType Either an Array of field definition objects
10975  * as specified to {@link Roo.data.Record#create},
10976  * or an {@link Roo.data.Record} object
10977  * created using {@link Roo.data.Record#create}.
10978  */
10979 Roo.data.ArrayReader = function(meta, recordType){
10980     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10981 };
10982
10983 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10984     /**
10985      * Create a data block containing Roo.data.Records from an XML document.
10986      * @param {Object} o An Array of row objects which represents the dataset.
10987      * @return {Object} data A data block which is used by an Roo.data.Store object as
10988      * a cache of Roo.data.Records.
10989      */
10990     readRecords : function(o){
10991         var sid = this.meta ? this.meta.id : null;
10992         var recordType = this.recordType, fields = recordType.prototype.fields;
10993         var records = [];
10994         var root = o;
10995             for(var i = 0; i < root.length; i++){
10996                     var n = root[i];
10997                 var values = {};
10998                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10999                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11000                 var f = fields.items[j];
11001                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11002                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11003                 v = f.convert(v);
11004                 values[f.name] = v;
11005             }
11006                 var record = new recordType(values, id);
11007                 record.json = n;
11008                 records[records.length] = record;
11009             }
11010             return {
11011                 records : records,
11012                 totalRecords : records.length
11013             };
11014     }
11015 });/*
11016  * - LGPL
11017  * * 
11018  */
11019
11020 /**
11021  * @class Roo.bootstrap.ComboBox
11022  * @extends Roo.bootstrap.TriggerField
11023  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11024  * @cfg {Boolean} append (true|false) default false
11025  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11026  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11027  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11028  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11029  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11030  * @constructor
11031  * Create a new ComboBox.
11032  * @param {Object} config Configuration options
11033  */
11034 Roo.bootstrap.ComboBox = function(config){
11035     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11036     this.addEvents({
11037         /**
11038          * @event expand
11039          * Fires when the dropdown list is expanded
11040              * @param {Roo.bootstrap.ComboBox} combo This combo box
11041              */
11042         'expand' : true,
11043         /**
11044          * @event collapse
11045          * Fires when the dropdown list is collapsed
11046              * @param {Roo.bootstrap.ComboBox} combo This combo box
11047              */
11048         'collapse' : true,
11049         /**
11050          * @event beforeselect
11051          * Fires before a list item is selected. Return false to cancel the selection.
11052              * @param {Roo.bootstrap.ComboBox} combo This combo box
11053              * @param {Roo.data.Record} record The data record returned from the underlying store
11054              * @param {Number} index The index of the selected item in the dropdown list
11055              */
11056         'beforeselect' : true,
11057         /**
11058          * @event select
11059          * Fires when a list item is selected
11060              * @param {Roo.bootstrap.ComboBox} combo This combo box
11061              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11062              * @param {Number} index The index of the selected item in the dropdown list
11063              */
11064         'select' : true,
11065         /**
11066          * @event beforequery
11067          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11068          * The event object passed has these properties:
11069              * @param {Roo.bootstrap.ComboBox} combo This combo box
11070              * @param {String} query The query
11071              * @param {Boolean} forceAll true to force "all" query
11072              * @param {Boolean} cancel true to cancel the query
11073              * @param {Object} e The query event object
11074              */
11075         'beforequery': true,
11076          /**
11077          * @event add
11078          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11079              * @param {Roo.bootstrap.ComboBox} combo This combo box
11080              */
11081         'add' : true,
11082         /**
11083          * @event edit
11084          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11085              * @param {Roo.bootstrap.ComboBox} combo This combo box
11086              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11087              */
11088         'edit' : true,
11089         /**
11090          * @event remove
11091          * Fires when the remove value from the combobox array
11092              * @param {Roo.bootstrap.ComboBox} combo This combo box
11093              */
11094         'remove' : true,
11095         /**
11096          * @event specialfilter
11097          * Fires when specialfilter
11098             * @param {Roo.bootstrap.ComboBox} combo This combo box
11099             */
11100         'specialfilter' : true
11101         
11102     });
11103     
11104     this.item = [];
11105     this.tickItems = [];
11106     
11107     this.selectedIndex = -1;
11108     if(this.mode == 'local'){
11109         if(config.queryDelay === undefined){
11110             this.queryDelay = 10;
11111         }
11112         if(config.minChars === undefined){
11113             this.minChars = 0;
11114         }
11115     }
11116 };
11117
11118 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11119      
11120     /**
11121      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11122      * rendering into an Roo.Editor, defaults to false)
11123      */
11124     /**
11125      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11126      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11127      */
11128     /**
11129      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11130      */
11131     /**
11132      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11133      * the dropdown list (defaults to undefined, with no header element)
11134      */
11135
11136      /**
11137      * @cfg {String/Roo.Template} tpl The template to use to render the output
11138      */
11139      
11140      /**
11141      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11142      */
11143     listWidth: undefined,
11144     /**
11145      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11146      * mode = 'remote' or 'text' if mode = 'local')
11147      */
11148     displayField: undefined,
11149     
11150     /**
11151      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11152      * mode = 'remote' or 'value' if mode = 'local'). 
11153      * Note: use of a valueField requires the user make a selection
11154      * in order for a value to be mapped.
11155      */
11156     valueField: undefined,
11157     
11158     
11159     /**
11160      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11161      * field's data value (defaults to the underlying DOM element's name)
11162      */
11163     hiddenName: undefined,
11164     /**
11165      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11166      */
11167     listClass: '',
11168     /**
11169      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11170      */
11171     selectedClass: 'active',
11172     
11173     /**
11174      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11175      */
11176     shadow:'sides',
11177     /**
11178      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11179      * anchor positions (defaults to 'tl-bl')
11180      */
11181     listAlign: 'tl-bl?',
11182     /**
11183      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11184      */
11185     maxHeight: 300,
11186     /**
11187      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11188      * query specified by the allQuery config option (defaults to 'query')
11189      */
11190     triggerAction: 'query',
11191     /**
11192      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11193      * (defaults to 4, does not apply if editable = false)
11194      */
11195     minChars : 4,
11196     /**
11197      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11198      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11199      */
11200     typeAhead: false,
11201     /**
11202      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11203      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11204      */
11205     queryDelay: 500,
11206     /**
11207      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11208      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11209      */
11210     pageSize: 0,
11211     /**
11212      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11213      * when editable = true (defaults to false)
11214      */
11215     selectOnFocus:false,
11216     /**
11217      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11218      */
11219     queryParam: 'query',
11220     /**
11221      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11222      * when mode = 'remote' (defaults to 'Loading...')
11223      */
11224     loadingText: 'Loading...',
11225     /**
11226      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11227      */
11228     resizable: false,
11229     /**
11230      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11231      */
11232     handleHeight : 8,
11233     /**
11234      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11235      * traditional select (defaults to true)
11236      */
11237     editable: true,
11238     /**
11239      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11240      */
11241     allQuery: '',
11242     /**
11243      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11244      */
11245     mode: 'remote',
11246     /**
11247      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11248      * listWidth has a higher value)
11249      */
11250     minListWidth : 70,
11251     /**
11252      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11253      * allow the user to set arbitrary text into the field (defaults to false)
11254      */
11255     forceSelection:false,
11256     /**
11257      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11258      * if typeAhead = true (defaults to 250)
11259      */
11260     typeAheadDelay : 250,
11261     /**
11262      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11263      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11264      */
11265     valueNotFoundText : undefined,
11266     /**
11267      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11268      */
11269     blockFocus : false,
11270     
11271     /**
11272      * @cfg {Boolean} disableClear Disable showing of clear button.
11273      */
11274     disableClear : false,
11275     /**
11276      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11277      */
11278     alwaysQuery : false,
11279     
11280     /**
11281      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11282      */
11283     multiple : false,
11284     
11285     /**
11286      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11287      */
11288     invalidClass : "has-warning",
11289     
11290     /**
11291      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11292      */
11293     validClass : "has-success",
11294     
11295     /**
11296      * @cfg {Boolean} specialFilter (true|false) special filter default false
11297      */
11298     specialFilter : false,
11299     
11300     //private
11301     addicon : false,
11302     editicon: false,
11303     
11304     page: 0,
11305     hasQuery: false,
11306     append: false,
11307     loadNext: false,
11308     autoFocus : true,
11309     tickable : false,
11310     btnPosition : 'right',
11311     triggerList : true,
11312     showToggleBtn : true,
11313     // element that contains real text value.. (when hidden is used..)
11314     
11315     getAutoCreate : function()
11316     {
11317         var cfg = false;
11318         
11319         /*
11320          *  Normal ComboBox
11321          */
11322         if(!this.tickable){
11323             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11324             return cfg;
11325         }
11326         
11327         /*
11328          *  ComboBox with tickable selections
11329          */
11330              
11331         var align = this.labelAlign || this.parentLabelAlign();
11332         
11333         cfg = {
11334             cls : 'form-group roo-combobox-tickable' //input-group
11335         };
11336         
11337         var buttons = {
11338             tag : 'div',
11339             cls : 'tickable-buttons',
11340             cn : [
11341                 {
11342                     tag : 'button',
11343                     type : 'button',
11344                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11345                     html : 'Edit'
11346                 },
11347                 {
11348                     tag : 'button',
11349                     type : 'button',
11350                     name : 'ok',
11351                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11352                     html : 'Done'
11353                 },
11354                 {
11355                     tag : 'button',
11356                     type : 'button',
11357                     name : 'cancel',
11358                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11359                     html : 'Cancel'
11360                 }
11361             ]
11362         };
11363         
11364         if(this.editable){
11365             buttons.cn.unshift({
11366                 tag: 'input',
11367                 cls: 'select2-search-field-input'
11368             });
11369         }
11370         
11371         var _this = this;
11372         
11373         Roo.each(buttons.cn, function(c){
11374             if (_this.size) {
11375                 c.cls += ' btn-' + _this.size;
11376             }
11377
11378             if (_this.disabled) {
11379                 c.disabled = true;
11380             }
11381         });
11382         
11383         var box = {
11384             tag: 'div',
11385             cn: [
11386                 {
11387                     tag: 'input',
11388                     type : 'hidden',
11389                     cls: 'form-hidden-field'
11390                 },
11391                 {
11392                     tag: 'ul',
11393                     cls: 'select2-choices',
11394                     cn:[
11395                         {
11396                             tag: 'li',
11397                             cls: 'select2-search-field',
11398                             cn: [
11399
11400                                 buttons
11401                             ]
11402                         }
11403                     ]
11404                 }
11405             ]
11406         }
11407         
11408         var combobox = {
11409             cls: 'select2-container input-group select2-container-multi',
11410             cn: [
11411                 box
11412 //                {
11413 //                    tag: 'ul',
11414 //                    cls: 'typeahead typeahead-long dropdown-menu',
11415 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11416 //                }
11417             ]
11418         };
11419         
11420         if(this.hasFeedback && !this.allowBlank){
11421             
11422             var feedback = {
11423                 tag: 'span',
11424                 cls: 'glyphicon form-control-feedback'
11425             };
11426
11427             combobox.cn.push(feedback);
11428         }
11429         
11430         if (align ==='left' && this.fieldLabel.length) {
11431             
11432                 Roo.log("left and has label");
11433                 cfg.cn = [
11434                     
11435                     {
11436                         tag: 'label',
11437                         'for' :  id,
11438                         cls : 'control-label col-sm-' + this.labelWidth,
11439                         html : this.fieldLabel
11440                         
11441                     },
11442                     {
11443                         cls : "col-sm-" + (12 - this.labelWidth), 
11444                         cn: [
11445                             combobox
11446                         ]
11447                     }
11448                     
11449                 ];
11450         } else if ( this.fieldLabel.length) {
11451                 Roo.log(" label");
11452                  cfg.cn = [
11453                    
11454                     {
11455                         tag: 'label',
11456                         //cls : 'input-group-addon',
11457                         html : this.fieldLabel
11458                         
11459                     },
11460                     
11461                     combobox
11462                     
11463                 ];
11464
11465         } else {
11466             
11467                 Roo.log(" no label && no align");
11468                 cfg = combobox
11469                      
11470                 
11471         }
11472          
11473         var settings=this;
11474         ['xs','sm','md','lg'].map(function(size){
11475             if (settings[size]) {
11476                 cfg.cls += ' col-' + size + '-' + settings[size];
11477             }
11478         });
11479         
11480         return cfg;
11481         
11482     },
11483     
11484     // private
11485     initEvents: function()
11486     {
11487         
11488         if (!this.store) {
11489             throw "can not find store for combo";
11490         }
11491         this.store = Roo.factory(this.store, Roo.data);
11492         
11493         if(this.tickable){
11494             this.initTickableEvents();
11495             return;
11496         }
11497         
11498         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11499         
11500         if(this.hiddenName){
11501             
11502             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11503             
11504             this.hiddenField.dom.value =
11505                 this.hiddenValue !== undefined ? this.hiddenValue :
11506                 this.value !== undefined ? this.value : '';
11507
11508             // prevent input submission
11509             this.el.dom.removeAttribute('name');
11510             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11511              
11512              
11513         }
11514         //if(Roo.isGecko){
11515         //    this.el.dom.setAttribute('autocomplete', 'off');
11516         //}
11517         
11518         var cls = 'x-combo-list';
11519         
11520         //this.list = new Roo.Layer({
11521         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11522         //});
11523         
11524         var _this = this;
11525         
11526         (function(){
11527             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11528             _this.list.setWidth(lw);
11529         }).defer(100);
11530         
11531         this.list.on('mouseover', this.onViewOver, this);
11532         this.list.on('mousemove', this.onViewMove, this);
11533         
11534         this.list.on('scroll', this.onViewScroll, this);
11535         
11536         /*
11537         this.list.swallowEvent('mousewheel');
11538         this.assetHeight = 0;
11539
11540         if(this.title){
11541             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11542             this.assetHeight += this.header.getHeight();
11543         }
11544
11545         this.innerList = this.list.createChild({cls:cls+'-inner'});
11546         this.innerList.on('mouseover', this.onViewOver, this);
11547         this.innerList.on('mousemove', this.onViewMove, this);
11548         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11549         
11550         if(this.allowBlank && !this.pageSize && !this.disableClear){
11551             this.footer = this.list.createChild({cls:cls+'-ft'});
11552             this.pageTb = new Roo.Toolbar(this.footer);
11553            
11554         }
11555         if(this.pageSize){
11556             this.footer = this.list.createChild({cls:cls+'-ft'});
11557             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11558                     {pageSize: this.pageSize});
11559             
11560         }
11561         
11562         if (this.pageTb && this.allowBlank && !this.disableClear) {
11563             var _this = this;
11564             this.pageTb.add(new Roo.Toolbar.Fill(), {
11565                 cls: 'x-btn-icon x-btn-clear',
11566                 text: '&#160;',
11567                 handler: function()
11568                 {
11569                     _this.collapse();
11570                     _this.clearValue();
11571                     _this.onSelect(false, -1);
11572                 }
11573             });
11574         }
11575         if (this.footer) {
11576             this.assetHeight += this.footer.getHeight();
11577         }
11578         */
11579             
11580         if(!this.tpl){
11581             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11582         }
11583
11584         this.view = new Roo.View(this.list, this.tpl, {
11585             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11586         });
11587         //this.view.wrapEl.setDisplayed(false);
11588         this.view.on('click', this.onViewClick, this);
11589         
11590         
11591         
11592         this.store.on('beforeload', this.onBeforeLoad, this);
11593         this.store.on('load', this.onLoad, this);
11594         this.store.on('loadexception', this.onLoadException, this);
11595         /*
11596         if(this.resizable){
11597             this.resizer = new Roo.Resizable(this.list,  {
11598                pinned:true, handles:'se'
11599             });
11600             this.resizer.on('resize', function(r, w, h){
11601                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11602                 this.listWidth = w;
11603                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11604                 this.restrictHeight();
11605             }, this);
11606             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11607         }
11608         */
11609         if(!this.editable){
11610             this.editable = true;
11611             this.setEditable(false);
11612         }
11613         
11614         /*
11615         
11616         if (typeof(this.events.add.listeners) != 'undefined') {
11617             
11618             this.addicon = this.wrap.createChild(
11619                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11620        
11621             this.addicon.on('click', function(e) {
11622                 this.fireEvent('add', this);
11623             }, this);
11624         }
11625         if (typeof(this.events.edit.listeners) != 'undefined') {
11626             
11627             this.editicon = this.wrap.createChild(
11628                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11629             if (this.addicon) {
11630                 this.editicon.setStyle('margin-left', '40px');
11631             }
11632             this.editicon.on('click', function(e) {
11633                 
11634                 // we fire even  if inothing is selected..
11635                 this.fireEvent('edit', this, this.lastData );
11636                 
11637             }, this);
11638         }
11639         */
11640         
11641         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11642             "up" : function(e){
11643                 this.inKeyMode = true;
11644                 this.selectPrev();
11645             },
11646
11647             "down" : function(e){
11648                 if(!this.isExpanded()){
11649                     this.onTriggerClick();
11650                 }else{
11651                     this.inKeyMode = true;
11652                     this.selectNext();
11653                 }
11654             },
11655
11656             "enter" : function(e){
11657 //                this.onViewClick();
11658                 //return true;
11659                 this.collapse();
11660                 
11661                 if(this.fireEvent("specialkey", this, e)){
11662                     this.onViewClick(false);
11663                 }
11664                 
11665                 return true;
11666             },
11667
11668             "esc" : function(e){
11669                 this.collapse();
11670             },
11671
11672             "tab" : function(e){
11673                 this.collapse();
11674                 
11675                 if(this.fireEvent("specialkey", this, e)){
11676                     this.onViewClick(false);
11677                 }
11678                 
11679                 return true;
11680             },
11681
11682             scope : this,
11683
11684             doRelay : function(foo, bar, hname){
11685                 if(hname == 'down' || this.scope.isExpanded()){
11686                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11687                 }
11688                 return true;
11689             },
11690
11691             forceKeyDown: true
11692         });
11693         
11694         
11695         this.queryDelay = Math.max(this.queryDelay || 10,
11696                 this.mode == 'local' ? 10 : 250);
11697         
11698         
11699         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11700         
11701         if(this.typeAhead){
11702             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11703         }
11704         if(this.editable !== false){
11705             this.inputEl().on("keyup", this.onKeyUp, this);
11706         }
11707         if(this.forceSelection){
11708             this.inputEl().on('blur', this.doForce, this);
11709         }
11710         
11711         if(this.multiple){
11712             this.choices = this.el.select('ul.select2-choices', true).first();
11713             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11714         }
11715     },
11716     
11717     initTickableEvents: function()
11718     {   
11719         this.createList();
11720         
11721         if(this.hiddenName){
11722             
11723             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11724             
11725             this.hiddenField.dom.value =
11726                 this.hiddenValue !== undefined ? this.hiddenValue :
11727                 this.value !== undefined ? this.value : '';
11728
11729             // prevent input submission
11730             this.el.dom.removeAttribute('name');
11731             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11732              
11733              
11734         }
11735         
11736 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11737         
11738         this.choices = this.el.select('ul.select2-choices', true).first();
11739         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11740         if(this.triggerList){
11741             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11742         }
11743          
11744         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11745         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11746         
11747         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11748         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11749         
11750         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11751         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11752         
11753         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11754         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11755         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11756         
11757         this.okBtn.hide();
11758         this.cancelBtn.hide();
11759         
11760         var _this = this;
11761         
11762         (function(){
11763             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11764             _this.list.setWidth(lw);
11765         }).defer(100);
11766         
11767         this.list.on('mouseover', this.onViewOver, this);
11768         this.list.on('mousemove', this.onViewMove, this);
11769         
11770         this.list.on('scroll', this.onViewScroll, this);
11771         
11772         if(!this.tpl){
11773             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>';
11774         }
11775
11776         this.view = new Roo.View(this.list, this.tpl, {
11777             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11778         });
11779         
11780         //this.view.wrapEl.setDisplayed(false);
11781         this.view.on('click', this.onViewClick, this);
11782         
11783         
11784         
11785         this.store.on('beforeload', this.onBeforeLoad, this);
11786         this.store.on('load', this.onLoad, this);
11787         this.store.on('loadexception', this.onLoadException, this);
11788         
11789         if(this.editable){
11790             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11791                 "up" : function(e){
11792                     this.inKeyMode = true;
11793                     this.selectPrev();
11794                 },
11795
11796                 "down" : function(e){
11797                     this.inKeyMode = true;
11798                     this.selectNext();
11799                 },
11800
11801                 "enter" : function(e){
11802                     if(this.fireEvent("specialkey", this, e)){
11803                         this.onViewClick(false);
11804                     }
11805                     
11806                     return true;
11807                 },
11808
11809                 "esc" : function(e){
11810                     this.onTickableFooterButtonClick(e, false, false);
11811                 },
11812
11813                 "tab" : function(e){
11814                     this.fireEvent("specialkey", this, e);
11815                     
11816                     this.onTickableFooterButtonClick(e, false, false);
11817                     
11818                     return true;
11819                 },
11820
11821                 scope : this,
11822
11823                 doRelay : function(e, fn, key){
11824                     if(this.scope.isExpanded()){
11825                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11826                     }
11827                     return true;
11828                 },
11829
11830                 forceKeyDown: true
11831             });
11832         }
11833         
11834         this.queryDelay = Math.max(this.queryDelay || 10,
11835                 this.mode == 'local' ? 10 : 250);
11836         
11837         
11838         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11839         
11840         if(this.typeAhead){
11841             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11842         }
11843         
11844         if(this.editable !== false){
11845             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11846         }
11847         
11848     },
11849
11850     onDestroy : function(){
11851         if(this.view){
11852             this.view.setStore(null);
11853             this.view.el.removeAllListeners();
11854             this.view.el.remove();
11855             this.view.purgeListeners();
11856         }
11857         if(this.list){
11858             this.list.dom.innerHTML  = '';
11859         }
11860         
11861         if(this.store){
11862             this.store.un('beforeload', this.onBeforeLoad, this);
11863             this.store.un('load', this.onLoad, this);
11864             this.store.un('loadexception', this.onLoadException, this);
11865         }
11866         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11867     },
11868
11869     // private
11870     fireKey : function(e){
11871         if(e.isNavKeyPress() && !this.list.isVisible()){
11872             this.fireEvent("specialkey", this, e);
11873         }
11874     },
11875
11876     // private
11877     onResize: function(w, h){
11878 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11879 //        
11880 //        if(typeof w != 'number'){
11881 //            // we do not handle it!?!?
11882 //            return;
11883 //        }
11884 //        var tw = this.trigger.getWidth();
11885 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11886 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11887 //        var x = w - tw;
11888 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11889 //            
11890 //        //this.trigger.setStyle('left', x+'px');
11891 //        
11892 //        if(this.list && this.listWidth === undefined){
11893 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11894 //            this.list.setWidth(lw);
11895 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11896 //        }
11897         
11898     
11899         
11900     },
11901
11902     /**
11903      * Allow or prevent the user from directly editing the field text.  If false is passed,
11904      * the user will only be able to select from the items defined in the dropdown list.  This method
11905      * is the runtime equivalent of setting the 'editable' config option at config time.
11906      * @param {Boolean} value True to allow the user to directly edit the field text
11907      */
11908     setEditable : function(value){
11909         if(value == this.editable){
11910             return;
11911         }
11912         this.editable = value;
11913         if(!value){
11914             this.inputEl().dom.setAttribute('readOnly', true);
11915             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11916             this.inputEl().addClass('x-combo-noedit');
11917         }else{
11918             this.inputEl().dom.setAttribute('readOnly', false);
11919             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11920             this.inputEl().removeClass('x-combo-noedit');
11921         }
11922     },
11923
11924     // private
11925     
11926     onBeforeLoad : function(combo,opts){
11927         if(!this.hasFocus){
11928             return;
11929         }
11930          if (!opts.add) {
11931             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11932          }
11933         this.restrictHeight();
11934         this.selectedIndex = -1;
11935     },
11936
11937     // private
11938     onLoad : function(){
11939         
11940         this.hasQuery = false;
11941         
11942         if(!this.hasFocus){
11943             return;
11944         }
11945         
11946         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11947             this.loading.hide();
11948         }
11949              
11950         if(this.store.getCount() > 0){
11951             this.expand();
11952             this.restrictHeight();
11953             if(this.lastQuery == this.allQuery){
11954                 if(this.editable && !this.tickable){
11955                     this.inputEl().dom.select();
11956                 }
11957                 
11958                 if(
11959                     !this.selectByValue(this.value, true) &&
11960                     this.autoFocus && 
11961                     (
11962                         !this.store.lastOptions ||
11963                         typeof(this.store.lastOptions.add) == 'undefined' || 
11964                         this.store.lastOptions.add != true
11965                     )
11966                 ){
11967                     this.select(0, true);
11968                 }
11969             }else{
11970                 if(this.autoFocus){
11971                     this.selectNext();
11972                 }
11973                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11974                     this.taTask.delay(this.typeAheadDelay);
11975                 }
11976             }
11977         }else{
11978             this.onEmptyResults();
11979         }
11980         
11981         //this.el.focus();
11982     },
11983     // private
11984     onLoadException : function()
11985     {
11986         this.hasQuery = false;
11987         
11988         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11989             this.loading.hide();
11990         }
11991         
11992         if(this.tickable && this.editable){
11993             return;
11994         }
11995         
11996         this.collapse();
11997         
11998         Roo.log(this.store.reader.jsonData);
11999         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12000             // fixme
12001             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12002         }
12003         
12004         
12005     },
12006     // private
12007     onTypeAhead : function(){
12008         if(this.store.getCount() > 0){
12009             var r = this.store.getAt(0);
12010             var newValue = r.data[this.displayField];
12011             var len = newValue.length;
12012             var selStart = this.getRawValue().length;
12013             
12014             if(selStart != len){
12015                 this.setRawValue(newValue);
12016                 this.selectText(selStart, newValue.length);
12017             }
12018         }
12019     },
12020
12021     // private
12022     onSelect : function(record, index){
12023         
12024         if(this.fireEvent('beforeselect', this, record, index) !== false){
12025         
12026             this.setFromData(index > -1 ? record.data : false);
12027             
12028             this.collapse();
12029             this.fireEvent('select', this, record, index);
12030         }
12031     },
12032
12033     /**
12034      * Returns the currently selected field value or empty string if no value is set.
12035      * @return {String} value The selected value
12036      */
12037     getValue : function(){
12038         
12039         if(this.multiple){
12040             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12041         }
12042         
12043         if(this.valueField){
12044             return typeof this.value != 'undefined' ? this.value : '';
12045         }else{
12046             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12047         }
12048     },
12049
12050     /**
12051      * Clears any text/value currently set in the field
12052      */
12053     clearValue : function(){
12054         if(this.hiddenField){
12055             this.hiddenField.dom.value = '';
12056         }
12057         this.value = '';
12058         this.setRawValue('');
12059         this.lastSelectionText = '';
12060         this.lastData = false;
12061         
12062         var close = this.closeTriggerEl();
12063         
12064         if(close){
12065             close.hide();
12066         }
12067         
12068     },
12069
12070     /**
12071      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12072      * will be displayed in the field.  If the value does not match the data value of an existing item,
12073      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12074      * Otherwise the field will be blank (although the value will still be set).
12075      * @param {String} value The value to match
12076      */
12077     setValue : function(v){
12078         if(this.multiple){
12079             this.syncValue();
12080             return;
12081         }
12082         
12083         var text = v;
12084         if(this.valueField){
12085             var r = this.findRecord(this.valueField, v);
12086             if(r){
12087                 text = r.data[this.displayField];
12088             }else if(this.valueNotFoundText !== undefined){
12089                 text = this.valueNotFoundText;
12090             }
12091         }
12092         this.lastSelectionText = text;
12093         if(this.hiddenField){
12094             this.hiddenField.dom.value = v;
12095         }
12096         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12097         this.value = v;
12098         
12099         var close = this.closeTriggerEl();
12100         
12101         if(close){
12102             (v.length || v * 1 > 0) ? close.show() : close.hide();
12103         }
12104     },
12105     /**
12106      * @property {Object} the last set data for the element
12107      */
12108     
12109     lastData : false,
12110     /**
12111      * Sets the value of the field based on a object which is related to the record format for the store.
12112      * @param {Object} value the value to set as. or false on reset?
12113      */
12114     setFromData : function(o){
12115         
12116         if(this.multiple){
12117             this.addItem(o);
12118             return;
12119         }
12120             
12121         var dv = ''; // display value
12122         var vv = ''; // value value..
12123         this.lastData = o;
12124         if (this.displayField) {
12125             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12126         } else {
12127             // this is an error condition!!!
12128             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12129         }
12130         
12131         if(this.valueField){
12132             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12133         }
12134         
12135         var close = this.closeTriggerEl();
12136         
12137         if(close){
12138             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12139         }
12140         
12141         if(this.hiddenField){
12142             this.hiddenField.dom.value = vv;
12143             
12144             this.lastSelectionText = dv;
12145             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12146             this.value = vv;
12147             return;
12148         }
12149         // no hidden field.. - we store the value in 'value', but still display
12150         // display field!!!!
12151         this.lastSelectionText = dv;
12152         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12153         this.value = vv;
12154         
12155         
12156         
12157     },
12158     // private
12159     reset : function(){
12160         // overridden so that last data is reset..
12161         
12162         if(this.multiple){
12163             this.clearItem();
12164             return;
12165         }
12166         
12167         this.setValue(this.originalValue);
12168         this.clearInvalid();
12169         this.lastData = false;
12170         if (this.view) {
12171             this.view.clearSelections();
12172         }
12173     },
12174     // private
12175     findRecord : function(prop, value){
12176         var record;
12177         if(this.store.getCount() > 0){
12178             this.store.each(function(r){
12179                 if(r.data[prop] == value){
12180                     record = r;
12181                     return false;
12182                 }
12183                 return true;
12184             });
12185         }
12186         return record;
12187     },
12188     
12189     getName: function()
12190     {
12191         // returns hidden if it's set..
12192         if (!this.rendered) {return ''};
12193         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12194         
12195     },
12196     // private
12197     onViewMove : function(e, t){
12198         this.inKeyMode = false;
12199     },
12200
12201     // private
12202     onViewOver : function(e, t){
12203         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12204             return;
12205         }
12206         var item = this.view.findItemFromChild(t);
12207         
12208         if(item){
12209             var index = this.view.indexOf(item);
12210             this.select(index, false);
12211         }
12212     },
12213
12214     // private
12215     onViewClick : function(view, doFocus, el, e)
12216     {
12217         var index = this.view.getSelectedIndexes()[0];
12218         
12219         var r = this.store.getAt(index);
12220         
12221         if(this.tickable){
12222             
12223             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12224                 return;
12225             }
12226             
12227             var rm = false;
12228             var _this = this;
12229             
12230             Roo.each(this.tickItems, function(v,k){
12231                 
12232                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12233                     _this.tickItems.splice(k, 1);
12234                     
12235                     if(typeof(e) == 'undefined' && view == false){
12236                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12237                     }
12238                     
12239                     rm = true;
12240                     return;
12241                 }
12242             });
12243             
12244             if(rm){
12245                 return;
12246             }
12247             
12248             this.tickItems.push(r.data);
12249             
12250             if(typeof(e) == 'undefined' && view == false){
12251                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12252             }
12253                     
12254             return;
12255         }
12256         
12257         if(r){
12258             this.onSelect(r, index);
12259         }
12260         if(doFocus !== false && !this.blockFocus){
12261             this.inputEl().focus();
12262         }
12263     },
12264
12265     // private
12266     restrictHeight : function(){
12267         //this.innerList.dom.style.height = '';
12268         //var inner = this.innerList.dom;
12269         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12270         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12271         //this.list.beginUpdate();
12272         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12273         this.list.alignTo(this.inputEl(), this.listAlign);
12274         this.list.alignTo(this.inputEl(), this.listAlign);
12275         //this.list.endUpdate();
12276     },
12277
12278     // private
12279     onEmptyResults : function(){
12280         
12281         if(this.tickable && this.editable){
12282             this.restrictHeight();
12283             return;
12284         }
12285         
12286         this.collapse();
12287     },
12288
12289     /**
12290      * Returns true if the dropdown list is expanded, else false.
12291      */
12292     isExpanded : function(){
12293         return this.list.isVisible();
12294     },
12295
12296     /**
12297      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12298      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12299      * @param {String} value The data value of the item to select
12300      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12301      * selected item if it is not currently in view (defaults to true)
12302      * @return {Boolean} True if the value matched an item in the list, else false
12303      */
12304     selectByValue : function(v, scrollIntoView){
12305         if(v !== undefined && v !== null){
12306             var r = this.findRecord(this.valueField || this.displayField, v);
12307             if(r){
12308                 this.select(this.store.indexOf(r), scrollIntoView);
12309                 return true;
12310             }
12311         }
12312         return false;
12313     },
12314
12315     /**
12316      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12317      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12318      * @param {Number} index The zero-based index of the list item to select
12319      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12320      * selected item if it is not currently in view (defaults to true)
12321      */
12322     select : function(index, scrollIntoView){
12323         this.selectedIndex = index;
12324         this.view.select(index);
12325         if(scrollIntoView !== false){
12326             var el = this.view.getNode(index);
12327             /*
12328              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12329              */
12330             if(el){
12331                 this.list.scrollChildIntoView(el, false);
12332             }
12333         }
12334     },
12335
12336     // private
12337     selectNext : function(){
12338         var ct = this.store.getCount();
12339         if(ct > 0){
12340             if(this.selectedIndex == -1){
12341                 this.select(0);
12342             }else if(this.selectedIndex < ct-1){
12343                 this.select(this.selectedIndex+1);
12344             }
12345         }
12346     },
12347
12348     // private
12349     selectPrev : function(){
12350         var ct = this.store.getCount();
12351         if(ct > 0){
12352             if(this.selectedIndex == -1){
12353                 this.select(0);
12354             }else if(this.selectedIndex != 0){
12355                 this.select(this.selectedIndex-1);
12356             }
12357         }
12358     },
12359
12360     // private
12361     onKeyUp : function(e){
12362         if(this.editable !== false && !e.isSpecialKey()){
12363             this.lastKey = e.getKey();
12364             this.dqTask.delay(this.queryDelay);
12365         }
12366     },
12367
12368     // private
12369     validateBlur : function(){
12370         return !this.list || !this.list.isVisible();   
12371     },
12372
12373     // private
12374     initQuery : function(){
12375         
12376         var v = this.getRawValue();
12377         
12378         if(this.tickable && this.editable){
12379             v = this.tickableInputEl().getValue();
12380         }
12381         
12382         this.doQuery(v);
12383     },
12384
12385     // private
12386     doForce : function(){
12387         if(this.inputEl().dom.value.length > 0){
12388             this.inputEl().dom.value =
12389                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12390              
12391         }
12392     },
12393
12394     /**
12395      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12396      * query allowing the query action to be canceled if needed.
12397      * @param {String} query The SQL query to execute
12398      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12399      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12400      * saved in the current store (defaults to false)
12401      */
12402     doQuery : function(q, forceAll){
12403         
12404         if(q === undefined || q === null){
12405             q = '';
12406         }
12407         var qe = {
12408             query: q,
12409             forceAll: forceAll,
12410             combo: this,
12411             cancel:false
12412         };
12413         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12414             return false;
12415         }
12416         q = qe.query;
12417         
12418         forceAll = qe.forceAll;
12419         if(forceAll === true || (q.length >= this.minChars)){
12420             
12421             this.hasQuery = true;
12422             
12423             if(this.lastQuery != q || this.alwaysQuery){
12424                 this.lastQuery = q;
12425                 if(this.mode == 'local'){
12426                     this.selectedIndex = -1;
12427                     if(forceAll){
12428                         this.store.clearFilter();
12429                     }else{
12430                         
12431                         if(this.specialFilter){
12432                             this.fireEvent('specialfilter', this);
12433                             this.onLoad();
12434                             return;
12435                         }
12436                         
12437                         this.store.filter(this.displayField, q);
12438                     }
12439                     
12440                     this.store.fireEvent("datachanged", this.store);
12441                     
12442                     this.onLoad();
12443                     
12444                     
12445                 }else{
12446                     
12447                     this.store.baseParams[this.queryParam] = q;
12448                     
12449                     var options = {params : this.getParams(q)};
12450                     
12451                     if(this.loadNext){
12452                         options.add = true;
12453                         options.params.start = this.page * this.pageSize;
12454                     }
12455                     
12456                     this.store.load(options);
12457                     
12458                     /*
12459                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12460                      *  we should expand the list on onLoad
12461                      *  so command out it
12462                      */
12463 //                    this.expand();
12464                 }
12465             }else{
12466                 this.selectedIndex = -1;
12467                 this.onLoad();   
12468             }
12469         }
12470         
12471         this.loadNext = false;
12472     },
12473     
12474     // private
12475     getParams : function(q){
12476         var p = {};
12477         //p[this.queryParam] = q;
12478         
12479         if(this.pageSize){
12480             p.start = 0;
12481             p.limit = this.pageSize;
12482         }
12483         return p;
12484     },
12485
12486     /**
12487      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12488      */
12489     collapse : function(){
12490         if(!this.isExpanded()){
12491             return;
12492         }
12493         
12494         this.list.hide();
12495         
12496         if(this.tickable){
12497             this.hasFocus = false;
12498             this.okBtn.hide();
12499             this.cancelBtn.hide();
12500             this.trigger.show();
12501             
12502             if(this.editable){
12503                 this.tickableInputEl().dom.value = '';
12504                 this.tickableInputEl().blur();
12505             }
12506             
12507         }
12508         
12509         Roo.get(document).un('mousedown', this.collapseIf, this);
12510         Roo.get(document).un('mousewheel', this.collapseIf, this);
12511         if (!this.editable) {
12512             Roo.get(document).un('keydown', this.listKeyPress, this);
12513         }
12514         this.fireEvent('collapse', this);
12515     },
12516
12517     // private
12518     collapseIf : function(e){
12519         var in_combo  = e.within(this.el);
12520         var in_list =  e.within(this.list);
12521         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12522         
12523         if (in_combo || in_list || is_list) {
12524             //e.stopPropagation();
12525             return;
12526         }
12527         
12528         if(this.tickable){
12529             this.onTickableFooterButtonClick(e, false, false);
12530         }
12531
12532         this.collapse();
12533         
12534     },
12535
12536     /**
12537      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12538      */
12539     expand : function(){
12540        
12541         if(this.isExpanded() || !this.hasFocus){
12542             return;
12543         }
12544         
12545         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12546         this.list.setWidth(lw);
12547         
12548         
12549          Roo.log('expand');
12550         
12551         this.list.show();
12552         
12553         this.restrictHeight();
12554         
12555         if(this.tickable){
12556             
12557             this.tickItems = Roo.apply([], this.item);
12558             
12559             this.okBtn.show();
12560             this.cancelBtn.show();
12561             this.trigger.hide();
12562             
12563             if(this.editable){
12564                 this.tickableInputEl().focus();
12565             }
12566             
12567         }
12568         
12569         Roo.get(document).on('mousedown', this.collapseIf, this);
12570         Roo.get(document).on('mousewheel', this.collapseIf, this);
12571         if (!this.editable) {
12572             Roo.get(document).on('keydown', this.listKeyPress, this);
12573         }
12574         
12575         this.fireEvent('expand', this);
12576     },
12577
12578     // private
12579     // Implements the default empty TriggerField.onTriggerClick function
12580     onTriggerClick : function(e)
12581     {
12582         Roo.log('trigger click');
12583         
12584         if(this.disabled || !this.triggerList){
12585             return;
12586         }
12587         
12588         this.page = 0;
12589         this.loadNext = false;
12590         
12591         if(this.isExpanded()){
12592             this.collapse();
12593             if (!this.blockFocus) {
12594                 this.inputEl().focus();
12595             }
12596             
12597         }else {
12598             this.hasFocus = true;
12599             if(this.triggerAction == 'all') {
12600                 this.doQuery(this.allQuery, true);
12601             } else {
12602                 this.doQuery(this.getRawValue());
12603             }
12604             if (!this.blockFocus) {
12605                 this.inputEl().focus();
12606             }
12607         }
12608     },
12609     
12610     onTickableTriggerClick : function(e)
12611     {
12612         if(this.disabled){
12613             return;
12614         }
12615         
12616         this.page = 0;
12617         this.loadNext = false;
12618         this.hasFocus = true;
12619         
12620         if(this.triggerAction == 'all') {
12621             this.doQuery(this.allQuery, true);
12622         } else {
12623             this.doQuery(this.getRawValue());
12624         }
12625     },
12626     
12627     onSearchFieldClick : function(e)
12628     {
12629         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12630             this.onTickableFooterButtonClick(e, false, false);
12631             return;
12632         }
12633         
12634         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12635             return;
12636         }
12637         
12638         this.page = 0;
12639         this.loadNext = false;
12640         this.hasFocus = true;
12641         
12642         if(this.triggerAction == 'all') {
12643             this.doQuery(this.allQuery, true);
12644         } else {
12645             this.doQuery(this.getRawValue());
12646         }
12647     },
12648     
12649     listKeyPress : function(e)
12650     {
12651         //Roo.log('listkeypress');
12652         // scroll to first matching element based on key pres..
12653         if (e.isSpecialKey()) {
12654             return false;
12655         }
12656         var k = String.fromCharCode(e.getKey()).toUpperCase();
12657         //Roo.log(k);
12658         var match  = false;
12659         var csel = this.view.getSelectedNodes();
12660         var cselitem = false;
12661         if (csel.length) {
12662             var ix = this.view.indexOf(csel[0]);
12663             cselitem  = this.store.getAt(ix);
12664             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12665                 cselitem = false;
12666             }
12667             
12668         }
12669         
12670         this.store.each(function(v) { 
12671             if (cselitem) {
12672                 // start at existing selection.
12673                 if (cselitem.id == v.id) {
12674                     cselitem = false;
12675                 }
12676                 return true;
12677             }
12678                 
12679             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12680                 match = this.store.indexOf(v);
12681                 return false;
12682             }
12683             return true;
12684         }, this);
12685         
12686         if (match === false) {
12687             return true; // no more action?
12688         }
12689         // scroll to?
12690         this.view.select(match);
12691         var sn = Roo.get(this.view.getSelectedNodes()[0])
12692         sn.scrollIntoView(sn.dom.parentNode, false);
12693     },
12694     
12695     onViewScroll : function(e, t){
12696         
12697         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){
12698             return;
12699         }
12700         
12701         this.hasQuery = true;
12702         
12703         this.loading = this.list.select('.loading', true).first();
12704         
12705         if(this.loading === null){
12706             this.list.createChild({
12707                 tag: 'div',
12708                 cls: 'loading select2-more-results select2-active',
12709                 html: 'Loading more results...'
12710             })
12711             
12712             this.loading = this.list.select('.loading', true).first();
12713             
12714             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12715             
12716             this.loading.hide();
12717         }
12718         
12719         this.loading.show();
12720         
12721         var _combo = this;
12722         
12723         this.page++;
12724         this.loadNext = true;
12725         
12726         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12727         
12728         return;
12729     },
12730     
12731     addItem : function(o)
12732     {   
12733         var dv = ''; // display value
12734         
12735         if (this.displayField) {
12736             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12737         } else {
12738             // this is an error condition!!!
12739             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12740         }
12741         
12742         if(!dv.length){
12743             return;
12744         }
12745         
12746         var choice = this.choices.createChild({
12747             tag: 'li',
12748             cls: 'select2-search-choice',
12749             cn: [
12750                 {
12751                     tag: 'div',
12752                     html: dv
12753                 },
12754                 {
12755                     tag: 'a',
12756                     href: '#',
12757                     cls: 'select2-search-choice-close',
12758                     tabindex: '-1'
12759                 }
12760             ]
12761             
12762         }, this.searchField);
12763         
12764         var close = choice.select('a.select2-search-choice-close', true).first()
12765         
12766         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12767         
12768         this.item.push(o);
12769         
12770         this.lastData = o;
12771         
12772         this.syncValue();
12773         
12774         this.inputEl().dom.value = '';
12775         
12776         this.validate();
12777     },
12778     
12779     onRemoveItem : function(e, _self, o)
12780     {
12781         e.preventDefault();
12782         
12783         this.lastItem = Roo.apply([], this.item);
12784         
12785         var index = this.item.indexOf(o.data) * 1;
12786         
12787         if( index < 0){
12788             Roo.log('not this item?!');
12789             return;
12790         }
12791         
12792         this.item.splice(index, 1);
12793         o.item.remove();
12794         
12795         this.syncValue();
12796         
12797         this.fireEvent('remove', this, e);
12798         
12799         this.validate();
12800         
12801     },
12802     
12803     syncValue : function()
12804     {
12805         if(!this.item.length){
12806             this.clearValue();
12807             return;
12808         }
12809             
12810         var value = [];
12811         var _this = this;
12812         Roo.each(this.item, function(i){
12813             if(_this.valueField){
12814                 value.push(i[_this.valueField]);
12815                 return;
12816             }
12817
12818             value.push(i);
12819         });
12820
12821         this.value = value.join(',');
12822
12823         if(this.hiddenField){
12824             this.hiddenField.dom.value = this.value;
12825         }
12826         
12827         this.store.fireEvent("datachanged", this.store);
12828     },
12829     
12830     clearItem : function()
12831     {
12832         if(!this.multiple){
12833             return;
12834         }
12835         
12836         this.item = [];
12837         
12838         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12839            c.remove();
12840         });
12841         
12842         this.syncValue();
12843         
12844         this.validate();
12845     },
12846     
12847     inputEl: function ()
12848     {
12849         if(this.tickable){
12850             return this.searchField;
12851         }
12852         return this.el.select('input.form-control',true).first();
12853     },
12854     
12855     
12856     onTickableFooterButtonClick : function(e, btn, el)
12857     {
12858         e.preventDefault();
12859         
12860         this.lastItem = Roo.apply([], this.item);
12861         
12862         if(btn && btn.name == 'cancel'){
12863             this.tickItems = Roo.apply([], this.item);
12864             this.collapse();
12865             return;
12866         }
12867         
12868         this.clearItem();
12869         
12870         var _this = this;
12871         
12872         Roo.each(this.tickItems, function(o){
12873             _this.addItem(o);
12874         });
12875         
12876         this.collapse();
12877         
12878     },
12879     
12880     validate : function()
12881     {
12882         var v = this.getRawValue();
12883         
12884         if(this.multiple){
12885             v = this.getValue();
12886         }
12887         
12888         if(this.disabled || this.allowBlank || v.length){
12889             this.markValid();
12890             return true;
12891         }
12892         
12893         this.markInvalid();
12894         return false;
12895     },
12896     
12897     tickableInputEl : function()
12898     {
12899         if(!this.tickable || !this.editable){
12900             return this.inputEl();
12901         }
12902         
12903         return this.inputEl().select('.select2-search-field-input', true).first();
12904     }
12905     
12906     
12907
12908     /** 
12909     * @cfg {Boolean} grow 
12910     * @hide 
12911     */
12912     /** 
12913     * @cfg {Number} growMin 
12914     * @hide 
12915     */
12916     /** 
12917     * @cfg {Number} growMax 
12918     * @hide 
12919     */
12920     /**
12921      * @hide
12922      * @method autoSize
12923      */
12924 });
12925 /*
12926  * Based on:
12927  * Ext JS Library 1.1.1
12928  * Copyright(c) 2006-2007, Ext JS, LLC.
12929  *
12930  * Originally Released Under LGPL - original licence link has changed is not relivant.
12931  *
12932  * Fork - LGPL
12933  * <script type="text/javascript">
12934  */
12935
12936 /**
12937  * @class Roo.View
12938  * @extends Roo.util.Observable
12939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12940  * This class also supports single and multi selection modes. <br>
12941  * Create a data model bound view:
12942  <pre><code>
12943  var store = new Roo.data.Store(...);
12944
12945  var view = new Roo.View({
12946     el : "my-element",
12947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12948  
12949     singleSelect: true,
12950     selectedClass: "ydataview-selected",
12951     store: store
12952  });
12953
12954  // listen for node click?
12955  view.on("click", function(vw, index, node, e){
12956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12957  });
12958
12959  // load XML data
12960  dataModel.load("foobar.xml");
12961  </code></pre>
12962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12963  * <br><br>
12964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12966  * 
12967  * Note: old style constructor is still suported (container, template, config)
12968  * 
12969  * @constructor
12970  * Create a new View
12971  * @param {Object} config The config object
12972  * 
12973  */
12974 Roo.View = function(config, depreciated_tpl, depreciated_config){
12975     
12976     this.parent = false;
12977     
12978     if (typeof(depreciated_tpl) == 'undefined') {
12979         // new way.. - universal constructor.
12980         Roo.apply(this, config);
12981         this.el  = Roo.get(this.el);
12982     } else {
12983         // old format..
12984         this.el  = Roo.get(config);
12985         this.tpl = depreciated_tpl;
12986         Roo.apply(this, depreciated_config);
12987     }
12988     this.wrapEl  = this.el.wrap().wrap();
12989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12990     
12991     
12992     if(typeof(this.tpl) == "string"){
12993         this.tpl = new Roo.Template(this.tpl);
12994     } else {
12995         // support xtype ctors..
12996         this.tpl = new Roo.factory(this.tpl, Roo);
12997     }
12998     
12999     
13000     this.tpl.compile();
13001     
13002     /** @private */
13003     this.addEvents({
13004         /**
13005          * @event beforeclick
13006          * Fires before a click is processed. Returns false to cancel the default action.
13007          * @param {Roo.View} this
13008          * @param {Number} index The index of the target node
13009          * @param {HTMLElement} node The target node
13010          * @param {Roo.EventObject} e The raw event object
13011          */
13012             "beforeclick" : true,
13013         /**
13014          * @event click
13015          * Fires when a template node is clicked.
13016          * @param {Roo.View} this
13017          * @param {Number} index The index of the target node
13018          * @param {HTMLElement} node The target node
13019          * @param {Roo.EventObject} e The raw event object
13020          */
13021             "click" : true,
13022         /**
13023          * @event dblclick
13024          * Fires when a template node is double clicked.
13025          * @param {Roo.View} this
13026          * @param {Number} index The index of the target node
13027          * @param {HTMLElement} node The target node
13028          * @param {Roo.EventObject} e The raw event object
13029          */
13030             "dblclick" : true,
13031         /**
13032          * @event contextmenu
13033          * Fires when a template node is right clicked.
13034          * @param {Roo.View} this
13035          * @param {Number} index The index of the target node
13036          * @param {HTMLElement} node The target node
13037          * @param {Roo.EventObject} e The raw event object
13038          */
13039             "contextmenu" : true,
13040         /**
13041          * @event selectionchange
13042          * Fires when the selected nodes change.
13043          * @param {Roo.View} this
13044          * @param {Array} selections Array of the selected nodes
13045          */
13046             "selectionchange" : true,
13047     
13048         /**
13049          * @event beforeselect
13050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13051          * @param {Roo.View} this
13052          * @param {HTMLElement} node The node to be selected
13053          * @param {Array} selections Array of currently selected nodes
13054          */
13055             "beforeselect" : true,
13056         /**
13057          * @event preparedata
13058          * Fires on every row to render, to allow you to change the data.
13059          * @param {Roo.View} this
13060          * @param {Object} data to be rendered (change this)
13061          */
13062           "preparedata" : true
13063           
13064           
13065         });
13066
13067
13068
13069     this.el.on({
13070         "click": this.onClick,
13071         "dblclick": this.onDblClick,
13072         "contextmenu": this.onContextMenu,
13073         scope:this
13074     });
13075
13076     this.selections = [];
13077     this.nodes = [];
13078     this.cmp = new Roo.CompositeElementLite([]);
13079     if(this.store){
13080         this.store = Roo.factory(this.store, Roo.data);
13081         this.setStore(this.store, true);
13082     }
13083     
13084     if ( this.footer && this.footer.xtype) {
13085            
13086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13087         
13088         this.footer.dataSource = this.store
13089         this.footer.container = fctr;
13090         this.footer = Roo.factory(this.footer, Roo);
13091         fctr.insertFirst(this.el);
13092         
13093         // this is a bit insane - as the paging toolbar seems to detach the el..
13094 //        dom.parentNode.parentNode.parentNode
13095          // they get detached?
13096     }
13097     
13098     
13099     Roo.View.superclass.constructor.call(this);
13100     
13101     
13102 };
13103
13104 Roo.extend(Roo.View, Roo.util.Observable, {
13105     
13106      /**
13107      * @cfg {Roo.data.Store} store Data store to load data from.
13108      */
13109     store : false,
13110     
13111     /**
13112      * @cfg {String|Roo.Element} el The container element.
13113      */
13114     el : '',
13115     
13116     /**
13117      * @cfg {String|Roo.Template} tpl The template used by this View 
13118      */
13119     tpl : false,
13120     /**
13121      * @cfg {String} dataName the named area of the template to use as the data area
13122      *                          Works with domtemplates roo-name="name"
13123      */
13124     dataName: false,
13125     /**
13126      * @cfg {String} selectedClass The css class to add to selected nodes
13127      */
13128     selectedClass : "x-view-selected",
13129      /**
13130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13131      */
13132     emptyText : "",
13133     
13134     /**
13135      * @cfg {String} text to display on mask (default Loading)
13136      */
13137     mask : false,
13138     /**
13139      * @cfg {Boolean} multiSelect Allow multiple selection
13140      */
13141     multiSelect : false,
13142     /**
13143      * @cfg {Boolean} singleSelect Allow single selection
13144      */
13145     singleSelect:  false,
13146     
13147     /**
13148      * @cfg {Boolean} toggleSelect - selecting 
13149      */
13150     toggleSelect : false,
13151     
13152     /**
13153      * @cfg {Boolean} tickable - selecting 
13154      */
13155     tickable : false,
13156     
13157     /**
13158      * Returns the element this view is bound to.
13159      * @return {Roo.Element}
13160      */
13161     getEl : function(){
13162         return this.wrapEl;
13163     },
13164     
13165     
13166
13167     /**
13168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13169      */
13170     refresh : function(){
13171         //Roo.log('refresh');
13172         var t = this.tpl;
13173         
13174         // if we are using something like 'domtemplate', then
13175         // the what gets used is:
13176         // t.applySubtemplate(NAME, data, wrapping data..)
13177         // the outer template then get' applied with
13178         //     the store 'extra data'
13179         // and the body get's added to the
13180         //      roo-name="data" node?
13181         //      <span class='roo-tpl-{name}'></span> ?????
13182         
13183         
13184         
13185         this.clearSelections();
13186         this.el.update("");
13187         var html = [];
13188         var records = this.store.getRange();
13189         if(records.length < 1) {
13190             
13191             // is this valid??  = should it render a template??
13192             
13193             this.el.update(this.emptyText);
13194             return;
13195         }
13196         var el = this.el;
13197         if (this.dataName) {
13198             this.el.update(t.apply(this.store.meta)); //????
13199             el = this.el.child('.roo-tpl-' + this.dataName);
13200         }
13201         
13202         for(var i = 0, len = records.length; i < len; i++){
13203             var data = this.prepareData(records[i].data, i, records[i]);
13204             this.fireEvent("preparedata", this, data, i, records[i]);
13205             
13206             var d = Roo.apply({}, data);
13207             
13208             if(this.tickable){
13209                 Roo.apply(d, {'roo-id' : Roo.id()});
13210                 
13211                 var _this = this;
13212             
13213                 Roo.each(this.parent.item, function(item){
13214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13215                         return;
13216                     }
13217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13218                 });
13219             }
13220             
13221             html[html.length] = Roo.util.Format.trim(
13222                 this.dataName ?
13223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13224                     t.apply(d)
13225             );
13226         }
13227         
13228         
13229         
13230         el.update(html.join(""));
13231         this.nodes = el.dom.childNodes;
13232         this.updateIndexes(0);
13233     },
13234     
13235
13236     /**
13237      * Function to override to reformat the data that is sent to
13238      * the template for each node.
13239      * DEPRICATED - use the preparedata event handler.
13240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13241      * a JSON object for an UpdateManager bound view).
13242      */
13243     prepareData : function(data, index, record)
13244     {
13245         this.fireEvent("preparedata", this, data, index, record);
13246         return data;
13247     },
13248
13249     onUpdate : function(ds, record){
13250         // Roo.log('on update');   
13251         this.clearSelections();
13252         var index = this.store.indexOf(record);
13253         var n = this.nodes[index];
13254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13255         n.parentNode.removeChild(n);
13256         this.updateIndexes(index, index);
13257     },
13258
13259     
13260     
13261 // --------- FIXME     
13262     onAdd : function(ds, records, index)
13263     {
13264         //Roo.log(['on Add', ds, records, index] );        
13265         this.clearSelections();
13266         if(this.nodes.length == 0){
13267             this.refresh();
13268             return;
13269         }
13270         var n = this.nodes[index];
13271         for(var i = 0, len = records.length; i < len; i++){
13272             var d = this.prepareData(records[i].data, i, records[i]);
13273             if(n){
13274                 this.tpl.insertBefore(n, d);
13275             }else{
13276                 
13277                 this.tpl.append(this.el, d);
13278             }
13279         }
13280         this.updateIndexes(index);
13281     },
13282
13283     onRemove : function(ds, record, index){
13284        // Roo.log('onRemove');
13285         this.clearSelections();
13286         var el = this.dataName  ?
13287             this.el.child('.roo-tpl-' + this.dataName) :
13288             this.el; 
13289         
13290         el.dom.removeChild(this.nodes[index]);
13291         this.updateIndexes(index);
13292     },
13293
13294     /**
13295      * Refresh an individual node.
13296      * @param {Number} index
13297      */
13298     refreshNode : function(index){
13299         this.onUpdate(this.store, this.store.getAt(index));
13300     },
13301
13302     updateIndexes : function(startIndex, endIndex){
13303         var ns = this.nodes;
13304         startIndex = startIndex || 0;
13305         endIndex = endIndex || ns.length - 1;
13306         for(var i = startIndex; i <= endIndex; i++){
13307             ns[i].nodeIndex = i;
13308         }
13309     },
13310
13311     /**
13312      * Changes the data store this view uses and refresh the view.
13313      * @param {Store} store
13314      */
13315     setStore : function(store, initial){
13316         if(!initial && this.store){
13317             this.store.un("datachanged", this.refresh);
13318             this.store.un("add", this.onAdd);
13319             this.store.un("remove", this.onRemove);
13320             this.store.un("update", this.onUpdate);
13321             this.store.un("clear", this.refresh);
13322             this.store.un("beforeload", this.onBeforeLoad);
13323             this.store.un("load", this.onLoad);
13324             this.store.un("loadexception", this.onLoad);
13325         }
13326         if(store){
13327           
13328             store.on("datachanged", this.refresh, this);
13329             store.on("add", this.onAdd, this);
13330             store.on("remove", this.onRemove, this);
13331             store.on("update", this.onUpdate, this);
13332             store.on("clear", this.refresh, this);
13333             store.on("beforeload", this.onBeforeLoad, this);
13334             store.on("load", this.onLoad, this);
13335             store.on("loadexception", this.onLoad, this);
13336         }
13337         
13338         if(store){
13339             this.refresh();
13340         }
13341     },
13342     /**
13343      * onbeforeLoad - masks the loading area.
13344      *
13345      */
13346     onBeforeLoad : function(store,opts)
13347     {
13348          //Roo.log('onBeforeLoad');   
13349         if (!opts.add) {
13350             this.el.update("");
13351         }
13352         this.el.mask(this.mask ? this.mask : "Loading" ); 
13353     },
13354     onLoad : function ()
13355     {
13356         this.el.unmask();
13357     },
13358     
13359
13360     /**
13361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13362      * @param {HTMLElement} node
13363      * @return {HTMLElement} The template node
13364      */
13365     findItemFromChild : function(node){
13366         var el = this.dataName  ?
13367             this.el.child('.roo-tpl-' + this.dataName,true) :
13368             this.el.dom; 
13369         
13370         if(!node || node.parentNode == el){
13371                     return node;
13372             }
13373             var p = node.parentNode;
13374             while(p && p != el){
13375             if(p.parentNode == el){
13376                 return p;
13377             }
13378             p = p.parentNode;
13379         }
13380             return null;
13381     },
13382
13383     /** @ignore */
13384     onClick : function(e){
13385         var item = this.findItemFromChild(e.getTarget());
13386         if(item){
13387             var index = this.indexOf(item);
13388             if(this.onItemClick(item, index, e) !== false){
13389                 this.fireEvent("click", this, index, item, e);
13390             }
13391         }else{
13392             this.clearSelections();
13393         }
13394     },
13395
13396     /** @ignore */
13397     onContextMenu : function(e){
13398         var item = this.findItemFromChild(e.getTarget());
13399         if(item){
13400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13401         }
13402     },
13403
13404     /** @ignore */
13405     onDblClick : function(e){
13406         var item = this.findItemFromChild(e.getTarget());
13407         if(item){
13408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13409         }
13410     },
13411
13412     onItemClick : function(item, index, e)
13413     {
13414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13415             return false;
13416         }
13417         if (this.toggleSelect) {
13418             var m = this.isSelected(item) ? 'unselect' : 'select';
13419             //Roo.log(m);
13420             var _t = this;
13421             _t[m](item, true, false);
13422             return true;
13423         }
13424         if(this.multiSelect || this.singleSelect){
13425             if(this.multiSelect && e.shiftKey && this.lastSelection){
13426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13427             }else{
13428                 this.select(item, this.multiSelect && e.ctrlKey);
13429                 this.lastSelection = item;
13430             }
13431             
13432             if(!this.tickable){
13433                 e.preventDefault();
13434             }
13435             
13436         }
13437         return true;
13438     },
13439
13440     /**
13441      * Get the number of selected nodes.
13442      * @return {Number}
13443      */
13444     getSelectionCount : function(){
13445         return this.selections.length;
13446     },
13447
13448     /**
13449      * Get the currently selected nodes.
13450      * @return {Array} An array of HTMLElements
13451      */
13452     getSelectedNodes : function(){
13453         return this.selections;
13454     },
13455
13456     /**
13457      * Get the indexes of the selected nodes.
13458      * @return {Array}
13459      */
13460     getSelectedIndexes : function(){
13461         var indexes = [], s = this.selections;
13462         for(var i = 0, len = s.length; i < len; i++){
13463             indexes.push(s[i].nodeIndex);
13464         }
13465         return indexes;
13466     },
13467
13468     /**
13469      * Clear all selections
13470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13471      */
13472     clearSelections : function(suppressEvent){
13473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13474             this.cmp.elements = this.selections;
13475             this.cmp.removeClass(this.selectedClass);
13476             this.selections = [];
13477             if(!suppressEvent){
13478                 this.fireEvent("selectionchange", this, this.selections);
13479             }
13480         }
13481     },
13482
13483     /**
13484      * Returns true if the passed node is selected
13485      * @param {HTMLElement/Number} node The node or node index
13486      * @return {Boolean}
13487      */
13488     isSelected : function(node){
13489         var s = this.selections;
13490         if(s.length < 1){
13491             return false;
13492         }
13493         node = this.getNode(node);
13494         return s.indexOf(node) !== -1;
13495     },
13496
13497     /**
13498      * Selects nodes.
13499      * @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
13500      * @param {Boolean} keepExisting (optional) true to keep existing selections
13501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13502      */
13503     select : function(nodeInfo, keepExisting, suppressEvent){
13504         if(nodeInfo instanceof Array){
13505             if(!keepExisting){
13506                 this.clearSelections(true);
13507             }
13508             for(var i = 0, len = nodeInfo.length; i < len; i++){
13509                 this.select(nodeInfo[i], true, true);
13510             }
13511             return;
13512         } 
13513         var node = this.getNode(nodeInfo);
13514         if(!node || this.isSelected(node)){
13515             return; // already selected.
13516         }
13517         if(!keepExisting){
13518             this.clearSelections(true);
13519         }
13520         
13521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13522             Roo.fly(node).addClass(this.selectedClass);
13523             this.selections.push(node);
13524             if(!suppressEvent){
13525                 this.fireEvent("selectionchange", this, this.selections);
13526             }
13527         }
13528         
13529         
13530     },
13531       /**
13532      * Unselects nodes.
13533      * @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
13534      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13536      */
13537     unselect : function(nodeInfo, keepExisting, suppressEvent)
13538     {
13539         if(nodeInfo instanceof Array){
13540             Roo.each(this.selections, function(s) {
13541                 this.unselect(s, nodeInfo);
13542             }, this);
13543             return;
13544         }
13545         var node = this.getNode(nodeInfo);
13546         if(!node || !this.isSelected(node)){
13547             //Roo.log("not selected");
13548             return; // not selected.
13549         }
13550         // fireevent???
13551         var ns = [];
13552         Roo.each(this.selections, function(s) {
13553             if (s == node ) {
13554                 Roo.fly(node).removeClass(this.selectedClass);
13555
13556                 return;
13557             }
13558             ns.push(s);
13559         },this);
13560         
13561         this.selections= ns;
13562         this.fireEvent("selectionchange", this, this.selections);
13563     },
13564
13565     /**
13566      * Gets a template node.
13567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13568      * @return {HTMLElement} The node or null if it wasn't found
13569      */
13570     getNode : function(nodeInfo){
13571         if(typeof nodeInfo == "string"){
13572             return document.getElementById(nodeInfo);
13573         }else if(typeof nodeInfo == "number"){
13574             return this.nodes[nodeInfo];
13575         }
13576         return nodeInfo;
13577     },
13578
13579     /**
13580      * Gets a range template nodes.
13581      * @param {Number} startIndex
13582      * @param {Number} endIndex
13583      * @return {Array} An array of nodes
13584      */
13585     getNodes : function(start, end){
13586         var ns = this.nodes;
13587         start = start || 0;
13588         end = typeof end == "undefined" ? ns.length - 1 : end;
13589         var nodes = [];
13590         if(start <= end){
13591             for(var i = start; i <= end; i++){
13592                 nodes.push(ns[i]);
13593             }
13594         } else{
13595             for(var i = start; i >= end; i--){
13596                 nodes.push(ns[i]);
13597             }
13598         }
13599         return nodes;
13600     },
13601
13602     /**
13603      * Finds the index of the passed node
13604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13605      * @return {Number} The index of the node or -1
13606      */
13607     indexOf : function(node){
13608         node = this.getNode(node);
13609         if(typeof node.nodeIndex == "number"){
13610             return node.nodeIndex;
13611         }
13612         var ns = this.nodes;
13613         for(var i = 0, len = ns.length; i < len; i++){
13614             if(ns[i] == node){
13615                 return i;
13616             }
13617         }
13618         return -1;
13619     }
13620 });
13621 /*
13622  * - LGPL
13623  *
13624  * based on jquery fullcalendar
13625  * 
13626  */
13627
13628 Roo.bootstrap = Roo.bootstrap || {};
13629 /**
13630  * @class Roo.bootstrap.Calendar
13631  * @extends Roo.bootstrap.Component
13632  * Bootstrap Calendar class
13633  * @cfg {Boolean} loadMask (true|false) default false
13634  * @cfg {Object} header generate the user specific header of the calendar, default false
13635
13636  * @constructor
13637  * Create a new Container
13638  * @param {Object} config The config object
13639  */
13640
13641
13642
13643 Roo.bootstrap.Calendar = function(config){
13644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13645      this.addEvents({
13646         /**
13647              * @event select
13648              * Fires when a date is selected
13649              * @param {DatePicker} this
13650              * @param {Date} date The selected date
13651              */
13652         'select': true,
13653         /**
13654              * @event monthchange
13655              * Fires when the displayed month changes 
13656              * @param {DatePicker} this
13657              * @param {Date} date The selected month
13658              */
13659         'monthchange': true,
13660         /**
13661              * @event evententer
13662              * Fires when mouse over an event
13663              * @param {Calendar} this
13664              * @param {event} Event
13665              */
13666         'evententer': true,
13667         /**
13668              * @event eventleave
13669              * Fires when the mouse leaves an
13670              * @param {Calendar} this
13671              * @param {event}
13672              */
13673         'eventleave': true,
13674         /**
13675              * @event eventclick
13676              * Fires when the mouse click an
13677              * @param {Calendar} this
13678              * @param {event}
13679              */
13680         'eventclick': true
13681         
13682     });
13683
13684 };
13685
13686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13687     
13688      /**
13689      * @cfg {Number} startDay
13690      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13691      */
13692     startDay : 0,
13693     
13694     loadMask : false,
13695     
13696     header : false,
13697       
13698     getAutoCreate : function(){
13699         
13700         
13701         var fc_button = function(name, corner, style, content ) {
13702             return Roo.apply({},{
13703                 tag : 'span',
13704                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13705                          (corner.length ?
13706                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13707                             ''
13708                         ),
13709                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13710                 unselectable: 'on'
13711             });
13712         };
13713         
13714         var header = {};
13715         
13716         if(!this.header){
13717             header = {
13718                 tag : 'table',
13719                 cls : 'fc-header',
13720                 style : 'width:100%',
13721                 cn : [
13722                     {
13723                         tag: 'tr',
13724                         cn : [
13725                             {
13726                                 tag : 'td',
13727                                 cls : 'fc-header-left',
13728                                 cn : [
13729                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13730                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13731                                     { tag: 'span', cls: 'fc-header-space' },
13732                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13733
13734
13735                                 ]
13736                             },
13737
13738                             {
13739                                 tag : 'td',
13740                                 cls : 'fc-header-center',
13741                                 cn : [
13742                                     {
13743                                         tag: 'span',
13744                                         cls: 'fc-header-title',
13745                                         cn : {
13746                                             tag: 'H2',
13747                                             html : 'month / year'
13748                                         }
13749                                     }
13750
13751                                 ]
13752                             },
13753                             {
13754                                 tag : 'td',
13755                                 cls : 'fc-header-right',
13756                                 cn : [
13757                               /*      fc_button('month', 'left', '', 'month' ),
13758                                     fc_button('week', '', '', 'week' ),
13759                                     fc_button('day', 'right', '', 'day' )
13760                                 */    
13761
13762                                 ]
13763                             }
13764
13765                         ]
13766                     }
13767                 ]
13768             };
13769         }
13770         
13771         header = this.header;
13772         
13773        
13774         var cal_heads = function() {
13775             var ret = [];
13776             // fixme - handle this.
13777             
13778             for (var i =0; i < Date.dayNames.length; i++) {
13779                 var d = Date.dayNames[i];
13780                 ret.push({
13781                     tag: 'th',
13782                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13783                     html : d.substring(0,3)
13784                 });
13785                 
13786             }
13787             ret[0].cls += ' fc-first';
13788             ret[6].cls += ' fc-last';
13789             return ret;
13790         };
13791         var cal_cell = function(n) {
13792             return  {
13793                 tag: 'td',
13794                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13795                 cn : [
13796                     {
13797                         cn : [
13798                             {
13799                                 cls: 'fc-day-number',
13800                                 html: 'D'
13801                             },
13802                             {
13803                                 cls: 'fc-day-content',
13804                              
13805                                 cn : [
13806                                      {
13807                                         style: 'position: relative;' // height: 17px;
13808                                     }
13809                                 ]
13810                             }
13811                             
13812                             
13813                         ]
13814                     }
13815                 ]
13816                 
13817             }
13818         };
13819         var cal_rows = function() {
13820             
13821             var ret = [];
13822             for (var r = 0; r < 6; r++) {
13823                 var row= {
13824                     tag : 'tr',
13825                     cls : 'fc-week',
13826                     cn : []
13827                 };
13828                 
13829                 for (var i =0; i < Date.dayNames.length; i++) {
13830                     var d = Date.dayNames[i];
13831                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13832
13833                 }
13834                 row.cn[0].cls+=' fc-first';
13835                 row.cn[0].cn[0].style = 'min-height:90px';
13836                 row.cn[6].cls+=' fc-last';
13837                 ret.push(row);
13838                 
13839             }
13840             ret[0].cls += ' fc-first';
13841             ret[4].cls += ' fc-prev-last';
13842             ret[5].cls += ' fc-last';
13843             return ret;
13844             
13845         };
13846         
13847         var cal_table = {
13848             tag: 'table',
13849             cls: 'fc-border-separate',
13850             style : 'width:100%',
13851             cellspacing  : 0,
13852             cn : [
13853                 { 
13854                     tag: 'thead',
13855                     cn : [
13856                         { 
13857                             tag: 'tr',
13858                             cls : 'fc-first fc-last',
13859                             cn : cal_heads()
13860                         }
13861                     ]
13862                 },
13863                 { 
13864                     tag: 'tbody',
13865                     cn : cal_rows()
13866                 }
13867                   
13868             ]
13869         };
13870          
13871          var cfg = {
13872             cls : 'fc fc-ltr',
13873             cn : [
13874                 header,
13875                 {
13876                     cls : 'fc-content',
13877                     style : "position: relative;",
13878                     cn : [
13879                         {
13880                             cls : 'fc-view fc-view-month fc-grid',
13881                             style : 'position: relative',
13882                             unselectable : 'on',
13883                             cn : [
13884                                 {
13885                                     cls : 'fc-event-container',
13886                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13887                                 },
13888                                 cal_table
13889                             ]
13890                         }
13891                     ]
13892     
13893                 }
13894            ] 
13895             
13896         };
13897         
13898          
13899         
13900         return cfg;
13901     },
13902     
13903     
13904     initEvents : function()
13905     {
13906         if(!this.store){
13907             throw "can not find store for calendar";
13908         }
13909         
13910         var mark = {
13911             tag: "div",
13912             cls:"x-dlg-mask",
13913             style: "text-align:center",
13914             cn: [
13915                 {
13916                     tag: "div",
13917                     style: "background-color:white;width:50%;margin:250 auto",
13918                     cn: [
13919                         {
13920                             tag: "img",
13921                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13922                         },
13923                         {
13924                             tag: "span",
13925                             html: "Loading"
13926                         }
13927                         
13928                     ]
13929                 }
13930             ]
13931         }
13932         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13933         
13934         var size = this.el.select('.fc-content', true).first().getSize();
13935         this.maskEl.setSize(size.width, size.height);
13936         this.maskEl.enableDisplayMode("block");
13937         if(!this.loadMask){
13938             this.maskEl.hide();
13939         }
13940         
13941         this.store = Roo.factory(this.store, Roo.data);
13942         this.store.on('load', this.onLoad, this);
13943         this.store.on('beforeload', this.onBeforeLoad, this);
13944         
13945         this.resize();
13946         
13947         this.cells = this.el.select('.fc-day',true);
13948         //Roo.log(this.cells);
13949         this.textNodes = this.el.query('.fc-day-number');
13950         this.cells.addClassOnOver('fc-state-hover');
13951         
13952         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13953         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13954         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13955         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13956         
13957         this.on('monthchange', this.onMonthChange, this);
13958         
13959         this.update(new Date().clearTime());
13960     },
13961     
13962     resize : function() {
13963         var sz  = this.el.getSize();
13964         
13965         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13966         this.el.select('.fc-day-content div',true).setHeight(34);
13967     },
13968     
13969     
13970     // private
13971     showPrevMonth : function(e){
13972         this.update(this.activeDate.add("mo", -1));
13973     },
13974     showToday : function(e){
13975         this.update(new Date().clearTime());
13976     },
13977     // private
13978     showNextMonth : function(e){
13979         this.update(this.activeDate.add("mo", 1));
13980     },
13981
13982     // private
13983     showPrevYear : function(){
13984         this.update(this.activeDate.add("y", -1));
13985     },
13986
13987     // private
13988     showNextYear : function(){
13989         this.update(this.activeDate.add("y", 1));
13990     },
13991
13992     
13993    // private
13994     update : function(date)
13995     {
13996         var vd = this.activeDate;
13997         this.activeDate = date;
13998 //        if(vd && this.el){
13999 //            var t = date.getTime();
14000 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14001 //                Roo.log('using add remove');
14002 //                
14003 //                this.fireEvent('monthchange', this, date);
14004 //                
14005 //                this.cells.removeClass("fc-state-highlight");
14006 //                this.cells.each(function(c){
14007 //                   if(c.dateValue == t){
14008 //                       c.addClass("fc-state-highlight");
14009 //                       setTimeout(function(){
14010 //                            try{c.dom.firstChild.focus();}catch(e){}
14011 //                       }, 50);
14012 //                       return false;
14013 //                   }
14014 //                   return true;
14015 //                });
14016 //                return;
14017 //            }
14018 //        }
14019         
14020         var days = date.getDaysInMonth();
14021         
14022         var firstOfMonth = date.getFirstDateOfMonth();
14023         var startingPos = firstOfMonth.getDay()-this.startDay;
14024         
14025         if(startingPos < this.startDay){
14026             startingPos += 7;
14027         }
14028         
14029         var pm = date.add(Date.MONTH, -1);
14030         var prevStart = pm.getDaysInMonth()-startingPos;
14031 //        
14032         this.cells = this.el.select('.fc-day',true);
14033         this.textNodes = this.el.query('.fc-day-number');
14034         this.cells.addClassOnOver('fc-state-hover');
14035         
14036         var cells = this.cells.elements;
14037         var textEls = this.textNodes;
14038         
14039         Roo.each(cells, function(cell){
14040             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14041         });
14042         
14043         days += startingPos;
14044
14045         // convert everything to numbers so it's fast
14046         var day = 86400000;
14047         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14048         //Roo.log(d);
14049         //Roo.log(pm);
14050         //Roo.log(prevStart);
14051         
14052         var today = new Date().clearTime().getTime();
14053         var sel = date.clearTime().getTime();
14054         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14055         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14056         var ddMatch = this.disabledDatesRE;
14057         var ddText = this.disabledDatesText;
14058         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14059         var ddaysText = this.disabledDaysText;
14060         var format = this.format;
14061         
14062         var setCellClass = function(cal, cell){
14063             cell.row = 0;
14064             cell.events = [];
14065             cell.more = [];
14066             //Roo.log('set Cell Class');
14067             cell.title = "";
14068             var t = d.getTime();
14069             
14070             //Roo.log(d);
14071             
14072             cell.dateValue = t;
14073             if(t == today){
14074                 cell.className += " fc-today";
14075                 cell.className += " fc-state-highlight";
14076                 cell.title = cal.todayText;
14077             }
14078             if(t == sel){
14079                 // disable highlight in other month..
14080                 //cell.className += " fc-state-highlight";
14081                 
14082             }
14083             // disabling
14084             if(t < min) {
14085                 cell.className = " fc-state-disabled";
14086                 cell.title = cal.minText;
14087                 return;
14088             }
14089             if(t > max) {
14090                 cell.className = " fc-state-disabled";
14091                 cell.title = cal.maxText;
14092                 return;
14093             }
14094             if(ddays){
14095                 if(ddays.indexOf(d.getDay()) != -1){
14096                     cell.title = ddaysText;
14097                     cell.className = " fc-state-disabled";
14098                 }
14099             }
14100             if(ddMatch && format){
14101                 var fvalue = d.dateFormat(format);
14102                 if(ddMatch.test(fvalue)){
14103                     cell.title = ddText.replace("%0", fvalue);
14104                     cell.className = " fc-state-disabled";
14105                 }
14106             }
14107             
14108             if (!cell.initialClassName) {
14109                 cell.initialClassName = cell.dom.className;
14110             }
14111             
14112             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14113         };
14114
14115         var i = 0;
14116         
14117         for(; i < startingPos; i++) {
14118             textEls[i].innerHTML = (++prevStart);
14119             d.setDate(d.getDate()+1);
14120             
14121             cells[i].className = "fc-past fc-other-month";
14122             setCellClass(this, cells[i]);
14123         }
14124         
14125         var intDay = 0;
14126         
14127         for(; i < days; i++){
14128             intDay = i - startingPos + 1;
14129             textEls[i].innerHTML = (intDay);
14130             d.setDate(d.getDate()+1);
14131             
14132             cells[i].className = ''; // "x-date-active";
14133             setCellClass(this, cells[i]);
14134         }
14135         var extraDays = 0;
14136         
14137         for(; i < 42; i++) {
14138             textEls[i].innerHTML = (++extraDays);
14139             d.setDate(d.getDate()+1);
14140             
14141             cells[i].className = "fc-future fc-other-month";
14142             setCellClass(this, cells[i]);
14143         }
14144         
14145         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14146         
14147         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14148         
14149         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14150         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14151         
14152         if(totalRows != 6){
14153             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14154             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14155         }
14156         
14157         this.fireEvent('monthchange', this, date);
14158         
14159         
14160         /*
14161         if(!this.internalRender){
14162             var main = this.el.dom.firstChild;
14163             var w = main.offsetWidth;
14164             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14165             Roo.fly(main).setWidth(w);
14166             this.internalRender = true;
14167             // opera does not respect the auto grow header center column
14168             // then, after it gets a width opera refuses to recalculate
14169             // without a second pass
14170             if(Roo.isOpera && !this.secondPass){
14171                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14172                 this.secondPass = true;
14173                 this.update.defer(10, this, [date]);
14174             }
14175         }
14176         */
14177         
14178     },
14179     
14180     findCell : function(dt) {
14181         dt = dt.clearTime().getTime();
14182         var ret = false;
14183         this.cells.each(function(c){
14184             //Roo.log("check " +c.dateValue + '?=' + dt);
14185             if(c.dateValue == dt){
14186                 ret = c;
14187                 return false;
14188             }
14189             return true;
14190         });
14191         
14192         return ret;
14193     },
14194     
14195     findCells : function(ev) {
14196         var s = ev.start.clone().clearTime().getTime();
14197        // Roo.log(s);
14198         var e= ev.end.clone().clearTime().getTime();
14199        // Roo.log(e);
14200         var ret = [];
14201         this.cells.each(function(c){
14202              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14203             
14204             if(c.dateValue > e){
14205                 return ;
14206             }
14207             if(c.dateValue < s){
14208                 return ;
14209             }
14210             ret.push(c);
14211         });
14212         
14213         return ret;    
14214     },
14215     
14216 //    findBestRow: function(cells)
14217 //    {
14218 //        var ret = 0;
14219 //        
14220 //        for (var i =0 ; i < cells.length;i++) {
14221 //            ret  = Math.max(cells[i].rows || 0,ret);
14222 //        }
14223 //        return ret;
14224 //        
14225 //    },
14226     
14227     
14228     addItem : function(ev)
14229     {
14230         // look for vertical location slot in
14231         var cells = this.findCells(ev);
14232         
14233 //        ev.row = this.findBestRow(cells);
14234         
14235         // work out the location.
14236         
14237         var crow = false;
14238         var rows = [];
14239         for(var i =0; i < cells.length; i++) {
14240             
14241             cells[i].row = cells[0].row;
14242             
14243             if(i == 0){
14244                 cells[i].row = cells[i].row + 1;
14245             }
14246             
14247             if (!crow) {
14248                 crow = {
14249                     start : cells[i],
14250                     end :  cells[i]
14251                 };
14252                 continue;
14253             }
14254             if (crow.start.getY() == cells[i].getY()) {
14255                 // on same row.
14256                 crow.end = cells[i];
14257                 continue;
14258             }
14259             // different row.
14260             rows.push(crow);
14261             crow = {
14262                 start: cells[i],
14263                 end : cells[i]
14264             };
14265             
14266         }
14267         
14268         rows.push(crow);
14269         ev.els = [];
14270         ev.rows = rows;
14271         ev.cells = cells;
14272         
14273         cells[0].events.push(ev);
14274         
14275         this.calevents.push(ev);
14276     },
14277     
14278     clearEvents: function() {
14279         
14280         if(!this.calevents){
14281             return;
14282         }
14283         
14284         Roo.each(this.cells.elements, function(c){
14285             c.row = 0;
14286             c.events = [];
14287             c.more = [];
14288         });
14289         
14290         Roo.each(this.calevents, function(e) {
14291             Roo.each(e.els, function(el) {
14292                 el.un('mouseenter' ,this.onEventEnter, this);
14293                 el.un('mouseleave' ,this.onEventLeave, this);
14294                 el.remove();
14295             },this);
14296         },this);
14297         
14298         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14299             e.remove();
14300         });
14301         
14302     },
14303     
14304     renderEvents: function()
14305     {   
14306         var _this = this;
14307         
14308         this.cells.each(function(c) {
14309             
14310             if(c.row < 5){
14311                 return;
14312             }
14313             
14314             var ev = c.events;
14315             
14316             var r = 4;
14317             if(c.row != c.events.length){
14318                 r = 4 - (4 - (c.row - c.events.length));
14319             }
14320             
14321             c.events = ev.slice(0, r);
14322             c.more = ev.slice(r);
14323             
14324             if(c.more.length && c.more.length == 1){
14325                 c.events.push(c.more.pop());
14326             }
14327             
14328             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14329             
14330         });
14331             
14332         this.cells.each(function(c) {
14333             
14334             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14335             
14336             
14337             for (var e = 0; e < c.events.length; e++){
14338                 var ev = c.events[e];
14339                 var rows = ev.rows;
14340                 
14341                 for(var i = 0; i < rows.length; i++) {
14342                 
14343                     // how many rows should it span..
14344
14345                     var  cfg = {
14346                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14347                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14348
14349                         unselectable : "on",
14350                         cn : [
14351                             {
14352                                 cls: 'fc-event-inner',
14353                                 cn : [
14354     //                                {
14355     //                                  tag:'span',
14356     //                                  cls: 'fc-event-time',
14357     //                                  html : cells.length > 1 ? '' : ev.time
14358     //                                },
14359                                     {
14360                                       tag:'span',
14361                                       cls: 'fc-event-title',
14362                                       html : String.format('{0}', ev.title)
14363                                     }
14364
14365
14366                                 ]
14367                             },
14368                             {
14369                                 cls: 'ui-resizable-handle ui-resizable-e',
14370                                 html : '&nbsp;&nbsp;&nbsp'
14371                             }
14372
14373                         ]
14374                     };
14375
14376                     if (i == 0) {
14377                         cfg.cls += ' fc-event-start';
14378                     }
14379                     if ((i+1) == rows.length) {
14380                         cfg.cls += ' fc-event-end';
14381                     }
14382
14383                     var ctr = _this.el.select('.fc-event-container',true).first();
14384                     var cg = ctr.createChild(cfg);
14385
14386                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14387                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14388
14389                     var r = (c.more.length) ? 1 : 0;
14390                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14391                     cg.setWidth(ebox.right - sbox.x -2);
14392
14393                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14394                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14395                     cg.on('click', _this.onEventClick, _this, ev);
14396
14397                     ev.els.push(cg);
14398                     
14399                 }
14400                 
14401             }
14402             
14403             
14404             if(c.more.length){
14405                 var  cfg = {
14406                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14407                     style : 'position: absolute',
14408                     unselectable : "on",
14409                     cn : [
14410                         {
14411                             cls: 'fc-event-inner',
14412                             cn : [
14413                                 {
14414                                   tag:'span',
14415                                   cls: 'fc-event-title',
14416                                   html : 'More'
14417                                 }
14418
14419
14420                             ]
14421                         },
14422                         {
14423                             cls: 'ui-resizable-handle ui-resizable-e',
14424                             html : '&nbsp;&nbsp;&nbsp'
14425                         }
14426
14427                     ]
14428                 };
14429
14430                 var ctr = _this.el.select('.fc-event-container',true).first();
14431                 var cg = ctr.createChild(cfg);
14432
14433                 var sbox = c.select('.fc-day-content',true).first().getBox();
14434                 var ebox = c.select('.fc-day-content',true).first().getBox();
14435                 //Roo.log(cg);
14436                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14437                 cg.setWidth(ebox.right - sbox.x -2);
14438
14439                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14440                 
14441             }
14442             
14443         });
14444         
14445         
14446         
14447     },
14448     
14449     onEventEnter: function (e, el,event,d) {
14450         this.fireEvent('evententer', this, el, event);
14451     },
14452     
14453     onEventLeave: function (e, el,event,d) {
14454         this.fireEvent('eventleave', this, el, event);
14455     },
14456     
14457     onEventClick: function (e, el,event,d) {
14458         this.fireEvent('eventclick', this, el, event);
14459     },
14460     
14461     onMonthChange: function () {
14462         this.store.load();
14463     },
14464     
14465     onMoreEventClick: function(e, el, more)
14466     {
14467         var _this = this;
14468         
14469         this.calpopover.placement = 'right';
14470         this.calpopover.setTitle('More');
14471         
14472         this.calpopover.setContent('');
14473         
14474         var ctr = this.calpopover.el.select('.popover-content', true).first();
14475         
14476         Roo.each(more, function(m){
14477             var cfg = {
14478                 cls : 'fc-event-hori fc-event-draggable',
14479                 html : m.title
14480             }
14481             var cg = ctr.createChild(cfg);
14482             
14483             cg.on('click', _this.onEventClick, _this, m);
14484         });
14485         
14486         this.calpopover.show(el);
14487         
14488         
14489     },
14490     
14491     onLoad: function () 
14492     {   
14493         this.calevents = [];
14494         var cal = this;
14495         
14496         if(this.store.getCount() > 0){
14497             this.store.data.each(function(d){
14498                cal.addItem({
14499                     id : d.data.id,
14500                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14501                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14502                     time : d.data.start_time,
14503                     title : d.data.title,
14504                     description : d.data.description,
14505                     venue : d.data.venue
14506                 });
14507             });
14508         }
14509         
14510         this.renderEvents();
14511         
14512         if(this.calevents.length && this.loadMask){
14513             this.maskEl.hide();
14514         }
14515     },
14516     
14517     onBeforeLoad: function()
14518     {
14519         this.clearEvents();
14520         if(this.loadMask){
14521             this.maskEl.show();
14522         }
14523     }
14524 });
14525
14526  
14527  /*
14528  * - LGPL
14529  *
14530  * element
14531  * 
14532  */
14533
14534 /**
14535  * @class Roo.bootstrap.Popover
14536  * @extends Roo.bootstrap.Component
14537  * Bootstrap Popover class
14538  * @cfg {String} html contents of the popover   (or false to use children..)
14539  * @cfg {String} title of popover (or false to hide)
14540  * @cfg {String} placement how it is placed
14541  * @cfg {String} trigger click || hover (or false to trigger manually)
14542  * @cfg {String} over what (parent or false to trigger manually.)
14543  * @cfg {Number} delay - delay before showing
14544  
14545  * @constructor
14546  * Create a new Popover
14547  * @param {Object} config The config object
14548  */
14549
14550 Roo.bootstrap.Popover = function(config){
14551     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14552 };
14553
14554 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14555     
14556     title: 'Fill in a title',
14557     html: false,
14558     
14559     placement : 'right',
14560     trigger : 'hover', // hover
14561     
14562     delay : 0,
14563     
14564     over: 'parent',
14565     
14566     can_build_overlaid : false,
14567     
14568     getChildContainer : function()
14569     {
14570         return this.el.select('.popover-content',true).first();
14571     },
14572     
14573     getAutoCreate : function(){
14574          Roo.log('make popover?');
14575         var cfg = {
14576            cls : 'popover roo-dynamic',
14577            style: 'display:block',
14578            cn : [
14579                 {
14580                     cls : 'arrow'
14581                 },
14582                 {
14583                     cls : 'popover-inner',
14584                     cn : [
14585                         {
14586                             tag: 'h3',
14587                             cls: 'popover-title',
14588                             html : this.title
14589                         },
14590                         {
14591                             cls : 'popover-content',
14592                             html : this.html
14593                         }
14594                     ]
14595                     
14596                 }
14597            ]
14598         };
14599         
14600         return cfg;
14601     },
14602     setTitle: function(str)
14603     {
14604         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14605     },
14606     setContent: function(str)
14607     {
14608         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14609     },
14610     // as it get's added to the bottom of the page.
14611     onRender : function(ct, position)
14612     {
14613         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14614         if(!this.el){
14615             var cfg = Roo.apply({},  this.getAutoCreate());
14616             cfg.id = Roo.id();
14617             
14618             if (this.cls) {
14619                 cfg.cls += ' ' + this.cls;
14620             }
14621             if (this.style) {
14622                 cfg.style = this.style;
14623             }
14624             Roo.log("adding to ")
14625             this.el = Roo.get(document.body).createChild(cfg, position);
14626             Roo.log(this.el);
14627         }
14628         this.initEvents();
14629     },
14630     
14631     initEvents : function()
14632     {
14633         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14634         this.el.enableDisplayMode('block');
14635         this.el.hide();
14636         if (this.over === false) {
14637             return; 
14638         }
14639         if (this.triggers === false) {
14640             return;
14641         }
14642         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14643         var triggers = this.trigger ? this.trigger.split(' ') : [];
14644         Roo.each(triggers, function(trigger) {
14645         
14646             if (trigger == 'click') {
14647                 on_el.on('click', this.toggle, this);
14648             } else if (trigger != 'manual') {
14649                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14650                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14651       
14652                 on_el.on(eventIn  ,this.enter, this);
14653                 on_el.on(eventOut, this.leave, this);
14654             }
14655         }, this);
14656         
14657     },
14658     
14659     
14660     // private
14661     timeout : null,
14662     hoverState : null,
14663     
14664     toggle : function () {
14665         this.hoverState == 'in' ? this.leave() : this.enter();
14666     },
14667     
14668     enter : function () {
14669        
14670     
14671         clearTimeout(this.timeout);
14672     
14673         this.hoverState = 'in';
14674     
14675         if (!this.delay || !this.delay.show) {
14676             this.show();
14677             return;
14678         }
14679         var _t = this;
14680         this.timeout = setTimeout(function () {
14681             if (_t.hoverState == 'in') {
14682                 _t.show();
14683             }
14684         }, this.delay.show)
14685     },
14686     leave : function() {
14687         clearTimeout(this.timeout);
14688     
14689         this.hoverState = 'out';
14690     
14691         if (!this.delay || !this.delay.hide) {
14692             this.hide();
14693             return;
14694         }
14695         var _t = this;
14696         this.timeout = setTimeout(function () {
14697             if (_t.hoverState == 'out') {
14698                 _t.hide();
14699             }
14700         }, this.delay.hide)
14701     },
14702     
14703     show : function (on_el)
14704     {
14705         if (!on_el) {
14706             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14707         }
14708         // set content.
14709         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14710         if (this.html !== false) {
14711             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14712         }
14713         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14714         if (!this.title.length) {
14715             this.el.select('.popover-title',true).hide();
14716         }
14717         
14718         var placement = typeof this.placement == 'function' ?
14719             this.placement.call(this, this.el, on_el) :
14720             this.placement;
14721             
14722         var autoToken = /\s?auto?\s?/i;
14723         var autoPlace = autoToken.test(placement);
14724         if (autoPlace) {
14725             placement = placement.replace(autoToken, '') || 'top';
14726         }
14727         
14728         //this.el.detach()
14729         //this.el.setXY([0,0]);
14730         this.el.show();
14731         this.el.dom.style.display='block';
14732         this.el.addClass(placement);
14733         
14734         //this.el.appendTo(on_el);
14735         
14736         var p = this.getPosition();
14737         var box = this.el.getBox();
14738         
14739         if (autoPlace) {
14740             // fixme..
14741         }
14742         var align = Roo.bootstrap.Popover.alignment[placement];
14743         this.el.alignTo(on_el, align[0],align[1]);
14744         //var arrow = this.el.select('.arrow',true).first();
14745         //arrow.set(align[2], 
14746         
14747         this.el.addClass('in');
14748         this.hoverState = null;
14749         
14750         if (this.el.hasClass('fade')) {
14751             // fade it?
14752         }
14753         
14754     },
14755     hide : function()
14756     {
14757         this.el.setXY([0,0]);
14758         this.el.removeClass('in');
14759         this.el.hide();
14760         
14761     }
14762     
14763 });
14764
14765 Roo.bootstrap.Popover.alignment = {
14766     'left' : ['r-l', [-10,0], 'right'],
14767     'right' : ['l-r', [10,0], 'left'],
14768     'bottom' : ['t-b', [0,10], 'top'],
14769     'top' : [ 'b-t', [0,-10], 'bottom']
14770 };
14771
14772  /*
14773  * - LGPL
14774  *
14775  * Progress
14776  * 
14777  */
14778
14779 /**
14780  * @class Roo.bootstrap.Progress
14781  * @extends Roo.bootstrap.Component
14782  * Bootstrap Progress class
14783  * @cfg {Boolean} striped striped of the progress bar
14784  * @cfg {Boolean} active animated of the progress bar
14785  * 
14786  * 
14787  * @constructor
14788  * Create a new Progress
14789  * @param {Object} config The config object
14790  */
14791
14792 Roo.bootstrap.Progress = function(config){
14793     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14794 };
14795
14796 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14797     
14798     striped : false,
14799     active: false,
14800     
14801     getAutoCreate : function(){
14802         var cfg = {
14803             tag: 'div',
14804             cls: 'progress'
14805         };
14806         
14807         
14808         if(this.striped){
14809             cfg.cls += ' progress-striped';
14810         }
14811       
14812         if(this.active){
14813             cfg.cls += ' active';
14814         }
14815         
14816         
14817         return cfg;
14818     }
14819    
14820 });
14821
14822  
14823
14824  /*
14825  * - LGPL
14826  *
14827  * ProgressBar
14828  * 
14829  */
14830
14831 /**
14832  * @class Roo.bootstrap.ProgressBar
14833  * @extends Roo.bootstrap.Component
14834  * Bootstrap ProgressBar class
14835  * @cfg {Number} aria_valuenow aria-value now
14836  * @cfg {Number} aria_valuemin aria-value min
14837  * @cfg {Number} aria_valuemax aria-value max
14838  * @cfg {String} label label for the progress bar
14839  * @cfg {String} panel (success | info | warning | danger )
14840  * @cfg {String} role role of the progress bar
14841  * @cfg {String} sr_only text
14842  * 
14843  * 
14844  * @constructor
14845  * Create a new ProgressBar
14846  * @param {Object} config The config object
14847  */
14848
14849 Roo.bootstrap.ProgressBar = function(config){
14850     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14851 };
14852
14853 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14854     
14855     aria_valuenow : 0,
14856     aria_valuemin : 0,
14857     aria_valuemax : 100,
14858     label : false,
14859     panel : false,
14860     role : false,
14861     sr_only: false,
14862     
14863     getAutoCreate : function()
14864     {
14865         
14866         var cfg = {
14867             tag: 'div',
14868             cls: 'progress-bar',
14869             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14870         };
14871         
14872         if(this.sr_only){
14873             cfg.cn = {
14874                 tag: 'span',
14875                 cls: 'sr-only',
14876                 html: this.sr_only
14877             }
14878         }
14879         
14880         if(this.role){
14881             cfg.role = this.role;
14882         }
14883         
14884         if(this.aria_valuenow){
14885             cfg['aria-valuenow'] = this.aria_valuenow;
14886         }
14887         
14888         if(this.aria_valuemin){
14889             cfg['aria-valuemin'] = this.aria_valuemin;
14890         }
14891         
14892         if(this.aria_valuemax){
14893             cfg['aria-valuemax'] = this.aria_valuemax;
14894         }
14895         
14896         if(this.label && !this.sr_only){
14897             cfg.html = this.label;
14898         }
14899         
14900         if(this.panel){
14901             cfg.cls += ' progress-bar-' + this.panel;
14902         }
14903         
14904         return cfg;
14905     },
14906     
14907     update : function(aria_valuenow)
14908     {
14909         this.aria_valuenow = aria_valuenow;
14910         
14911         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14912     }
14913    
14914 });
14915
14916  
14917
14918  /*
14919  * - LGPL
14920  *
14921  * column
14922  * 
14923  */
14924
14925 /**
14926  * @class Roo.bootstrap.TabGroup
14927  * @extends Roo.bootstrap.Column
14928  * Bootstrap Column class
14929  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14930  * @cfg {Boolean} carousel true to make the group behave like a carousel
14931  * @cfg {Number} bullets show the panel pointer.. default 0
14932  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14933  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14934  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14935  * 
14936  * @constructor
14937  * Create a new TabGroup
14938  * @param {Object} config The config object
14939  */
14940
14941 Roo.bootstrap.TabGroup = function(config){
14942     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14943     if (!this.navId) {
14944         this.navId = Roo.id();
14945     }
14946     this.tabs = [];
14947     Roo.bootstrap.TabGroup.register(this);
14948     
14949 };
14950
14951 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14952     
14953     carousel : false,
14954     transition : false,
14955     bullets : 0,
14956     timer : 0,
14957     autoslide : false,
14958     slideFn : false,
14959     slideOnTouch : false,
14960     
14961     getAutoCreate : function()
14962     {
14963         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14964         
14965         cfg.cls += ' tab-content';
14966         
14967         Roo.log('get auto create...............');
14968         
14969         if (this.carousel) {
14970             cfg.cls += ' carousel slide';
14971             
14972             cfg.cn = [{
14973                cls : 'carousel-inner'
14974             }];
14975         
14976             if(this.bullets > 0 && !Roo.isTouch){
14977                 
14978                 var bullets = {
14979                     cls : 'carousel-bullets',
14980                     cn : []
14981                 };
14982                 
14983                 if(this.bullets_cls){
14984                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14985                 }
14986                 
14987                 for (var i = 0; i < this.bullets; i++){
14988                     bullets.cn.push({
14989                         cls : 'bullet bullet-' + i
14990                     });
14991                 }
14992                 
14993                 bullets.cn.push({
14994                     cls : 'clear'
14995                 });
14996                 
14997                 cfg.cn[0].cn = bullets;
14998             }
14999         }
15000         
15001         return cfg;
15002     },
15003     
15004     initEvents:  function()
15005     {
15006         Roo.log('-------- init events on tab group ---------');
15007         
15008         if(this.bullets > 0 && !Roo.isTouch){
15009             this.initBullet();
15010         }
15011         
15012         Roo.log(this);
15013         
15014         if(Roo.isTouch && this.slideOnTouch){
15015             this.el.on("touchstart", this.onTouchStart, this);
15016         }
15017         
15018         if(this.autoslide){
15019             var _this = this;
15020             
15021             this.slideFn = window.setInterval(function() {
15022                 _this.showPanelNext();
15023             }, this.timer);
15024         }
15025         
15026     },
15027     
15028     onTouchStart : function(e, el, o)
15029     {
15030         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15031             return;
15032         }
15033         
15034         this.showPanelNext();
15035     },
15036     
15037     getChildContainer : function()
15038     {
15039         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15040     },
15041     
15042     /**
15043     * register a Navigation item
15044     * @param {Roo.bootstrap.NavItem} the navitem to add
15045     */
15046     register : function(item)
15047     {
15048         this.tabs.push( item);
15049         item.navId = this.navId; // not really needed..
15050     
15051     },
15052     
15053     getActivePanel : function()
15054     {
15055         var r = false;
15056         Roo.each(this.tabs, function(t) {
15057             if (t.active) {
15058                 r = t;
15059                 return false;
15060             }
15061             return null;
15062         });
15063         return r;
15064         
15065     },
15066     getPanelByName : function(n)
15067     {
15068         var r = false;
15069         Roo.each(this.tabs, function(t) {
15070             if (t.tabId == n) {
15071                 r = t;
15072                 return false;
15073             }
15074             return null;
15075         });
15076         return r;
15077     },
15078     indexOfPanel : function(p)
15079     {
15080         var r = false;
15081         Roo.each(this.tabs, function(t,i) {
15082             if (t.tabId == p.tabId) {
15083                 r = i;
15084                 return false;
15085             }
15086             return null;
15087         });
15088         return r;
15089     },
15090     /**
15091      * show a specific panel
15092      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15093      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15094      */
15095     showPanel : function (pan)
15096     {
15097         if(this.transition){
15098             Roo.log("waiting for the transitionend");
15099             return;
15100         }
15101         
15102         if (typeof(pan) == 'number') {
15103             pan = this.tabs[pan];
15104         }
15105         if (typeof(pan) == 'string') {
15106             pan = this.getPanelByName(pan);
15107         }
15108         if (pan.tabId == this.getActivePanel().tabId) {
15109             return true;
15110         }
15111         var cur = this.getActivePanel();
15112         
15113         if (false === cur.fireEvent('beforedeactivate')) {
15114             return false;
15115         }
15116         
15117         if(this.bullets > 0 && !Roo.isTouch){
15118             this.setActiveBullet(this.indexOfPanel(pan));
15119         }
15120         
15121         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15122             
15123             this.transition = true;
15124             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15125             var lr = dir == 'next' ? 'left' : 'right';
15126             pan.el.addClass(dir); // or prev
15127             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15128             cur.el.addClass(lr); // or right
15129             pan.el.addClass(lr);
15130             
15131             var _this = this;
15132             cur.el.on('transitionend', function() {
15133                 Roo.log("trans end?");
15134                 
15135                 pan.el.removeClass([lr,dir]);
15136                 pan.setActive(true);
15137                 
15138                 cur.el.removeClass([lr]);
15139                 cur.setActive(false);
15140                 
15141                 _this.transition = false;
15142                 
15143             }, this, { single:  true } );
15144             
15145             return true;
15146         }
15147         
15148         cur.setActive(false);
15149         pan.setActive(true);
15150         
15151         return true;
15152         
15153     },
15154     showPanelNext : function()
15155     {
15156         var i = this.indexOfPanel(this.getActivePanel());
15157         
15158         if (i >= this.tabs.length - 1 && !this.autoslide) {
15159             return;
15160         }
15161         
15162         if (i >= this.tabs.length - 1 && this.autoslide) {
15163             i = -1;
15164         }
15165         
15166         this.showPanel(this.tabs[i+1]);
15167     },
15168     
15169     showPanelPrev : function()
15170     {
15171         var i = this.indexOfPanel(this.getActivePanel());
15172         
15173         if (i  < 1 && !this.autoslide) {
15174             return;
15175         }
15176         
15177         if (i < 1 && this.autoslide) {
15178             i = this.tabs.length;
15179         }
15180         
15181         this.showPanel(this.tabs[i-1]);
15182     },
15183     
15184     initBullet : function()
15185     {
15186         if(Roo.isTouch){
15187             return;
15188         }
15189         
15190         var _this = this;
15191         
15192         for (var i = 0; i < this.bullets; i++){
15193             var bullet = this.el.select('.bullet-' + i, true).first();
15194
15195             if(!bullet){
15196                 continue;
15197             }
15198
15199             bullet.on('click', (function(e, el, o, ii, t){
15200
15201                 e.preventDefault();
15202
15203                 _this.showPanel(ii);
15204
15205                 if(_this.autoslide && _this.slideFn){
15206                     clearInterval(_this.slideFn);
15207                     _this.slideFn = window.setInterval(function() {
15208                         _this.showPanelNext();
15209                     }, _this.timer);
15210                 }
15211
15212             }).createDelegate(this, [i, bullet], true));
15213         }
15214     },
15215     
15216     setActiveBullet : function(i)
15217     {
15218         if(Roo.isTouch){
15219             return;
15220         }
15221         
15222         Roo.each(this.el.select('.bullet', true).elements, function(el){
15223             el.removeClass('selected');
15224         });
15225
15226         var bullet = this.el.select('.bullet-' + i, true).first();
15227         
15228         if(!bullet){
15229             return;
15230         }
15231         
15232         bullet.addClass('selected');
15233     }
15234     
15235     
15236   
15237 });
15238
15239  
15240
15241  
15242  
15243 Roo.apply(Roo.bootstrap.TabGroup, {
15244     
15245     groups: {},
15246      /**
15247     * register a Navigation Group
15248     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15249     */
15250     register : function(navgrp)
15251     {
15252         this.groups[navgrp.navId] = navgrp;
15253         
15254     },
15255     /**
15256     * fetch a Navigation Group based on the navigation ID
15257     * if one does not exist , it will get created.
15258     * @param {string} the navgroup to add
15259     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15260     */
15261     get: function(navId) {
15262         if (typeof(this.groups[navId]) == 'undefined') {
15263             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15264         }
15265         return this.groups[navId] ;
15266     }
15267     
15268     
15269     
15270 });
15271
15272  /*
15273  * - LGPL
15274  *
15275  * TabPanel
15276  * 
15277  */
15278
15279 /**
15280  * @class Roo.bootstrap.TabPanel
15281  * @extends Roo.bootstrap.Component
15282  * Bootstrap TabPanel class
15283  * @cfg {Boolean} active panel active
15284  * @cfg {String} html panel content
15285  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15286  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15287  * 
15288  * 
15289  * @constructor
15290  * Create a new TabPanel
15291  * @param {Object} config The config object
15292  */
15293
15294 Roo.bootstrap.TabPanel = function(config){
15295     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15296     this.addEvents({
15297         /**
15298              * @event changed
15299              * Fires when the active status changes
15300              * @param {Roo.bootstrap.TabPanel} this
15301              * @param {Boolean} state the new state
15302             
15303          */
15304         'changed': true,
15305         /**
15306              * @event beforedeactivate
15307              * Fires before a tab is de-activated - can be used to do validation on a form.
15308              * @param {Roo.bootstrap.TabPanel} this
15309              * @return {Boolean} false if there is an error
15310             
15311          */
15312         'beforedeactivate': true
15313      });
15314     
15315     this.tabId = this.tabId || Roo.id();
15316   
15317 };
15318
15319 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15320     
15321     active: false,
15322     html: false,
15323     tabId: false,
15324     navId : false,
15325     
15326     getAutoCreate : function(){
15327         var cfg = {
15328             tag: 'div',
15329             // item is needed for carousel - not sure if it has any effect otherwise
15330             cls: 'tab-pane item',
15331             html: this.html || ''
15332         };
15333         
15334         if(this.active){
15335             cfg.cls += ' active';
15336         }
15337         
15338         if(this.tabId){
15339             cfg.tabId = this.tabId;
15340         }
15341         
15342         
15343         return cfg;
15344     },
15345     
15346     initEvents:  function()
15347     {
15348         Roo.log('-------- init events on tab panel ---------');
15349         
15350         var p = this.parent();
15351         this.navId = this.navId || p.navId;
15352         
15353         if (typeof(this.navId) != 'undefined') {
15354             // not really needed.. but just in case.. parent should be a NavGroup.
15355             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15356             Roo.log(['register', tg, this]);
15357             tg.register(this);
15358             
15359             var i = tg.tabs.length - 1;
15360             
15361             if(this.active && tg.bullets > 0 && i < tg.bullets){
15362                 tg.setActiveBullet(i);
15363             }
15364         }
15365         
15366     },
15367     
15368     
15369     onRender : function(ct, position)
15370     {
15371        // Roo.log("Call onRender: " + this.xtype);
15372         
15373         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15374         
15375         
15376         
15377         
15378         
15379     },
15380     
15381     setActive: function(state)
15382     {
15383         Roo.log("panel - set active " + this.tabId + "=" + state);
15384         
15385         this.active = state;
15386         if (!state) {
15387             this.el.removeClass('active');
15388             
15389         } else  if (!this.el.hasClass('active')) {
15390             this.el.addClass('active');
15391         }
15392         
15393         this.fireEvent('changed', this, state);
15394     }
15395     
15396     
15397 });
15398  
15399
15400  
15401
15402  /*
15403  * - LGPL
15404  *
15405  * DateField
15406  * 
15407  */
15408
15409 /**
15410  * @class Roo.bootstrap.DateField
15411  * @extends Roo.bootstrap.Input
15412  * Bootstrap DateField class
15413  * @cfg {Number} weekStart default 0
15414  * @cfg {String} viewMode default empty, (months|years)
15415  * @cfg {String} minViewMode default empty, (months|years)
15416  * @cfg {Number} startDate default -Infinity
15417  * @cfg {Number} endDate default Infinity
15418  * @cfg {Boolean} todayHighlight default false
15419  * @cfg {Boolean} todayBtn default false
15420  * @cfg {Boolean} calendarWeeks default false
15421  * @cfg {Object} daysOfWeekDisabled default empty
15422  * @cfg {Boolean} singleMode default false (true | false)
15423  * 
15424  * @cfg {Boolean} keyboardNavigation default true
15425  * @cfg {String} language default en
15426  * 
15427  * @constructor
15428  * Create a new DateField
15429  * @param {Object} config The config object
15430  */
15431
15432 Roo.bootstrap.DateField = function(config){
15433     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15434      this.addEvents({
15435             /**
15436              * @event show
15437              * Fires when this field show.
15438              * @param {Roo.bootstrap.DateField} this
15439              * @param {Mixed} date The date value
15440              */
15441             show : true,
15442             /**
15443              * @event show
15444              * Fires when this field hide.
15445              * @param {Roo.bootstrap.DateField} this
15446              * @param {Mixed} date The date value
15447              */
15448             hide : true,
15449             /**
15450              * @event select
15451              * Fires when select a date.
15452              * @param {Roo.bootstrap.DateField} this
15453              * @param {Mixed} date The date value
15454              */
15455             select : true
15456         });
15457 };
15458
15459 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15460     
15461     /**
15462      * @cfg {String} format
15463      * The default date format string which can be overriden for localization support.  The format must be
15464      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15465      */
15466     format : "m/d/y",
15467     /**
15468      * @cfg {String} altFormats
15469      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15470      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15471      */
15472     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15473     
15474     weekStart : 0,
15475     
15476     viewMode : '',
15477     
15478     minViewMode : '',
15479     
15480     todayHighlight : false,
15481     
15482     todayBtn: false,
15483     
15484     language: 'en',
15485     
15486     keyboardNavigation: true,
15487     
15488     calendarWeeks: false,
15489     
15490     startDate: -Infinity,
15491     
15492     endDate: Infinity,
15493     
15494     daysOfWeekDisabled: [],
15495     
15496     _events: [],
15497     
15498     singleMode : false,
15499     
15500     UTCDate: function()
15501     {
15502         return new Date(Date.UTC.apply(Date, arguments));
15503     },
15504     
15505     UTCToday: function()
15506     {
15507         var today = new Date();
15508         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15509     },
15510     
15511     getDate: function() {
15512             var d = this.getUTCDate();
15513             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15514     },
15515     
15516     getUTCDate: function() {
15517             return this.date;
15518     },
15519     
15520     setDate: function(d) {
15521             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15522     },
15523     
15524     setUTCDate: function(d) {
15525             this.date = d;
15526             this.setValue(this.formatDate(this.date));
15527     },
15528         
15529     onRender: function(ct, position)
15530     {
15531         
15532         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15533         
15534         this.language = this.language || 'en';
15535         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15536         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15537         
15538         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15539         this.format = this.format || 'm/d/y';
15540         this.isInline = false;
15541         this.isInput = true;
15542         this.component = this.el.select('.add-on', true).first() || false;
15543         this.component = (this.component && this.component.length === 0) ? false : this.component;
15544         this.hasInput = this.component && this.inputEL().length;
15545         
15546         if (typeof(this.minViewMode === 'string')) {
15547             switch (this.minViewMode) {
15548                 case 'months':
15549                     this.minViewMode = 1;
15550                     break;
15551                 case 'years':
15552                     this.minViewMode = 2;
15553                     break;
15554                 default:
15555                     this.minViewMode = 0;
15556                     break;
15557             }
15558         }
15559         
15560         if (typeof(this.viewMode === 'string')) {
15561             switch (this.viewMode) {
15562                 case 'months':
15563                     this.viewMode = 1;
15564                     break;
15565                 case 'years':
15566                     this.viewMode = 2;
15567                     break;
15568                 default:
15569                     this.viewMode = 0;
15570                     break;
15571             }
15572         }
15573                 
15574         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15575         
15576 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15577         
15578         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15579         
15580         this.picker().on('mousedown', this.onMousedown, this);
15581         this.picker().on('click', this.onClick, this);
15582         
15583         this.picker().addClass('datepicker-dropdown');
15584         
15585         this.startViewMode = this.viewMode;
15586         
15587         if(this.singleMode){
15588             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15589                 v.setVisibilityMode(Roo.Element.DISPLAY)
15590                 v.hide();
15591             });
15592             
15593             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15594                 v.setStyle('width', '189px');
15595             });
15596         }
15597         
15598         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15599             if(!this.calendarWeeks){
15600                 v.remove();
15601                 return;
15602             }
15603             
15604             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15605             v.attr('colspan', function(i, val){
15606                 return parseInt(val) + 1;
15607             });
15608         })
15609                         
15610         
15611         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15612         
15613         this.setStartDate(this.startDate);
15614         this.setEndDate(this.endDate);
15615         
15616         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15617         
15618         this.fillDow();
15619         this.fillMonths();
15620         this.update();
15621         this.showMode();
15622         
15623         if(this.isInline) {
15624             this.show();
15625         }
15626     },
15627     
15628     picker : function()
15629     {
15630         return this.pickerEl;
15631 //        return this.el.select('.datepicker', true).first();
15632     },
15633     
15634     fillDow: function()
15635     {
15636         var dowCnt = this.weekStart;
15637         
15638         var dow = {
15639             tag: 'tr',
15640             cn: [
15641                 
15642             ]
15643         };
15644         
15645         if(this.calendarWeeks){
15646             dow.cn.push({
15647                 tag: 'th',
15648                 cls: 'cw',
15649                 html: '&nbsp;'
15650             })
15651         }
15652         
15653         while (dowCnt < this.weekStart + 7) {
15654             dow.cn.push({
15655                 tag: 'th',
15656                 cls: 'dow',
15657                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15658             });
15659         }
15660         
15661         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15662     },
15663     
15664     fillMonths: function()
15665     {    
15666         var i = 0;
15667         var months = this.picker().select('>.datepicker-months td', true).first();
15668         
15669         months.dom.innerHTML = '';
15670         
15671         while (i < 12) {
15672             var month = {
15673                 tag: 'span',
15674                 cls: 'month',
15675                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15676             }
15677             
15678             months.createChild(month);
15679         }
15680         
15681     },
15682     
15683     update: function()
15684     {
15685         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;
15686         
15687         if (this.date < this.startDate) {
15688             this.viewDate = new Date(this.startDate);
15689         } else if (this.date > this.endDate) {
15690             this.viewDate = new Date(this.endDate);
15691         } else {
15692             this.viewDate = new Date(this.date);
15693         }
15694         
15695         this.fill();
15696     },
15697     
15698     fill: function() 
15699     {
15700         var d = new Date(this.viewDate),
15701                 year = d.getUTCFullYear(),
15702                 month = d.getUTCMonth(),
15703                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15704                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15705                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15706                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15707                 currentDate = this.date && this.date.valueOf(),
15708                 today = this.UTCToday();
15709         
15710         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15711         
15712 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15713         
15714 //        this.picker.select('>tfoot th.today').
15715 //                                              .text(dates[this.language].today)
15716 //                                              .toggle(this.todayBtn !== false);
15717     
15718         this.updateNavArrows();
15719         this.fillMonths();
15720                                                 
15721         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15722         
15723         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15724          
15725         prevMonth.setUTCDate(day);
15726         
15727         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15728         
15729         var nextMonth = new Date(prevMonth);
15730         
15731         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15732         
15733         nextMonth = nextMonth.valueOf();
15734         
15735         var fillMonths = false;
15736         
15737         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15738         
15739         while(prevMonth.valueOf() < nextMonth) {
15740             var clsName = '';
15741             
15742             if (prevMonth.getUTCDay() === this.weekStart) {
15743                 if(fillMonths){
15744                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15745                 }
15746                     
15747                 fillMonths = {
15748                     tag: 'tr',
15749                     cn: []
15750                 };
15751                 
15752                 if(this.calendarWeeks){
15753                     // ISO 8601: First week contains first thursday.
15754                     // ISO also states week starts on Monday, but we can be more abstract here.
15755                     var
15756                     // Start of current week: based on weekstart/current date
15757                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15758                     // Thursday of this week
15759                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15760                     // First Thursday of year, year from thursday
15761                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15762                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15763                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15764                     
15765                     fillMonths.cn.push({
15766                         tag: 'td',
15767                         cls: 'cw',
15768                         html: calWeek
15769                     });
15770                 }
15771             }
15772             
15773             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15774                 clsName += ' old';
15775             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15776                 clsName += ' new';
15777             }
15778             if (this.todayHighlight &&
15779                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15780                 prevMonth.getUTCMonth() == today.getMonth() &&
15781                 prevMonth.getUTCDate() == today.getDate()) {
15782                 clsName += ' today';
15783             }
15784             
15785             if (currentDate && prevMonth.valueOf() === currentDate) {
15786                 clsName += ' active';
15787             }
15788             
15789             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15790                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15791                     clsName += ' disabled';
15792             }
15793             
15794             fillMonths.cn.push({
15795                 tag: 'td',
15796                 cls: 'day ' + clsName,
15797                 html: prevMonth.getDate()
15798             })
15799             
15800             prevMonth.setDate(prevMonth.getDate()+1);
15801         }
15802           
15803         var currentYear = this.date && this.date.getUTCFullYear();
15804         var currentMonth = this.date && this.date.getUTCMonth();
15805         
15806         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15807         
15808         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15809             v.removeClass('active');
15810             
15811             if(currentYear === year && k === currentMonth){
15812                 v.addClass('active');
15813             }
15814             
15815             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15816                 v.addClass('disabled');
15817             }
15818             
15819         });
15820         
15821         
15822         year = parseInt(year/10, 10) * 10;
15823         
15824         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15825         
15826         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15827         
15828         year -= 1;
15829         for (var i = -1; i < 11; i++) {
15830             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15831                 tag: 'span',
15832                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15833                 html: year
15834             })
15835             
15836             year += 1;
15837         }
15838     },
15839     
15840     showMode: function(dir) 
15841     {
15842         if (dir) {
15843             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15844         }
15845         
15846         Roo.each(this.picker().select('>div',true).elements, function(v){
15847             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15848             v.hide();
15849         });
15850         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15851     },
15852     
15853     place: function()
15854     {
15855         if(this.isInline) return;
15856         
15857         this.picker().removeClass(['bottom', 'top']);
15858         
15859         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15860             /*
15861              * place to the top of element!
15862              *
15863              */
15864             
15865             this.picker().addClass('top');
15866             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15867             
15868             return;
15869         }
15870         
15871         this.picker().addClass('bottom');
15872         
15873         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15874     },
15875     
15876     parseDate : function(value)
15877     {
15878         if(!value || value instanceof Date){
15879             return value;
15880         }
15881         var v = Date.parseDate(value, this.format);
15882         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15883             v = Date.parseDate(value, 'Y-m-d');
15884         }
15885         if(!v && this.altFormats){
15886             if(!this.altFormatsArray){
15887                 this.altFormatsArray = this.altFormats.split("|");
15888             }
15889             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15890                 v = Date.parseDate(value, this.altFormatsArray[i]);
15891             }
15892         }
15893         return v;
15894     },
15895     
15896     formatDate : function(date, fmt)
15897     {   
15898         return (!date || !(date instanceof Date)) ?
15899         date : date.dateFormat(fmt || this.format);
15900     },
15901     
15902     onFocus : function()
15903     {
15904         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15905         this.show();
15906     },
15907     
15908     onBlur : function()
15909     {
15910         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15911         
15912         var d = this.inputEl().getValue();
15913         
15914         this.setValue(d);
15915                 
15916         this.hide();
15917     },
15918     
15919     show : function()
15920     {
15921         this.picker().show();
15922         this.update();
15923         this.place();
15924         
15925         this.fireEvent('show', this, this.date);
15926     },
15927     
15928     hide : function()
15929     {
15930         if(this.isInline) return;
15931         this.picker().hide();
15932         this.viewMode = this.startViewMode;
15933         this.showMode();
15934         
15935         this.fireEvent('hide', this, this.date);
15936         
15937     },
15938     
15939     onMousedown: function(e)
15940     {
15941         e.stopPropagation();
15942         e.preventDefault();
15943     },
15944     
15945     keyup: function(e)
15946     {
15947         Roo.bootstrap.DateField.superclass.keyup.call(this);
15948         this.update();
15949     },
15950
15951     setValue: function(v)
15952     {
15953         
15954         // v can be a string or a date..
15955         
15956         
15957         var d = new Date(this.parseDate(v) ).clearTime();
15958         
15959         if(isNaN(d.getTime())){
15960             this.date = this.viewDate = '';
15961             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15962             return;
15963         }
15964         
15965         v = this.formatDate(d);
15966         
15967         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15968         
15969         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15970      
15971         this.update();
15972
15973         this.fireEvent('select', this, this.date);
15974         
15975     },
15976     
15977     getValue: function()
15978     {
15979         return this.formatDate(this.date);
15980     },
15981     
15982     fireKey: function(e)
15983     {
15984         if (!this.picker().isVisible()){
15985             if (e.keyCode == 27) // allow escape to hide and re-show picker
15986                 this.show();
15987             return;
15988         }
15989         
15990         var dateChanged = false,
15991         dir, day, month,
15992         newDate, newViewDate;
15993         
15994         switch(e.keyCode){
15995             case 27: // escape
15996                 this.hide();
15997                 e.preventDefault();
15998                 break;
15999             case 37: // left
16000             case 39: // right
16001                 if (!this.keyboardNavigation) break;
16002                 dir = e.keyCode == 37 ? -1 : 1;
16003                 
16004                 if (e.ctrlKey){
16005                     newDate = this.moveYear(this.date, dir);
16006                     newViewDate = this.moveYear(this.viewDate, dir);
16007                 } else if (e.shiftKey){
16008                     newDate = this.moveMonth(this.date, dir);
16009                     newViewDate = this.moveMonth(this.viewDate, dir);
16010                 } else {
16011                     newDate = new Date(this.date);
16012                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16013                     newViewDate = new Date(this.viewDate);
16014                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16015                 }
16016                 if (this.dateWithinRange(newDate)){
16017                     this.date = newDate;
16018                     this.viewDate = newViewDate;
16019                     this.setValue(this.formatDate(this.date));
16020 //                    this.update();
16021                     e.preventDefault();
16022                     dateChanged = true;
16023                 }
16024                 break;
16025             case 38: // up
16026             case 40: // down
16027                 if (!this.keyboardNavigation) break;
16028                 dir = e.keyCode == 38 ? -1 : 1;
16029                 if (e.ctrlKey){
16030                     newDate = this.moveYear(this.date, dir);
16031                     newViewDate = this.moveYear(this.viewDate, dir);
16032                 } else if (e.shiftKey){
16033                     newDate = this.moveMonth(this.date, dir);
16034                     newViewDate = this.moveMonth(this.viewDate, dir);
16035                 } else {
16036                     newDate = new Date(this.date);
16037                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16038                     newViewDate = new Date(this.viewDate);
16039                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16040                 }
16041                 if (this.dateWithinRange(newDate)){
16042                     this.date = newDate;
16043                     this.viewDate = newViewDate;
16044                     this.setValue(this.formatDate(this.date));
16045 //                    this.update();
16046                     e.preventDefault();
16047                     dateChanged = true;
16048                 }
16049                 break;
16050             case 13: // enter
16051                 this.setValue(this.formatDate(this.date));
16052                 this.hide();
16053                 e.preventDefault();
16054                 break;
16055             case 9: // tab
16056                 this.setValue(this.formatDate(this.date));
16057                 this.hide();
16058                 break;
16059             case 16: // shift
16060             case 17: // ctrl
16061             case 18: // alt
16062                 break;
16063             default :
16064                 this.hide();
16065                 
16066         }
16067     },
16068     
16069     
16070     onClick: function(e) 
16071     {
16072         e.stopPropagation();
16073         e.preventDefault();
16074         
16075         var target = e.getTarget();
16076         
16077         if(target.nodeName.toLowerCase() === 'i'){
16078             target = Roo.get(target).dom.parentNode;
16079         }
16080         
16081         var nodeName = target.nodeName;
16082         var className = target.className;
16083         var html = target.innerHTML;
16084         //Roo.log(nodeName);
16085         
16086         switch(nodeName.toLowerCase()) {
16087             case 'th':
16088                 switch(className) {
16089                     case 'switch':
16090                         this.showMode(1);
16091                         break;
16092                     case 'prev':
16093                     case 'next':
16094                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16095                         switch(this.viewMode){
16096                                 case 0:
16097                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16098                                         break;
16099                                 case 1:
16100                                 case 2:
16101                                         this.viewDate = this.moveYear(this.viewDate, dir);
16102                                         break;
16103                         }
16104                         this.fill();
16105                         break;
16106                     case 'today':
16107                         var date = new Date();
16108                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16109 //                        this.fill()
16110                         this.setValue(this.formatDate(this.date));
16111                         
16112                         this.hide();
16113                         break;
16114                 }
16115                 break;
16116             case 'span':
16117                 if (className.indexOf('disabled') < 0) {
16118                     this.viewDate.setUTCDate(1);
16119                     if (className.indexOf('month') > -1) {
16120                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16121                     } else {
16122                         var year = parseInt(html, 10) || 0;
16123                         this.viewDate.setUTCFullYear(year);
16124                         
16125                     }
16126                     
16127                     if(this.singleMode){
16128                         this.setValue(this.formatDate(this.viewDate));
16129                         this.hide();
16130                         return;
16131                     }
16132                     
16133                     this.showMode(-1);
16134                     this.fill();
16135                 }
16136                 break;
16137                 
16138             case 'td':
16139                 //Roo.log(className);
16140                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16141                     var day = parseInt(html, 10) || 1;
16142                     var year = this.viewDate.getUTCFullYear(),
16143                         month = this.viewDate.getUTCMonth();
16144
16145                     if (className.indexOf('old') > -1) {
16146                         if(month === 0 ){
16147                             month = 11;
16148                             year -= 1;
16149                         }else{
16150                             month -= 1;
16151                         }
16152                     } else if (className.indexOf('new') > -1) {
16153                         if (month == 11) {
16154                             month = 0;
16155                             year += 1;
16156                         } else {
16157                             month += 1;
16158                         }
16159                     }
16160                     //Roo.log([year,month,day]);
16161                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16162                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16163 //                    this.fill();
16164                     //Roo.log(this.formatDate(this.date));
16165                     this.setValue(this.formatDate(this.date));
16166                     this.hide();
16167                 }
16168                 break;
16169         }
16170     },
16171     
16172     setStartDate: function(startDate)
16173     {
16174         this.startDate = startDate || -Infinity;
16175         if (this.startDate !== -Infinity) {
16176             this.startDate = this.parseDate(this.startDate);
16177         }
16178         this.update();
16179         this.updateNavArrows();
16180     },
16181
16182     setEndDate: function(endDate)
16183     {
16184         this.endDate = endDate || Infinity;
16185         if (this.endDate !== Infinity) {
16186             this.endDate = this.parseDate(this.endDate);
16187         }
16188         this.update();
16189         this.updateNavArrows();
16190     },
16191     
16192     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16193     {
16194         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16195         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16196             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16197         }
16198         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16199             return parseInt(d, 10);
16200         });
16201         this.update();
16202         this.updateNavArrows();
16203     },
16204     
16205     updateNavArrows: function() 
16206     {
16207         if(this.singleMode){
16208             return;
16209         }
16210         
16211         var d = new Date(this.viewDate),
16212         year = d.getUTCFullYear(),
16213         month = d.getUTCMonth();
16214         
16215         Roo.each(this.picker().select('.prev', true).elements, function(v){
16216             v.show();
16217             switch (this.viewMode) {
16218                 case 0:
16219
16220                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16221                         v.hide();
16222                     }
16223                     break;
16224                 case 1:
16225                 case 2:
16226                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16227                         v.hide();
16228                     }
16229                     break;
16230             }
16231         });
16232         
16233         Roo.each(this.picker().select('.next', true).elements, function(v){
16234             v.show();
16235             switch (this.viewMode) {
16236                 case 0:
16237
16238                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16239                         v.hide();
16240                     }
16241                     break;
16242                 case 1:
16243                 case 2:
16244                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16245                         v.hide();
16246                     }
16247                     break;
16248             }
16249         })
16250     },
16251     
16252     moveMonth: function(date, dir)
16253     {
16254         if (!dir) return date;
16255         var new_date = new Date(date.valueOf()),
16256         day = new_date.getUTCDate(),
16257         month = new_date.getUTCMonth(),
16258         mag = Math.abs(dir),
16259         new_month, test;
16260         dir = dir > 0 ? 1 : -1;
16261         if (mag == 1){
16262             test = dir == -1
16263             // If going back one month, make sure month is not current month
16264             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16265             ? function(){
16266                 return new_date.getUTCMonth() == month;
16267             }
16268             // If going forward one month, make sure month is as expected
16269             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16270             : function(){
16271                 return new_date.getUTCMonth() != new_month;
16272             };
16273             new_month = month + dir;
16274             new_date.setUTCMonth(new_month);
16275             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16276             if (new_month < 0 || new_month > 11)
16277                 new_month = (new_month + 12) % 12;
16278         } else {
16279             // For magnitudes >1, move one month at a time...
16280             for (var i=0; i<mag; i++)
16281                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16282                 new_date = this.moveMonth(new_date, dir);
16283             // ...then reset the day, keeping it in the new month
16284             new_month = new_date.getUTCMonth();
16285             new_date.setUTCDate(day);
16286             test = function(){
16287                 return new_month != new_date.getUTCMonth();
16288             };
16289         }
16290         // Common date-resetting loop -- if date is beyond end of month, make it
16291         // end of month
16292         while (test()){
16293             new_date.setUTCDate(--day);
16294             new_date.setUTCMonth(new_month);
16295         }
16296         return new_date;
16297     },
16298
16299     moveYear: function(date, dir)
16300     {
16301         return this.moveMonth(date, dir*12);
16302     },
16303
16304     dateWithinRange: function(date)
16305     {
16306         return date >= this.startDate && date <= this.endDate;
16307     },
16308
16309     
16310     remove: function() 
16311     {
16312         this.picker().remove();
16313     }
16314    
16315 });
16316
16317 Roo.apply(Roo.bootstrap.DateField,  {
16318     
16319     head : {
16320         tag: 'thead',
16321         cn: [
16322         {
16323             tag: 'tr',
16324             cn: [
16325             {
16326                 tag: 'th',
16327                 cls: 'prev',
16328                 html: '<i class="fa fa-arrow-left"/>'
16329             },
16330             {
16331                 tag: 'th',
16332                 cls: 'switch',
16333                 colspan: '5'
16334             },
16335             {
16336                 tag: 'th',
16337                 cls: 'next',
16338                 html: '<i class="fa fa-arrow-right"/>'
16339             }
16340
16341             ]
16342         }
16343         ]
16344     },
16345     
16346     content : {
16347         tag: 'tbody',
16348         cn: [
16349         {
16350             tag: 'tr',
16351             cn: [
16352             {
16353                 tag: 'td',
16354                 colspan: '7'
16355             }
16356             ]
16357         }
16358         ]
16359     },
16360     
16361     footer : {
16362         tag: 'tfoot',
16363         cn: [
16364         {
16365             tag: 'tr',
16366             cn: [
16367             {
16368                 tag: 'th',
16369                 colspan: '7',
16370                 cls: 'today'
16371             }
16372                     
16373             ]
16374         }
16375         ]
16376     },
16377     
16378     dates:{
16379         en: {
16380             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16381             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16382             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16383             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16384             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16385             today: "Today"
16386         }
16387     },
16388     
16389     modes: [
16390     {
16391         clsName: 'days',
16392         navFnc: 'Month',
16393         navStep: 1
16394     },
16395     {
16396         clsName: 'months',
16397         navFnc: 'FullYear',
16398         navStep: 1
16399     },
16400     {
16401         clsName: 'years',
16402         navFnc: 'FullYear',
16403         navStep: 10
16404     }]
16405 });
16406
16407 Roo.apply(Roo.bootstrap.DateField,  {
16408   
16409     template : {
16410         tag: 'div',
16411         cls: 'datepicker dropdown-menu roo-dynamic',
16412         cn: [
16413         {
16414             tag: 'div',
16415             cls: 'datepicker-days',
16416             cn: [
16417             {
16418                 tag: 'table',
16419                 cls: 'table-condensed',
16420                 cn:[
16421                 Roo.bootstrap.DateField.head,
16422                 {
16423                     tag: 'tbody'
16424                 },
16425                 Roo.bootstrap.DateField.footer
16426                 ]
16427             }
16428             ]
16429         },
16430         {
16431             tag: 'div',
16432             cls: 'datepicker-months',
16433             cn: [
16434             {
16435                 tag: 'table',
16436                 cls: 'table-condensed',
16437                 cn:[
16438                 Roo.bootstrap.DateField.head,
16439                 Roo.bootstrap.DateField.content,
16440                 Roo.bootstrap.DateField.footer
16441                 ]
16442             }
16443             ]
16444         },
16445         {
16446             tag: 'div',
16447             cls: 'datepicker-years',
16448             cn: [
16449             {
16450                 tag: 'table',
16451                 cls: 'table-condensed',
16452                 cn:[
16453                 Roo.bootstrap.DateField.head,
16454                 Roo.bootstrap.DateField.content,
16455                 Roo.bootstrap.DateField.footer
16456                 ]
16457             }
16458             ]
16459         }
16460         ]
16461     }
16462 });
16463
16464  
16465
16466  /*
16467  * - LGPL
16468  *
16469  * TimeField
16470  * 
16471  */
16472
16473 /**
16474  * @class Roo.bootstrap.TimeField
16475  * @extends Roo.bootstrap.Input
16476  * Bootstrap DateField class
16477  * 
16478  * 
16479  * @constructor
16480  * Create a new TimeField
16481  * @param {Object} config The config object
16482  */
16483
16484 Roo.bootstrap.TimeField = function(config){
16485     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16486     this.addEvents({
16487             /**
16488              * @event show
16489              * Fires when this field show.
16490              * @param {Roo.bootstrap.DateField} thisthis
16491              * @param {Mixed} date The date value
16492              */
16493             show : true,
16494             /**
16495              * @event show
16496              * Fires when this field hide.
16497              * @param {Roo.bootstrap.DateField} this
16498              * @param {Mixed} date The date value
16499              */
16500             hide : true,
16501             /**
16502              * @event select
16503              * Fires when select a date.
16504              * @param {Roo.bootstrap.DateField} this
16505              * @param {Mixed} date The date value
16506              */
16507             select : true
16508         });
16509 };
16510
16511 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16512     
16513     /**
16514      * @cfg {String} format
16515      * The default time format string which can be overriden for localization support.  The format must be
16516      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16517      */
16518     format : "H:i",
16519        
16520     onRender: function(ct, position)
16521     {
16522         
16523         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16524                 
16525         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16526         
16527         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16528         
16529         this.pop = this.picker().select('>.datepicker-time',true).first();
16530         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16531         
16532         this.picker().on('mousedown', this.onMousedown, this);
16533         this.picker().on('click', this.onClick, this);
16534         
16535         this.picker().addClass('datepicker-dropdown');
16536     
16537         this.fillTime();
16538         this.update();
16539             
16540         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16541         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16542         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16543         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16544         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16545         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16546
16547     },
16548     
16549     fireKey: function(e){
16550         if (!this.picker().isVisible()){
16551             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16552                 this.show();
16553             }
16554             return;
16555         }
16556
16557         e.preventDefault();
16558         
16559         switch(e.keyCode){
16560             case 27: // escape
16561                 this.hide();
16562                 break;
16563             case 37: // left
16564             case 39: // right
16565                 this.onTogglePeriod();
16566                 break;
16567             case 38: // up
16568                 this.onIncrementMinutes();
16569                 break;
16570             case 40: // down
16571                 this.onDecrementMinutes();
16572                 break;
16573             case 13: // enter
16574             case 9: // tab
16575                 this.setTime();
16576                 break;
16577         }
16578     },
16579     
16580     onClick: function(e) {
16581         e.stopPropagation();
16582         e.preventDefault();
16583     },
16584     
16585     picker : function()
16586     {
16587         return this.el.select('.datepicker', true).first();
16588     },
16589     
16590     fillTime: function()
16591     {    
16592         var time = this.pop.select('tbody', true).first();
16593         
16594         time.dom.innerHTML = '';
16595         
16596         time.createChild({
16597             tag: 'tr',
16598             cn: [
16599                 {
16600                     tag: 'td',
16601                     cn: [
16602                         {
16603                             tag: 'a',
16604                             href: '#',
16605                             cls: 'btn',
16606                             cn: [
16607                                 {
16608                                     tag: 'span',
16609                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16610                                 }
16611                             ]
16612                         } 
16613                     ]
16614                 },
16615                 {
16616                     tag: 'td',
16617                     cls: 'separator'
16618                 },
16619                 {
16620                     tag: 'td',
16621                     cn: [
16622                         {
16623                             tag: 'a',
16624                             href: '#',
16625                             cls: 'btn',
16626                             cn: [
16627                                 {
16628                                     tag: 'span',
16629                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16630                                 }
16631                             ]
16632                         }
16633                     ]
16634                 },
16635                 {
16636                     tag: 'td',
16637                     cls: 'separator'
16638                 }
16639             ]
16640         });
16641         
16642         time.createChild({
16643             tag: 'tr',
16644             cn: [
16645                 {
16646                     tag: 'td',
16647                     cn: [
16648                         {
16649                             tag: 'span',
16650                             cls: 'timepicker-hour',
16651                             html: '00'
16652                         }  
16653                     ]
16654                 },
16655                 {
16656                     tag: 'td',
16657                     cls: 'separator',
16658                     html: ':'
16659                 },
16660                 {
16661                     tag: 'td',
16662                     cn: [
16663                         {
16664                             tag: 'span',
16665                             cls: 'timepicker-minute',
16666                             html: '00'
16667                         }  
16668                     ]
16669                 },
16670                 {
16671                     tag: 'td',
16672                     cls: 'separator'
16673                 },
16674                 {
16675                     tag: 'td',
16676                     cn: [
16677                         {
16678                             tag: 'button',
16679                             type: 'button',
16680                             cls: 'btn btn-primary period',
16681                             html: 'AM'
16682                             
16683                         }
16684                     ]
16685                 }
16686             ]
16687         });
16688         
16689         time.createChild({
16690             tag: 'tr',
16691             cn: [
16692                 {
16693                     tag: 'td',
16694                     cn: [
16695                         {
16696                             tag: 'a',
16697                             href: '#',
16698                             cls: 'btn',
16699                             cn: [
16700                                 {
16701                                     tag: 'span',
16702                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16703                                 }
16704                             ]
16705                         }
16706                     ]
16707                 },
16708                 {
16709                     tag: 'td',
16710                     cls: 'separator'
16711                 },
16712                 {
16713                     tag: 'td',
16714                     cn: [
16715                         {
16716                             tag: 'a',
16717                             href: '#',
16718                             cls: 'btn',
16719                             cn: [
16720                                 {
16721                                     tag: 'span',
16722                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16723                                 }
16724                             ]
16725                         }
16726                     ]
16727                 },
16728                 {
16729                     tag: 'td',
16730                     cls: 'separator'
16731                 }
16732             ]
16733         });
16734         
16735     },
16736     
16737     update: function()
16738     {
16739         
16740         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16741         
16742         this.fill();
16743     },
16744     
16745     fill: function() 
16746     {
16747         var hours = this.time.getHours();
16748         var minutes = this.time.getMinutes();
16749         var period = 'AM';
16750         
16751         if(hours > 11){
16752             period = 'PM';
16753         }
16754         
16755         if(hours == 0){
16756             hours = 12;
16757         }
16758         
16759         
16760         if(hours > 12){
16761             hours = hours - 12;
16762         }
16763         
16764         if(hours < 10){
16765             hours = '0' + hours;
16766         }
16767         
16768         if(minutes < 10){
16769             minutes = '0' + minutes;
16770         }
16771         
16772         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16773         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16774         this.pop.select('button', true).first().dom.innerHTML = period;
16775         
16776     },
16777     
16778     place: function()
16779     {   
16780         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16781         
16782         var cls = ['bottom'];
16783         
16784         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16785             cls.pop();
16786             cls.push('top');
16787         }
16788         
16789         cls.push('right');
16790         
16791         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16792             cls.pop();
16793             cls.push('left');
16794         }
16795         
16796         this.picker().addClass(cls.join('-'));
16797         
16798         var _this = this;
16799         
16800         Roo.each(cls, function(c){
16801             if(c == 'bottom'){
16802                 _this.picker().setTop(_this.inputEl().getHeight());
16803                 return;
16804             }
16805             if(c == 'top'){
16806                 _this.picker().setTop(0 - _this.picker().getHeight());
16807                 return;
16808             }
16809             
16810             if(c == 'left'){
16811                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16812                 return;
16813             }
16814             if(c == 'right'){
16815                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16816                 return;
16817             }
16818         });
16819         
16820     },
16821   
16822     onFocus : function()
16823     {
16824         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16825         this.show();
16826     },
16827     
16828     onBlur : function()
16829     {
16830         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16831         this.hide();
16832     },
16833     
16834     show : function()
16835     {
16836         this.picker().show();
16837         this.pop.show();
16838         this.update();
16839         this.place();
16840         
16841         this.fireEvent('show', this, this.date);
16842     },
16843     
16844     hide : function()
16845     {
16846         this.picker().hide();
16847         this.pop.hide();
16848         
16849         this.fireEvent('hide', this, this.date);
16850     },
16851     
16852     setTime : function()
16853     {
16854         this.hide();
16855         this.setValue(this.time.format(this.format));
16856         
16857         this.fireEvent('select', this, this.date);
16858         
16859         
16860     },
16861     
16862     onMousedown: function(e){
16863         e.stopPropagation();
16864         e.preventDefault();
16865     },
16866     
16867     onIncrementHours: function()
16868     {
16869         Roo.log('onIncrementHours');
16870         this.time = this.time.add(Date.HOUR, 1);
16871         this.update();
16872         
16873     },
16874     
16875     onDecrementHours: function()
16876     {
16877         Roo.log('onDecrementHours');
16878         this.time = this.time.add(Date.HOUR, -1);
16879         this.update();
16880     },
16881     
16882     onIncrementMinutes: function()
16883     {
16884         Roo.log('onIncrementMinutes');
16885         this.time = this.time.add(Date.MINUTE, 1);
16886         this.update();
16887     },
16888     
16889     onDecrementMinutes: function()
16890     {
16891         Roo.log('onDecrementMinutes');
16892         this.time = this.time.add(Date.MINUTE, -1);
16893         this.update();
16894     },
16895     
16896     onTogglePeriod: function()
16897     {
16898         Roo.log('onTogglePeriod');
16899         this.time = this.time.add(Date.HOUR, 12);
16900         this.update();
16901     }
16902     
16903    
16904 });
16905
16906 Roo.apply(Roo.bootstrap.TimeField,  {
16907     
16908     content : {
16909         tag: 'tbody',
16910         cn: [
16911             {
16912                 tag: 'tr',
16913                 cn: [
16914                 {
16915                     tag: 'td',
16916                     colspan: '7'
16917                 }
16918                 ]
16919             }
16920         ]
16921     },
16922     
16923     footer : {
16924         tag: 'tfoot',
16925         cn: [
16926             {
16927                 tag: 'tr',
16928                 cn: [
16929                 {
16930                     tag: 'th',
16931                     colspan: '7',
16932                     cls: '',
16933                     cn: [
16934                         {
16935                             tag: 'button',
16936                             cls: 'btn btn-info ok',
16937                             html: 'OK'
16938                         }
16939                     ]
16940                 }
16941
16942                 ]
16943             }
16944         ]
16945     }
16946 });
16947
16948 Roo.apply(Roo.bootstrap.TimeField,  {
16949   
16950     template : {
16951         tag: 'div',
16952         cls: 'datepicker dropdown-menu',
16953         cn: [
16954             {
16955                 tag: 'div',
16956                 cls: 'datepicker-time',
16957                 cn: [
16958                 {
16959                     tag: 'table',
16960                     cls: 'table-condensed',
16961                     cn:[
16962                     Roo.bootstrap.TimeField.content,
16963                     Roo.bootstrap.TimeField.footer
16964                     ]
16965                 }
16966                 ]
16967             }
16968         ]
16969     }
16970 });
16971
16972  
16973
16974  /*
16975  * - LGPL
16976  *
16977  * MonthField
16978  * 
16979  */
16980
16981 /**
16982  * @class Roo.bootstrap.MonthField
16983  * @extends Roo.bootstrap.Input
16984  * Bootstrap MonthField class
16985  * 
16986  * @cfg {String} language default en
16987  * 
16988  * @constructor
16989  * Create a new MonthField
16990  * @param {Object} config The config object
16991  */
16992
16993 Roo.bootstrap.MonthField = function(config){
16994     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16995     
16996     this.addEvents({
16997         /**
16998          * @event show
16999          * Fires when this field show.
17000          * @param {Roo.bootstrap.MonthField} this
17001          * @param {Mixed} date The date value
17002          */
17003         show : true,
17004         /**
17005          * @event show
17006          * Fires when this field hide.
17007          * @param {Roo.bootstrap.MonthField} this
17008          * @param {Mixed} date The date value
17009          */
17010         hide : true,
17011         /**
17012          * @event select
17013          * Fires when select a date.
17014          * @param {Roo.bootstrap.MonthField} this
17015          * @param {String} oldvalue The old value
17016          * @param {String} newvalue The new value
17017          */
17018         select : true
17019     });
17020 };
17021
17022 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17023     
17024     onRender: function(ct, position)
17025     {
17026         
17027         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17028         
17029         this.language = this.language || 'en';
17030         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17031         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17032         
17033         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17034         this.isInline = false;
17035         this.isInput = true;
17036         this.component = this.el.select('.add-on', true).first() || false;
17037         this.component = (this.component && this.component.length === 0) ? false : this.component;
17038         this.hasInput = this.component && this.inputEL().length;
17039         
17040         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17041         
17042         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17043         
17044         this.picker().on('mousedown', this.onMousedown, this);
17045         this.picker().on('click', this.onClick, this);
17046         
17047         this.picker().addClass('datepicker-dropdown');
17048         
17049         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17050             v.setStyle('width', '189px');
17051         });
17052         
17053         this.fillMonths();
17054         
17055         this.update();
17056         
17057         if(this.isInline) {
17058             this.show();
17059         }
17060         
17061     },
17062     
17063     setValue: function(v, suppressEvent)
17064     {   
17065         var o = this.getValue();
17066         
17067         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17068         
17069         this.update();
17070
17071         if(suppressEvent !== true){
17072             this.fireEvent('select', this, o, v);
17073         }
17074         
17075     },
17076     
17077     getValue: function()
17078     {
17079         return this.value;
17080     },
17081     
17082     onClick: function(e) 
17083     {
17084         e.stopPropagation();
17085         e.preventDefault();
17086         
17087         var target = e.getTarget();
17088         
17089         if(target.nodeName.toLowerCase() === 'i'){
17090             target = Roo.get(target).dom.parentNode;
17091         }
17092         
17093         var nodeName = target.nodeName;
17094         var className = target.className;
17095         var html = target.innerHTML;
17096         
17097         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17098             return;
17099         }
17100         
17101         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17102         
17103         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17104         
17105         this.hide();
17106                         
17107     },
17108     
17109     picker : function()
17110     {
17111         return this.pickerEl;
17112     },
17113     
17114     fillMonths: function()
17115     {    
17116         var i = 0;
17117         var months = this.picker().select('>.datepicker-months td', true).first();
17118         
17119         months.dom.innerHTML = '';
17120         
17121         while (i < 12) {
17122             var month = {
17123                 tag: 'span',
17124                 cls: 'month',
17125                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17126             }
17127             
17128             months.createChild(month);
17129         }
17130         
17131     },
17132     
17133     update: function()
17134     {
17135         var _this = this;
17136         
17137         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17138             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17139         }
17140         
17141         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17142             e.removeClass('active');
17143             
17144             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17145                 e.addClass('active');
17146             }
17147         })
17148     },
17149     
17150     place: function()
17151     {
17152         if(this.isInline) return;
17153         
17154         this.picker().removeClass(['bottom', 'top']);
17155         
17156         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17157             /*
17158              * place to the top of element!
17159              *
17160              */
17161             
17162             this.picker().addClass('top');
17163             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17164             
17165             return;
17166         }
17167         
17168         this.picker().addClass('bottom');
17169         
17170         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17171     },
17172     
17173     onFocus : function()
17174     {
17175         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17176         this.show();
17177     },
17178     
17179     onBlur : function()
17180     {
17181         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17182         
17183         var d = this.inputEl().getValue();
17184         
17185         this.setValue(d);
17186                 
17187         this.hide();
17188     },
17189     
17190     show : function()
17191     {
17192         this.picker().show();
17193         this.picker().select('>.datepicker-months', true).first().show();
17194         this.update();
17195         this.place();
17196         
17197         this.fireEvent('show', this, this.date);
17198     },
17199     
17200     hide : function()
17201     {
17202         if(this.isInline) return;
17203         this.picker().hide();
17204         this.fireEvent('hide', this, this.date);
17205         
17206     },
17207     
17208     onMousedown: function(e)
17209     {
17210         e.stopPropagation();
17211         e.preventDefault();
17212     },
17213     
17214     keyup: function(e)
17215     {
17216         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17217         this.update();
17218     },
17219
17220     fireKey: function(e)
17221     {
17222         if (!this.picker().isVisible()){
17223             if (e.keyCode == 27) // allow escape to hide and re-show picker
17224                 this.show();
17225             return;
17226         }
17227         
17228         var dir;
17229         
17230         switch(e.keyCode){
17231             case 27: // escape
17232                 this.hide();
17233                 e.preventDefault();
17234                 break;
17235             case 37: // left
17236             case 39: // right
17237                 dir = e.keyCode == 37 ? -1 : 1;
17238                 
17239                 this.vIndex = this.vIndex + dir;
17240                 
17241                 if(this.vIndex < 0){
17242                     this.vIndex = 0;
17243                 }
17244                 
17245                 if(this.vIndex > 11){
17246                     this.vIndex = 11;
17247                 }
17248                 
17249                 if(isNaN(this.vIndex)){
17250                     this.vIndex = 0;
17251                 }
17252                 
17253                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17254                 
17255                 break;
17256             case 38: // up
17257             case 40: // down
17258                 
17259                 dir = e.keyCode == 38 ? -1 : 1;
17260                 
17261                 this.vIndex = this.vIndex + dir * 4;
17262                 
17263                 if(this.vIndex < 0){
17264                     this.vIndex = 0;
17265                 }
17266                 
17267                 if(this.vIndex > 11){
17268                     this.vIndex = 11;
17269                 }
17270                 
17271                 if(isNaN(this.vIndex)){
17272                     this.vIndex = 0;
17273                 }
17274                 
17275                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17276                 break;
17277                 
17278             case 13: // enter
17279                 
17280                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17281                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17282                 }
17283                 
17284                 this.hide();
17285                 e.preventDefault();
17286                 break;
17287             case 9: // tab
17288                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17289                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17290                 }
17291                 this.hide();
17292                 break;
17293             case 16: // shift
17294             case 17: // ctrl
17295             case 18: // alt
17296                 break;
17297             default :
17298                 this.hide();
17299                 
17300         }
17301     },
17302     
17303     remove: function() 
17304     {
17305         this.picker().remove();
17306     }
17307    
17308 });
17309
17310 Roo.apply(Roo.bootstrap.MonthField,  {
17311     
17312     content : {
17313         tag: 'tbody',
17314         cn: [
17315         {
17316             tag: 'tr',
17317             cn: [
17318             {
17319                 tag: 'td',
17320                 colspan: '7'
17321             }
17322             ]
17323         }
17324         ]
17325     },
17326     
17327     dates:{
17328         en: {
17329             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17330             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17331         }
17332     }
17333 });
17334
17335 Roo.apply(Roo.bootstrap.MonthField,  {
17336   
17337     template : {
17338         tag: 'div',
17339         cls: 'datepicker dropdown-menu roo-dynamic',
17340         cn: [
17341             {
17342                 tag: 'div',
17343                 cls: 'datepicker-months',
17344                 cn: [
17345                 {
17346                     tag: 'table',
17347                     cls: 'table-condensed',
17348                     cn:[
17349                         Roo.bootstrap.DateField.content
17350                     ]
17351                 }
17352                 ]
17353             }
17354         ]
17355     }
17356 });
17357
17358  
17359
17360  
17361  /*
17362  * - LGPL
17363  *
17364  * CheckBox
17365  * 
17366  */
17367
17368 /**
17369  * @class Roo.bootstrap.CheckBox
17370  * @extends Roo.bootstrap.Input
17371  * Bootstrap CheckBox class
17372  * 
17373  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17374  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17375  * @cfg {String} boxLabel The text that appears beside the checkbox
17376  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17377  * @cfg {Boolean} checked initnal the element
17378  * @cfg {Boolean} inline inline the element (default false)
17379  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17380  * 
17381  * @constructor
17382  * Create a new CheckBox
17383  * @param {Object} config The config object
17384  */
17385
17386 Roo.bootstrap.CheckBox = function(config){
17387     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17388    
17389     this.addEvents({
17390         /**
17391         * @event check
17392         * Fires when the element is checked or unchecked.
17393         * @param {Roo.bootstrap.CheckBox} this This input
17394         * @param {Boolean} checked The new checked value
17395         */
17396        check : true
17397     });
17398     
17399 };
17400
17401 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17402   
17403     inputType: 'checkbox',
17404     inputValue: 1,
17405     valueOff: 0,
17406     boxLabel: false,
17407     checked: false,
17408     weight : false,
17409     inline: false,
17410     
17411     getAutoCreate : function()
17412     {
17413         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17414         
17415         var id = Roo.id();
17416         
17417         var cfg = {};
17418         
17419         cfg.cls = 'form-group ' + this.inputType; //input-group
17420         
17421         if(this.inline){
17422             cfg.cls += ' ' + this.inputType + '-inline';
17423         }
17424         
17425         var input =  {
17426             tag: 'input',
17427             id : id,
17428             type : this.inputType,
17429             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17430             cls : 'roo-' + this.inputType, //'form-box',
17431             placeholder : this.placeholder || ''
17432             
17433         };
17434         
17435         if (this.weight) { // Validity check?
17436             cfg.cls += " " + this.inputType + "-" + this.weight;
17437         }
17438         
17439         if (this.disabled) {
17440             input.disabled=true;
17441         }
17442         
17443         if(this.checked){
17444             input.checked = this.checked;
17445         }
17446         
17447         if (this.name) {
17448             input.name = this.name;
17449         }
17450         
17451         if (this.size) {
17452             input.cls += ' input-' + this.size;
17453         }
17454         
17455         var settings=this;
17456         
17457         ['xs','sm','md','lg'].map(function(size){
17458             if (settings[size]) {
17459                 cfg.cls += ' col-' + size + '-' + settings[size];
17460             }
17461         });
17462         
17463         var inputblock = input;
17464          
17465         if (this.before || this.after) {
17466             
17467             inputblock = {
17468                 cls : 'input-group',
17469                 cn :  [] 
17470             };
17471             
17472             if (this.before) {
17473                 inputblock.cn.push({
17474                     tag :'span',
17475                     cls : 'input-group-addon',
17476                     html : this.before
17477                 });
17478             }
17479             
17480             inputblock.cn.push(input);
17481             
17482             if (this.after) {
17483                 inputblock.cn.push({
17484                     tag :'span',
17485                     cls : 'input-group-addon',
17486                     html : this.after
17487                 });
17488             }
17489             
17490         }
17491         
17492         if (align ==='left' && this.fieldLabel.length) {
17493                 Roo.log("left and has label");
17494                 cfg.cn = [
17495                     
17496                     {
17497                         tag: 'label',
17498                         'for' :  id,
17499                         cls : 'control-label col-md-' + this.labelWidth,
17500                         html : this.fieldLabel
17501                         
17502                     },
17503                     {
17504                         cls : "col-md-" + (12 - this.labelWidth), 
17505                         cn: [
17506                             inputblock
17507                         ]
17508                     }
17509                     
17510                 ];
17511         } else if ( this.fieldLabel.length) {
17512                 Roo.log(" label");
17513                 cfg.cn = [
17514                    
17515                     {
17516                         tag: this.boxLabel ? 'span' : 'label',
17517                         'for': id,
17518                         cls: 'control-label box-input-label',
17519                         //cls : 'input-group-addon',
17520                         html : this.fieldLabel
17521                         
17522                     },
17523                     
17524                     inputblock
17525                     
17526                 ];
17527
17528         } else {
17529             
17530                 Roo.log(" no label && no align");
17531                 cfg.cn = [  inputblock ] ;
17532                 
17533                 
17534         }
17535         if(this.boxLabel){
17536              var boxLabelCfg = {
17537                 tag: 'label',
17538                 //'for': id, // box label is handled by onclick - so no for...
17539                 cls: 'box-label',
17540                 html: this.boxLabel
17541             }
17542             
17543             if(this.tooltip){
17544                 boxLabelCfg.tooltip = this.tooltip;
17545             }
17546              
17547             cfg.cn.push(boxLabelCfg);
17548         }
17549         
17550         
17551        
17552         return cfg;
17553         
17554     },
17555     
17556     /**
17557      * return the real input element.
17558      */
17559     inputEl: function ()
17560     {
17561         return this.el.select('input.roo-' + this.inputType,true).first();
17562     },
17563     
17564     labelEl: function()
17565     {
17566         return this.el.select('label.control-label',true).first();
17567     },
17568     /* depricated... */
17569     
17570     label: function()
17571     {
17572         return this.labelEl();
17573     },
17574     
17575     initEvents : function()
17576     {
17577 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17578         
17579         this.inputEl().on('click', this.onClick,  this);
17580         
17581         if (this.boxLabel) { 
17582             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17583         }
17584         
17585         this.startValue = this.getValue();
17586         
17587         if(this.groupId){
17588             Roo.bootstrap.CheckBox.register(this);
17589         }
17590     },
17591     
17592     onClick : function()
17593     {   
17594         this.setChecked(!this.checked);
17595     },
17596     
17597     setChecked : function(state,suppressEvent)
17598     {
17599         this.startValue = this.getValue();
17600         
17601         if(this.inputType == 'radio'){
17602             
17603             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17604                 e.dom.checked = false;
17605             });
17606             
17607             this.inputEl().dom.checked = true;
17608             
17609             this.inputEl().dom.value = this.inputValue;
17610             
17611             if(suppressEvent !== true){
17612                 this.fireEvent('check', this, true);
17613             }
17614             
17615             this.validate();
17616             
17617             return;
17618         }
17619         
17620         this.checked = state;
17621         
17622         this.inputEl().dom.checked = state;
17623         
17624         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17625         
17626         if(suppressEvent !== true){
17627             this.fireEvent('check', this, state);
17628         }
17629         
17630         this.validate();
17631     },
17632     
17633     getValue : function()
17634     {
17635         if(this.inputType == 'radio'){
17636             return this.getGroupValue();
17637         }
17638         
17639         return this.inputEl().getValue();
17640         
17641     },
17642     
17643     getGroupValue : function()
17644     {
17645         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17646             return '';
17647         }
17648         
17649         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17650     },
17651     
17652     setValue : function(v,suppressEvent)
17653     {
17654         if(this.inputType == 'radio'){
17655             this.setGroupValue(v, suppressEvent);
17656             return;
17657         }
17658         
17659         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17660         
17661         this.validate();
17662     },
17663     
17664     setGroupValue : function(v, suppressEvent)
17665     {
17666         this.startValue = this.getValue();
17667         
17668         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17669             e.dom.checked = false;
17670             
17671             if(e.dom.value == v){
17672                 e.dom.checked = true;
17673             }
17674         });
17675         
17676         if(suppressEvent !== true){
17677             this.fireEvent('check', this, true);
17678         }
17679
17680         this.validate();
17681         
17682         return;
17683     },
17684     
17685     validate : function()
17686     {
17687         if(
17688                 this.disabled || 
17689                 (this.inputType == 'radio' && this.validateRadio()) ||
17690                 (this.inputType == 'checkbox' && this.validateCheckbox())
17691         ){
17692             this.markValid();
17693             return true;
17694         }
17695         
17696         this.markInvalid();
17697         return false;
17698     },
17699     
17700     validateRadio : function()
17701     {
17702         var valid = false;
17703         
17704         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17705             if(!e.dom.checked){
17706                 return;
17707             }
17708             
17709             valid = true;
17710             
17711             return false;
17712         });
17713         
17714         return valid;
17715     },
17716     
17717     validateCheckbox : function()
17718     {
17719         if(!this.groupId){
17720             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17721         }
17722         
17723         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17724         
17725         if(!group){
17726             return false;
17727         }
17728         
17729         var r = false;
17730         
17731         for(var i in group){
17732             if(r){
17733                 break;
17734             }
17735             
17736             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17737         }
17738         
17739         return r;
17740     },
17741     
17742     /**
17743      * Mark this field as valid
17744      */
17745     markValid : function()
17746     {
17747         if(this.allowBlank){
17748             return;
17749         }
17750         
17751         var _this = this;
17752         
17753         this.fireEvent('valid', this);
17754         
17755         if(this.inputType == 'radio'){
17756             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17757                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17758                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17759             });
17760             
17761             return;
17762         }
17763         
17764         if(!this.groupId){
17765             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17766             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17767             return;
17768         }
17769         
17770         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17771             
17772         if(!group){
17773             return;
17774         }
17775         
17776         for(var i in group){
17777             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17778             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17779         }
17780     },
17781     
17782      /**
17783      * Mark this field as invalid
17784      * @param {String} msg The validation message
17785      */
17786     markInvalid : function(msg)
17787     {
17788         if(this.allowBlank){
17789             return;
17790         }
17791         
17792         var _this = this;
17793         
17794         this.fireEvent('invalid', this, msg);
17795         
17796         if(this.inputType == 'radio'){
17797             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17798                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17799                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17800             });
17801             
17802             return;
17803         }
17804         
17805         if(!this.groupId){
17806             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17807             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17808             return;
17809         }
17810         
17811         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17812             
17813         if(!group){
17814             return;
17815         }
17816         
17817         for(var i in group){
17818             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17819             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17820         }
17821         
17822     }
17823     
17824 });
17825
17826 Roo.apply(Roo.bootstrap.CheckBox, {
17827     
17828     groups: {},
17829     
17830      /**
17831     * register a CheckBox Group
17832     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17833     */
17834     register : function(checkbox)
17835     {
17836         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17837             this.groups[checkbox.groupId] = {};
17838         }
17839         
17840         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17841             return;
17842         }
17843         
17844         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17845         
17846     },
17847     /**
17848     * fetch a CheckBox Group based on the group ID
17849     * @param {string} the group ID
17850     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17851     */
17852     get: function(groupId) {
17853         if (typeof(this.groups[groupId]) == 'undefined') {
17854             return false;
17855         }
17856         
17857         return this.groups[groupId] ;
17858     }
17859     
17860     
17861 });
17862 /*
17863  * - LGPL
17864  *
17865  * Radio
17866  *
17867  *
17868  * not inline
17869  *<div class="radio">
17870   <label>
17871     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17872     Option one is this and that&mdash;be sure to include why it's great
17873   </label>
17874 </div>
17875  *
17876  *
17877  *inline
17878  *<span>
17879  *<label class="radio-inline">fieldLabel</label>
17880  *<label class="radio-inline">
17881   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17882 </label>
17883 <span>
17884  * 
17885  * 
17886  */
17887
17888 /**
17889  * @class Roo.bootstrap.Radio
17890  * @extends Roo.bootstrap.CheckBox
17891  * Bootstrap Radio class
17892
17893  * @constructor
17894  * Create a new Radio
17895  * @param {Object} config The config object
17896  */
17897
17898 Roo.bootstrap.Radio = function(config){
17899     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17900    
17901 };
17902
17903 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17904     
17905     inputType: 'radio',
17906     inputValue: '',
17907     valueOff: '',
17908     
17909     getAutoCreate : function()
17910     {
17911         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17912         align = align || 'left'; // default...
17913         
17914         
17915         
17916         var id = Roo.id();
17917         
17918         var cfg = {
17919                 tag : this.inline ? 'span' : 'div',
17920                 cls : '',
17921                 cn : []
17922         };
17923         
17924         var inline = this.inline ? ' radio-inline' : '';
17925         
17926         var lbl = {
17927                 tag: 'label' ,
17928                 // does not need for, as we wrap the input with it..
17929                 'for' : id,
17930                 cls : 'control-label box-label' + inline,
17931                 cn : []
17932         };
17933         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17934         
17935         var fieldLabel = {
17936             tag: 'label' ,
17937             //cls : 'control-label' + inline,
17938             html : this.fieldLabel,
17939             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17940         };
17941         
17942  
17943         
17944         
17945         var input =  {
17946             tag: 'input',
17947             id : id,
17948             type : this.inputType,
17949             //value : (!this.checked) ? this.valueOff : this.inputValue,
17950             value : this.inputValue,
17951             cls : 'roo-radio',
17952             placeholder : this.placeholder || '' // ?? needed????
17953             
17954         };
17955         if (this.weight) { // Validity check?
17956             input.cls += " radio-" + this.weight;
17957         }
17958         if (this.disabled) {
17959             input.disabled=true;
17960         }
17961         
17962         if(this.checked){
17963             input.checked = this.checked;
17964         }
17965         
17966         if (this.name) {
17967             input.name = this.name;
17968         }
17969         
17970         if (this.size) {
17971             input.cls += ' input-' + this.size;
17972         }
17973         
17974         //?? can span's inline have a width??
17975         
17976         var settings=this;
17977         ['xs','sm','md','lg'].map(function(size){
17978             if (settings[size]) {
17979                 cfg.cls += ' col-' + size + '-' + settings[size];
17980             }
17981         });
17982         
17983         var inputblock = input;
17984         
17985         if (this.before || this.after) {
17986             
17987             inputblock = {
17988                 cls : 'input-group',
17989                 tag : 'span',
17990                 cn :  [] 
17991             };
17992             if (this.before) {
17993                 inputblock.cn.push({
17994                     tag :'span',
17995                     cls : 'input-group-addon',
17996                     html : this.before
17997                 });
17998             }
17999             inputblock.cn.push(input);
18000             if (this.after) {
18001                 inputblock.cn.push({
18002                     tag :'span',
18003                     cls : 'input-group-addon',
18004                     html : this.after
18005                 });
18006             }
18007             
18008         };
18009         
18010         
18011         if (this.fieldLabel && this.fieldLabel.length) {
18012             cfg.cn.push(fieldLabel);
18013         }
18014        
18015         // normal bootstrap puts the input inside the label.
18016         // however with our styled version - it has to go after the input.
18017        
18018         //lbl.cn.push(inputblock);
18019         
18020         var lblwrap =  {
18021             tag: 'span',
18022             cls: 'radio' + inline,
18023             cn: [
18024                 inputblock,
18025                 lbl
18026             ]
18027         };
18028         
18029         cfg.cn.push( lblwrap);
18030         
18031         if(this.boxLabel){
18032             lbl.cn.push({
18033                 tag: 'span',
18034                 html: this.boxLabel
18035             })
18036         }
18037          
18038         
18039         return cfg;
18040         
18041     },
18042     
18043     initEvents : function()
18044     {
18045 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18046         
18047         this.inputEl().on('click', this.onClick,  this);
18048         if (this.boxLabel) {
18049             Roo.log('find label')
18050             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18051         }
18052         
18053     },
18054     
18055     inputEl: function ()
18056     {
18057         return this.el.select('input.roo-radio',true).first();
18058     },
18059     onClick : function()
18060     {   
18061         Roo.log("click");
18062         this.setChecked(true);
18063     },
18064     
18065     setChecked : function(state,suppressEvent)
18066     {
18067         if(state){
18068             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18069                 v.dom.checked = false;
18070             });
18071         }
18072         Roo.log(this.inputEl().dom);
18073         this.checked = state;
18074         this.inputEl().dom.checked = state;
18075         
18076         if(suppressEvent !== true){
18077             this.fireEvent('check', this, state);
18078         }
18079         
18080         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18081         
18082     },
18083     
18084     getGroupValue : function()
18085     {
18086         var value = '';
18087         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18088             if(v.dom.checked == true){
18089                 value = v.dom.value;
18090             }
18091         });
18092         
18093         return value;
18094     },
18095     
18096     /**
18097      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18098      * @return {Mixed} value The field value
18099      */
18100     getValue : function(){
18101         return this.getGroupValue();
18102     }
18103     
18104 });
18105
18106  
18107 //<script type="text/javascript">
18108
18109 /*
18110  * Based  Ext JS Library 1.1.1
18111  * Copyright(c) 2006-2007, Ext JS, LLC.
18112  * LGPL
18113  *
18114  */
18115  
18116 /**
18117  * @class Roo.HtmlEditorCore
18118  * @extends Roo.Component
18119  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18120  *
18121  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18122  */
18123
18124 Roo.HtmlEditorCore = function(config){
18125     
18126     
18127     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18128     
18129     
18130     this.addEvents({
18131         /**
18132          * @event initialize
18133          * Fires when the editor is fully initialized (including the iframe)
18134          * @param {Roo.HtmlEditorCore} this
18135          */
18136         initialize: true,
18137         /**
18138          * @event activate
18139          * Fires when the editor is first receives the focus. Any insertion must wait
18140          * until after this event.
18141          * @param {Roo.HtmlEditorCore} this
18142          */
18143         activate: true,
18144          /**
18145          * @event beforesync
18146          * Fires before the textarea is updated with content from the editor iframe. Return false
18147          * to cancel the sync.
18148          * @param {Roo.HtmlEditorCore} this
18149          * @param {String} html
18150          */
18151         beforesync: true,
18152          /**
18153          * @event beforepush
18154          * Fires before the iframe editor is updated with content from the textarea. Return false
18155          * to cancel the push.
18156          * @param {Roo.HtmlEditorCore} this
18157          * @param {String} html
18158          */
18159         beforepush: true,
18160          /**
18161          * @event sync
18162          * Fires when the textarea is updated with content from the editor iframe.
18163          * @param {Roo.HtmlEditorCore} this
18164          * @param {String} html
18165          */
18166         sync: true,
18167          /**
18168          * @event push
18169          * Fires when the iframe editor is updated with content from the textarea.
18170          * @param {Roo.HtmlEditorCore} this
18171          * @param {String} html
18172          */
18173         push: true,
18174         
18175         /**
18176          * @event editorevent
18177          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18178          * @param {Roo.HtmlEditorCore} this
18179          */
18180         editorevent: true
18181         
18182     });
18183     
18184     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18185     
18186     // defaults : white / black...
18187     this.applyBlacklists();
18188     
18189     
18190     
18191 };
18192
18193
18194 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18195
18196
18197      /**
18198      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18199      */
18200     
18201     owner : false,
18202     
18203      /**
18204      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18205      *                        Roo.resizable.
18206      */
18207     resizable : false,
18208      /**
18209      * @cfg {Number} height (in pixels)
18210      */   
18211     height: 300,
18212    /**
18213      * @cfg {Number} width (in pixels)
18214      */   
18215     width: 500,
18216     
18217     /**
18218      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18219      * 
18220      */
18221     stylesheets: false,
18222     
18223     // id of frame..
18224     frameId: false,
18225     
18226     // private properties
18227     validationEvent : false,
18228     deferHeight: true,
18229     initialized : false,
18230     activated : false,
18231     sourceEditMode : false,
18232     onFocus : Roo.emptyFn,
18233     iframePad:3,
18234     hideMode:'offsets',
18235     
18236     clearUp: true,
18237     
18238     // blacklist + whitelisted elements..
18239     black: false,
18240     white: false,
18241      
18242     
18243
18244     /**
18245      * Protected method that will not generally be called directly. It
18246      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18247      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18248      */
18249     getDocMarkup : function(){
18250         // body styles..
18251         var st = '';
18252         
18253         // inherit styels from page...?? 
18254         if (this.stylesheets === false) {
18255             
18256             Roo.get(document.head).select('style').each(function(node) {
18257                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18258             });
18259             
18260             Roo.get(document.head).select('link').each(function(node) { 
18261                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18262             });
18263             
18264         } else if (!this.stylesheets.length) {
18265                 // simple..
18266                 st = '<style type="text/css">' +
18267                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18268                    '</style>';
18269         } else { 
18270             
18271         }
18272         
18273         st +=  '<style type="text/css">' +
18274             'IMG { cursor: pointer } ' +
18275         '</style>';
18276
18277         
18278         return '<html><head>' + st  +
18279             //<style type="text/css">' +
18280             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18281             //'</style>' +
18282             ' </head><body class="roo-htmleditor-body"></body></html>';
18283     },
18284
18285     // private
18286     onRender : function(ct, position)
18287     {
18288         var _t = this;
18289         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18290         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18291         
18292         
18293         this.el.dom.style.border = '0 none';
18294         this.el.dom.setAttribute('tabIndex', -1);
18295         this.el.addClass('x-hidden hide');
18296         
18297         
18298         
18299         if(Roo.isIE){ // fix IE 1px bogus margin
18300             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18301         }
18302        
18303         
18304         this.frameId = Roo.id();
18305         
18306          
18307         
18308         var iframe = this.owner.wrap.createChild({
18309             tag: 'iframe',
18310             cls: 'form-control', // bootstrap..
18311             id: this.frameId,
18312             name: this.frameId,
18313             frameBorder : 'no',
18314             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18315         }, this.el
18316         );
18317         
18318         
18319         this.iframe = iframe.dom;
18320
18321          this.assignDocWin();
18322         
18323         this.doc.designMode = 'on';
18324        
18325         this.doc.open();
18326         this.doc.write(this.getDocMarkup());
18327         this.doc.close();
18328
18329         
18330         var task = { // must defer to wait for browser to be ready
18331             run : function(){
18332                 //console.log("run task?" + this.doc.readyState);
18333                 this.assignDocWin();
18334                 if(this.doc.body || this.doc.readyState == 'complete'){
18335                     try {
18336                         this.doc.designMode="on";
18337                     } catch (e) {
18338                         return;
18339                     }
18340                     Roo.TaskMgr.stop(task);
18341                     this.initEditor.defer(10, this);
18342                 }
18343             },
18344             interval : 10,
18345             duration: 10000,
18346             scope: this
18347         };
18348         Roo.TaskMgr.start(task);
18349
18350     },
18351
18352     // private
18353     onResize : function(w, h)
18354     {
18355          Roo.log('resize: ' +w + ',' + h );
18356         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18357         if(!this.iframe){
18358             return;
18359         }
18360         if(typeof w == 'number'){
18361             
18362             this.iframe.style.width = w + 'px';
18363         }
18364         if(typeof h == 'number'){
18365             
18366             this.iframe.style.height = h + 'px';
18367             if(this.doc){
18368                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18369             }
18370         }
18371         
18372     },
18373
18374     /**
18375      * Toggles the editor between standard and source edit mode.
18376      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18377      */
18378     toggleSourceEdit : function(sourceEditMode){
18379         
18380         this.sourceEditMode = sourceEditMode === true;
18381         
18382         if(this.sourceEditMode){
18383  
18384             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18385             
18386         }else{
18387             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18388             //this.iframe.className = '';
18389             this.deferFocus();
18390         }
18391         //this.setSize(this.owner.wrap.getSize());
18392         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18393     },
18394
18395     
18396   
18397
18398     /**
18399      * Protected method that will not generally be called directly. If you need/want
18400      * custom HTML cleanup, this is the method you should override.
18401      * @param {String} html The HTML to be cleaned
18402      * return {String} The cleaned HTML
18403      */
18404     cleanHtml : function(html){
18405         html = String(html);
18406         if(html.length > 5){
18407             if(Roo.isSafari){ // strip safari nonsense
18408                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18409             }
18410         }
18411         if(html == '&nbsp;'){
18412             html = '';
18413         }
18414         return html;
18415     },
18416
18417     /**
18418      * HTML Editor -> Textarea
18419      * Protected method that will not generally be called directly. Syncs the contents
18420      * of the editor iframe with the textarea.
18421      */
18422     syncValue : function(){
18423         if(this.initialized){
18424             var bd = (this.doc.body || this.doc.documentElement);
18425             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18426             var html = bd.innerHTML;
18427             if(Roo.isSafari){
18428                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18429                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18430                 if(m && m[1]){
18431                     html = '<div style="'+m[0]+'">' + html + '</div>';
18432                 }
18433             }
18434             html = this.cleanHtml(html);
18435             // fix up the special chars.. normaly like back quotes in word...
18436             // however we do not want to do this with chinese..
18437             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18438                 var cc = b.charCodeAt();
18439                 if (
18440                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18441                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18442                     (cc >= 0xf900 && cc < 0xfb00 )
18443                 ) {
18444                         return b;
18445                 }
18446                 return "&#"+cc+";" 
18447             });
18448             if(this.owner.fireEvent('beforesync', this, html) !== false){
18449                 this.el.dom.value = html;
18450                 this.owner.fireEvent('sync', this, html);
18451             }
18452         }
18453     },
18454
18455     /**
18456      * Protected method that will not generally be called directly. Pushes the value of the textarea
18457      * into the iframe editor.
18458      */
18459     pushValue : function(){
18460         if(this.initialized){
18461             var v = this.el.dom.value.trim();
18462             
18463 //            if(v.length < 1){
18464 //                v = '&#160;';
18465 //            }
18466             
18467             if(this.owner.fireEvent('beforepush', this, v) !== false){
18468                 var d = (this.doc.body || this.doc.documentElement);
18469                 d.innerHTML = v;
18470                 this.cleanUpPaste();
18471                 this.el.dom.value = d.innerHTML;
18472                 this.owner.fireEvent('push', this, v);
18473             }
18474         }
18475     },
18476
18477     // private
18478     deferFocus : function(){
18479         this.focus.defer(10, this);
18480     },
18481
18482     // doc'ed in Field
18483     focus : function(){
18484         if(this.win && !this.sourceEditMode){
18485             this.win.focus();
18486         }else{
18487             this.el.focus();
18488         }
18489     },
18490     
18491     assignDocWin: function()
18492     {
18493         var iframe = this.iframe;
18494         
18495          if(Roo.isIE){
18496             this.doc = iframe.contentWindow.document;
18497             this.win = iframe.contentWindow;
18498         } else {
18499 //            if (!Roo.get(this.frameId)) {
18500 //                return;
18501 //            }
18502 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18503 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18504             
18505             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18506                 return;
18507             }
18508             
18509             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18510             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18511         }
18512     },
18513     
18514     // private
18515     initEditor : function(){
18516         //console.log("INIT EDITOR");
18517         this.assignDocWin();
18518         
18519         
18520         
18521         this.doc.designMode="on";
18522         this.doc.open();
18523         this.doc.write(this.getDocMarkup());
18524         this.doc.close();
18525         
18526         var dbody = (this.doc.body || this.doc.documentElement);
18527         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18528         // this copies styles from the containing element into thsi one..
18529         // not sure why we need all of this..
18530         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18531         
18532         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18533         //ss['background-attachment'] = 'fixed'; // w3c
18534         dbody.bgProperties = 'fixed'; // ie
18535         //Roo.DomHelper.applyStyles(dbody, ss);
18536         Roo.EventManager.on(this.doc, {
18537             //'mousedown': this.onEditorEvent,
18538             'mouseup': this.onEditorEvent,
18539             'dblclick': this.onEditorEvent,
18540             'click': this.onEditorEvent,
18541             'keyup': this.onEditorEvent,
18542             buffer:100,
18543             scope: this
18544         });
18545         if(Roo.isGecko){
18546             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18547         }
18548         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18549             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18550         }
18551         this.initialized = true;
18552
18553         this.owner.fireEvent('initialize', this);
18554         this.pushValue();
18555     },
18556
18557     // private
18558     onDestroy : function(){
18559         
18560         
18561         
18562         if(this.rendered){
18563             
18564             //for (var i =0; i < this.toolbars.length;i++) {
18565             //    // fixme - ask toolbars for heights?
18566             //    this.toolbars[i].onDestroy();
18567            // }
18568             
18569             //this.wrap.dom.innerHTML = '';
18570             //this.wrap.remove();
18571         }
18572     },
18573
18574     // private
18575     onFirstFocus : function(){
18576         
18577         this.assignDocWin();
18578         
18579         
18580         this.activated = true;
18581          
18582     
18583         if(Roo.isGecko){ // prevent silly gecko errors
18584             this.win.focus();
18585             var s = this.win.getSelection();
18586             if(!s.focusNode || s.focusNode.nodeType != 3){
18587                 var r = s.getRangeAt(0);
18588                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18589                 r.collapse(true);
18590                 this.deferFocus();
18591             }
18592             try{
18593                 this.execCmd('useCSS', true);
18594                 this.execCmd('styleWithCSS', false);
18595             }catch(e){}
18596         }
18597         this.owner.fireEvent('activate', this);
18598     },
18599
18600     // private
18601     adjustFont: function(btn){
18602         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18603         //if(Roo.isSafari){ // safari
18604         //    adjust *= 2;
18605        // }
18606         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18607         if(Roo.isSafari){ // safari
18608             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18609             v =  (v < 10) ? 10 : v;
18610             v =  (v > 48) ? 48 : v;
18611             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18612             
18613         }
18614         
18615         
18616         v = Math.max(1, v+adjust);
18617         
18618         this.execCmd('FontSize', v  );
18619     },
18620
18621     onEditorEvent : function(e)
18622     {
18623         this.owner.fireEvent('editorevent', this, e);
18624       //  this.updateToolbar();
18625         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18626     },
18627
18628     insertTag : function(tg)
18629     {
18630         // could be a bit smarter... -> wrap the current selected tRoo..
18631         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18632             
18633             range = this.createRange(this.getSelection());
18634             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18635             wrappingNode.appendChild(range.extractContents());
18636             range.insertNode(wrappingNode);
18637
18638             return;
18639             
18640             
18641             
18642         }
18643         this.execCmd("formatblock",   tg);
18644         
18645     },
18646     
18647     insertText : function(txt)
18648     {
18649         
18650         
18651         var range = this.createRange();
18652         range.deleteContents();
18653                //alert(Sender.getAttribute('label'));
18654                
18655         range.insertNode(this.doc.createTextNode(txt));
18656     } ,
18657     
18658      
18659
18660     /**
18661      * Executes a Midas editor command on the editor document and performs necessary focus and
18662      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18663      * @param {String} cmd The Midas command
18664      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18665      */
18666     relayCmd : function(cmd, value){
18667         this.win.focus();
18668         this.execCmd(cmd, value);
18669         this.owner.fireEvent('editorevent', this);
18670         //this.updateToolbar();
18671         this.owner.deferFocus();
18672     },
18673
18674     /**
18675      * Executes a Midas editor command directly on the editor document.
18676      * For visual commands, you should use {@link #relayCmd} instead.
18677      * <b>This should only be called after the editor is initialized.</b>
18678      * @param {String} cmd The Midas command
18679      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18680      */
18681     execCmd : function(cmd, value){
18682         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18683         this.syncValue();
18684     },
18685  
18686  
18687    
18688     /**
18689      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18690      * to insert tRoo.
18691      * @param {String} text | dom node.. 
18692      */
18693     insertAtCursor : function(text)
18694     {
18695         
18696         
18697         
18698         if(!this.activated){
18699             return;
18700         }
18701         /*
18702         if(Roo.isIE){
18703             this.win.focus();
18704             var r = this.doc.selection.createRange();
18705             if(r){
18706                 r.collapse(true);
18707                 r.pasteHTML(text);
18708                 this.syncValue();
18709                 this.deferFocus();
18710             
18711             }
18712             return;
18713         }
18714         */
18715         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18716             this.win.focus();
18717             
18718             
18719             // from jquery ui (MIT licenced)
18720             var range, node;
18721             var win = this.win;
18722             
18723             if (win.getSelection && win.getSelection().getRangeAt) {
18724                 range = win.getSelection().getRangeAt(0);
18725                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18726                 range.insertNode(node);
18727             } else if (win.document.selection && win.document.selection.createRange) {
18728                 // no firefox support
18729                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18730                 win.document.selection.createRange().pasteHTML(txt);
18731             } else {
18732                 // no firefox support
18733                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18734                 this.execCmd('InsertHTML', txt);
18735             } 
18736             
18737             this.syncValue();
18738             
18739             this.deferFocus();
18740         }
18741     },
18742  // private
18743     mozKeyPress : function(e){
18744         if(e.ctrlKey){
18745             var c = e.getCharCode(), cmd;
18746           
18747             if(c > 0){
18748                 c = String.fromCharCode(c).toLowerCase();
18749                 switch(c){
18750                     case 'b':
18751                         cmd = 'bold';
18752                         break;
18753                     case 'i':
18754                         cmd = 'italic';
18755                         break;
18756                     
18757                     case 'u':
18758                         cmd = 'underline';
18759                         break;
18760                     
18761                     case 'v':
18762                         this.cleanUpPaste.defer(100, this);
18763                         return;
18764                         
18765                 }
18766                 if(cmd){
18767                     this.win.focus();
18768                     this.execCmd(cmd);
18769                     this.deferFocus();
18770                     e.preventDefault();
18771                 }
18772                 
18773             }
18774         }
18775     },
18776
18777     // private
18778     fixKeys : function(){ // load time branching for fastest keydown performance
18779         if(Roo.isIE){
18780             return function(e){
18781                 var k = e.getKey(), r;
18782                 if(k == e.TAB){
18783                     e.stopEvent();
18784                     r = this.doc.selection.createRange();
18785                     if(r){
18786                         r.collapse(true);
18787                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18788                         this.deferFocus();
18789                     }
18790                     return;
18791                 }
18792                 
18793                 if(k == e.ENTER){
18794                     r = this.doc.selection.createRange();
18795                     if(r){
18796                         var target = r.parentElement();
18797                         if(!target || target.tagName.toLowerCase() != 'li'){
18798                             e.stopEvent();
18799                             r.pasteHTML('<br />');
18800                             r.collapse(false);
18801                             r.select();
18802                         }
18803                     }
18804                 }
18805                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18806                     this.cleanUpPaste.defer(100, this);
18807                     return;
18808                 }
18809                 
18810                 
18811             };
18812         }else if(Roo.isOpera){
18813             return function(e){
18814                 var k = e.getKey();
18815                 if(k == e.TAB){
18816                     e.stopEvent();
18817                     this.win.focus();
18818                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18819                     this.deferFocus();
18820                 }
18821                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18822                     this.cleanUpPaste.defer(100, this);
18823                     return;
18824                 }
18825                 
18826             };
18827         }else if(Roo.isSafari){
18828             return function(e){
18829                 var k = e.getKey();
18830                 
18831                 if(k == e.TAB){
18832                     e.stopEvent();
18833                     this.execCmd('InsertText','\t');
18834                     this.deferFocus();
18835                     return;
18836                 }
18837                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18838                     this.cleanUpPaste.defer(100, this);
18839                     return;
18840                 }
18841                 
18842              };
18843         }
18844     }(),
18845     
18846     getAllAncestors: function()
18847     {
18848         var p = this.getSelectedNode();
18849         var a = [];
18850         if (!p) {
18851             a.push(p); // push blank onto stack..
18852             p = this.getParentElement();
18853         }
18854         
18855         
18856         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18857             a.push(p);
18858             p = p.parentNode;
18859         }
18860         a.push(this.doc.body);
18861         return a;
18862     },
18863     lastSel : false,
18864     lastSelNode : false,
18865     
18866     
18867     getSelection : function() 
18868     {
18869         this.assignDocWin();
18870         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18871     },
18872     
18873     getSelectedNode: function() 
18874     {
18875         // this may only work on Gecko!!!
18876         
18877         // should we cache this!!!!
18878         
18879         
18880         
18881          
18882         var range = this.createRange(this.getSelection()).cloneRange();
18883         
18884         if (Roo.isIE) {
18885             var parent = range.parentElement();
18886             while (true) {
18887                 var testRange = range.duplicate();
18888                 testRange.moveToElementText(parent);
18889                 if (testRange.inRange(range)) {
18890                     break;
18891                 }
18892                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18893                     break;
18894                 }
18895                 parent = parent.parentElement;
18896             }
18897             return parent;
18898         }
18899         
18900         // is ancestor a text element.
18901         var ac =  range.commonAncestorContainer;
18902         if (ac.nodeType == 3) {
18903             ac = ac.parentNode;
18904         }
18905         
18906         var ar = ac.childNodes;
18907          
18908         var nodes = [];
18909         var other_nodes = [];
18910         var has_other_nodes = false;
18911         for (var i=0;i<ar.length;i++) {
18912             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18913                 continue;
18914             }
18915             // fullly contained node.
18916             
18917             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18918                 nodes.push(ar[i]);
18919                 continue;
18920             }
18921             
18922             // probably selected..
18923             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18924                 other_nodes.push(ar[i]);
18925                 continue;
18926             }
18927             // outer..
18928             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18929                 continue;
18930             }
18931             
18932             
18933             has_other_nodes = true;
18934         }
18935         if (!nodes.length && other_nodes.length) {
18936             nodes= other_nodes;
18937         }
18938         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18939             return false;
18940         }
18941         
18942         return nodes[0];
18943     },
18944     createRange: function(sel)
18945     {
18946         // this has strange effects when using with 
18947         // top toolbar - not sure if it's a great idea.
18948         //this.editor.contentWindow.focus();
18949         if (typeof sel != "undefined") {
18950             try {
18951                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18952             } catch(e) {
18953                 return this.doc.createRange();
18954             }
18955         } else {
18956             return this.doc.createRange();
18957         }
18958     },
18959     getParentElement: function()
18960     {
18961         
18962         this.assignDocWin();
18963         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18964         
18965         var range = this.createRange(sel);
18966          
18967         try {
18968             var p = range.commonAncestorContainer;
18969             while (p.nodeType == 3) { // text node
18970                 p = p.parentNode;
18971             }
18972             return p;
18973         } catch (e) {
18974             return null;
18975         }
18976     
18977     },
18978     /***
18979      *
18980      * Range intersection.. the hard stuff...
18981      *  '-1' = before
18982      *  '0' = hits..
18983      *  '1' = after.
18984      *         [ -- selected range --- ]
18985      *   [fail]                        [fail]
18986      *
18987      *    basically..
18988      *      if end is before start or  hits it. fail.
18989      *      if start is after end or hits it fail.
18990      *
18991      *   if either hits (but other is outside. - then it's not 
18992      *   
18993      *    
18994      **/
18995     
18996     
18997     // @see http://www.thismuchiknow.co.uk/?p=64.
18998     rangeIntersectsNode : function(range, node)
18999     {
19000         var nodeRange = node.ownerDocument.createRange();
19001         try {
19002             nodeRange.selectNode(node);
19003         } catch (e) {
19004             nodeRange.selectNodeContents(node);
19005         }
19006     
19007         var rangeStartRange = range.cloneRange();
19008         rangeStartRange.collapse(true);
19009     
19010         var rangeEndRange = range.cloneRange();
19011         rangeEndRange.collapse(false);
19012     
19013         var nodeStartRange = nodeRange.cloneRange();
19014         nodeStartRange.collapse(true);
19015     
19016         var nodeEndRange = nodeRange.cloneRange();
19017         nodeEndRange.collapse(false);
19018     
19019         return rangeStartRange.compareBoundaryPoints(
19020                  Range.START_TO_START, nodeEndRange) == -1 &&
19021                rangeEndRange.compareBoundaryPoints(
19022                  Range.START_TO_START, nodeStartRange) == 1;
19023         
19024          
19025     },
19026     rangeCompareNode : function(range, node)
19027     {
19028         var nodeRange = node.ownerDocument.createRange();
19029         try {
19030             nodeRange.selectNode(node);
19031         } catch (e) {
19032             nodeRange.selectNodeContents(node);
19033         }
19034         
19035         
19036         range.collapse(true);
19037     
19038         nodeRange.collapse(true);
19039      
19040         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19041         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19042          
19043         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19044         
19045         var nodeIsBefore   =  ss == 1;
19046         var nodeIsAfter    = ee == -1;
19047         
19048         if (nodeIsBefore && nodeIsAfter)
19049             return 0; // outer
19050         if (!nodeIsBefore && nodeIsAfter)
19051             return 1; //right trailed.
19052         
19053         if (nodeIsBefore && !nodeIsAfter)
19054             return 2;  // left trailed.
19055         // fully contined.
19056         return 3;
19057     },
19058
19059     // private? - in a new class?
19060     cleanUpPaste :  function()
19061     {
19062         // cleans up the whole document..
19063         Roo.log('cleanuppaste');
19064         
19065         this.cleanUpChildren(this.doc.body);
19066         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19067         if (clean != this.doc.body.innerHTML) {
19068             this.doc.body.innerHTML = clean;
19069         }
19070         
19071     },
19072     
19073     cleanWordChars : function(input) {// change the chars to hex code
19074         var he = Roo.HtmlEditorCore;
19075         
19076         var output = input;
19077         Roo.each(he.swapCodes, function(sw) { 
19078             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19079             
19080             output = output.replace(swapper, sw[1]);
19081         });
19082         
19083         return output;
19084     },
19085     
19086     
19087     cleanUpChildren : function (n)
19088     {
19089         if (!n.childNodes.length) {
19090             return;
19091         }
19092         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19093            this.cleanUpChild(n.childNodes[i]);
19094         }
19095     },
19096     
19097     
19098         
19099     
19100     cleanUpChild : function (node)
19101     {
19102         var ed = this;
19103         //console.log(node);
19104         if (node.nodeName == "#text") {
19105             // clean up silly Windows -- stuff?
19106             return; 
19107         }
19108         if (node.nodeName == "#comment") {
19109             node.parentNode.removeChild(node);
19110             // clean up silly Windows -- stuff?
19111             return; 
19112         }
19113         var lcname = node.tagName.toLowerCase();
19114         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19115         // whitelist of tags..
19116         
19117         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19118             // remove node.
19119             node.parentNode.removeChild(node);
19120             return;
19121             
19122         }
19123         
19124         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19125         
19126         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19127         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19128         
19129         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19130         //    remove_keep_children = true;
19131         //}
19132         
19133         if (remove_keep_children) {
19134             this.cleanUpChildren(node);
19135             // inserts everything just before this node...
19136             while (node.childNodes.length) {
19137                 var cn = node.childNodes[0];
19138                 node.removeChild(cn);
19139                 node.parentNode.insertBefore(cn, node);
19140             }
19141             node.parentNode.removeChild(node);
19142             return;
19143         }
19144         
19145         if (!node.attributes || !node.attributes.length) {
19146             this.cleanUpChildren(node);
19147             return;
19148         }
19149         
19150         function cleanAttr(n,v)
19151         {
19152             
19153             if (v.match(/^\./) || v.match(/^\//)) {
19154                 return;
19155             }
19156             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19157                 return;
19158             }
19159             if (v.match(/^#/)) {
19160                 return;
19161             }
19162 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19163             node.removeAttribute(n);
19164             
19165         }
19166         
19167         var cwhite = this.cwhite;
19168         var cblack = this.cblack;
19169             
19170         function cleanStyle(n,v)
19171         {
19172             if (v.match(/expression/)) { //XSS?? should we even bother..
19173                 node.removeAttribute(n);
19174                 return;
19175             }
19176             
19177             var parts = v.split(/;/);
19178             var clean = [];
19179             
19180             Roo.each(parts, function(p) {
19181                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19182                 if (!p.length) {
19183                     return true;
19184                 }
19185                 var l = p.split(':').shift().replace(/\s+/g,'');
19186                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19187                 
19188                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19189 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19190                     //node.removeAttribute(n);
19191                     return true;
19192                 }
19193                 //Roo.log()
19194                 // only allow 'c whitelisted system attributes'
19195                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19196 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19197                     //node.removeAttribute(n);
19198                     return true;
19199                 }
19200                 
19201                 
19202                  
19203                 
19204                 clean.push(p);
19205                 return true;
19206             });
19207             if (clean.length) { 
19208                 node.setAttribute(n, clean.join(';'));
19209             } else {
19210                 node.removeAttribute(n);
19211             }
19212             
19213         }
19214         
19215         
19216         for (var i = node.attributes.length-1; i > -1 ; i--) {
19217             var a = node.attributes[i];
19218             //console.log(a);
19219             
19220             if (a.name.toLowerCase().substr(0,2)=='on')  {
19221                 node.removeAttribute(a.name);
19222                 continue;
19223             }
19224             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19225                 node.removeAttribute(a.name);
19226                 continue;
19227             }
19228             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19229                 cleanAttr(a.name,a.value); // fixme..
19230                 continue;
19231             }
19232             if (a.name == 'style') {
19233                 cleanStyle(a.name,a.value);
19234                 continue;
19235             }
19236             /// clean up MS crap..
19237             // tecnically this should be a list of valid class'es..
19238             
19239             
19240             if (a.name == 'class') {
19241                 if (a.value.match(/^Mso/)) {
19242                     node.className = '';
19243                 }
19244                 
19245                 if (a.value.match(/body/)) {
19246                     node.className = '';
19247                 }
19248                 continue;
19249             }
19250             
19251             // style cleanup!?
19252             // class cleanup?
19253             
19254         }
19255         
19256         
19257         this.cleanUpChildren(node);
19258         
19259         
19260     },
19261     
19262     /**
19263      * Clean up MS wordisms...
19264      */
19265     cleanWord : function(node)
19266     {
19267         
19268         
19269         if (!node) {
19270             this.cleanWord(this.doc.body);
19271             return;
19272         }
19273         if (node.nodeName == "#text") {
19274             // clean up silly Windows -- stuff?
19275             return; 
19276         }
19277         if (node.nodeName == "#comment") {
19278             node.parentNode.removeChild(node);
19279             // clean up silly Windows -- stuff?
19280             return; 
19281         }
19282         
19283         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19284             node.parentNode.removeChild(node);
19285             return;
19286         }
19287         
19288         // remove - but keep children..
19289         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19290             while (node.childNodes.length) {
19291                 var cn = node.childNodes[0];
19292                 node.removeChild(cn);
19293                 node.parentNode.insertBefore(cn, node);
19294             }
19295             node.parentNode.removeChild(node);
19296             this.iterateChildren(node, this.cleanWord);
19297             return;
19298         }
19299         // clean styles
19300         if (node.className.length) {
19301             
19302             var cn = node.className.split(/\W+/);
19303             var cna = [];
19304             Roo.each(cn, function(cls) {
19305                 if (cls.match(/Mso[a-zA-Z]+/)) {
19306                     return;
19307                 }
19308                 cna.push(cls);
19309             });
19310             node.className = cna.length ? cna.join(' ') : '';
19311             if (!cna.length) {
19312                 node.removeAttribute("class");
19313             }
19314         }
19315         
19316         if (node.hasAttribute("lang")) {
19317             node.removeAttribute("lang");
19318         }
19319         
19320         if (node.hasAttribute("style")) {
19321             
19322             var styles = node.getAttribute("style").split(";");
19323             var nstyle = [];
19324             Roo.each(styles, function(s) {
19325                 if (!s.match(/:/)) {
19326                     return;
19327                 }
19328                 var kv = s.split(":");
19329                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19330                     return;
19331                 }
19332                 // what ever is left... we allow.
19333                 nstyle.push(s);
19334             });
19335             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19336             if (!nstyle.length) {
19337                 node.removeAttribute('style');
19338             }
19339         }
19340         this.iterateChildren(node, this.cleanWord);
19341         
19342         
19343         
19344     },
19345     /**
19346      * iterateChildren of a Node, calling fn each time, using this as the scole..
19347      * @param {DomNode} node node to iterate children of.
19348      * @param {Function} fn method of this class to call on each item.
19349      */
19350     iterateChildren : function(node, fn)
19351     {
19352         if (!node.childNodes.length) {
19353                 return;
19354         }
19355         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19356            fn.call(this, node.childNodes[i])
19357         }
19358     },
19359     
19360     
19361     /**
19362      * cleanTableWidths.
19363      *
19364      * Quite often pasting from word etc.. results in tables with column and widths.
19365      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19366      *
19367      */
19368     cleanTableWidths : function(node)
19369     {
19370          
19371          
19372         if (!node) {
19373             this.cleanTableWidths(this.doc.body);
19374             return;
19375         }
19376         
19377         // ignore list...
19378         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19379             return; 
19380         }
19381         Roo.log(node.tagName);
19382         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19383             this.iterateChildren(node, this.cleanTableWidths);
19384             return;
19385         }
19386         if (node.hasAttribute('width')) {
19387             node.removeAttribute('width');
19388         }
19389         
19390          
19391         if (node.hasAttribute("style")) {
19392             // pretty basic...
19393             
19394             var styles = node.getAttribute("style").split(";");
19395             var nstyle = [];
19396             Roo.each(styles, function(s) {
19397                 if (!s.match(/:/)) {
19398                     return;
19399                 }
19400                 var kv = s.split(":");
19401                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
19402                     return;
19403                 }
19404                 // what ever is left... we allow.
19405                 nstyle.push(s);
19406             });
19407             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19408             if (!nstyle.length) {
19409                 node.removeAttribute('style');
19410             }
19411         }
19412         
19413         this.iterateChildren(node, this.cleanTableWidths);
19414         
19415         
19416     },
19417     
19418     
19419     
19420     
19421     domToHTML : function(currentElement, depth, nopadtext) {
19422         
19423         depth = depth || 0;
19424         nopadtext = nopadtext || false;
19425     
19426         if (!currentElement) {
19427             return this.domToHTML(this.doc.body);
19428         }
19429         
19430         //Roo.log(currentElement);
19431         var j;
19432         var allText = false;
19433         var nodeName = currentElement.nodeName;
19434         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19435         
19436         if  (nodeName == '#text') {
19437             
19438             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19439         }
19440         
19441         
19442         var ret = '';
19443         if (nodeName != 'BODY') {
19444              
19445             var i = 0;
19446             // Prints the node tagName, such as <A>, <IMG>, etc
19447             if (tagName) {
19448                 var attr = [];
19449                 for(i = 0; i < currentElement.attributes.length;i++) {
19450                     // quoting?
19451                     var aname = currentElement.attributes.item(i).name;
19452                     if (!currentElement.attributes.item(i).value.length) {
19453                         continue;
19454                     }
19455                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19456                 }
19457                 
19458                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19459             } 
19460             else {
19461                 
19462                 // eack
19463             }
19464         } else {
19465             tagName = false;
19466         }
19467         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19468             return ret;
19469         }
19470         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19471             nopadtext = true;
19472         }
19473         
19474         
19475         // Traverse the tree
19476         i = 0;
19477         var currentElementChild = currentElement.childNodes.item(i);
19478         var allText = true;
19479         var innerHTML  = '';
19480         lastnode = '';
19481         while (currentElementChild) {
19482             // Formatting code (indent the tree so it looks nice on the screen)
19483             var nopad = nopadtext;
19484             if (lastnode == 'SPAN') {
19485                 nopad  = true;
19486             }
19487             // text
19488             if  (currentElementChild.nodeName == '#text') {
19489                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19490                 toadd = nopadtext ? toadd : toadd.trim();
19491                 if (!nopad && toadd.length > 80) {
19492                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19493                 }
19494                 innerHTML  += toadd;
19495                 
19496                 i++;
19497                 currentElementChild = currentElement.childNodes.item(i);
19498                 lastNode = '';
19499                 continue;
19500             }
19501             allText = false;
19502             
19503             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19504                 
19505             // Recursively traverse the tree structure of the child node
19506             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19507             lastnode = currentElementChild.nodeName;
19508             i++;
19509             currentElementChild=currentElement.childNodes.item(i);
19510         }
19511         
19512         ret += innerHTML;
19513         
19514         if (!allText) {
19515                 // The remaining code is mostly for formatting the tree
19516             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19517         }
19518         
19519         
19520         if (tagName) {
19521             ret+= "</"+tagName+">";
19522         }
19523         return ret;
19524         
19525     },
19526         
19527     applyBlacklists : function()
19528     {
19529         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19530         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19531         
19532         this.white = [];
19533         this.black = [];
19534         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19535             if (b.indexOf(tag) > -1) {
19536                 return;
19537             }
19538             this.white.push(tag);
19539             
19540         }, this);
19541         
19542         Roo.each(w, function(tag) {
19543             if (b.indexOf(tag) > -1) {
19544                 return;
19545             }
19546             if (this.white.indexOf(tag) > -1) {
19547                 return;
19548             }
19549             this.white.push(tag);
19550             
19551         }, this);
19552         
19553         
19554         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19555             if (w.indexOf(tag) > -1) {
19556                 return;
19557             }
19558             this.black.push(tag);
19559             
19560         }, this);
19561         
19562         Roo.each(b, function(tag) {
19563             if (w.indexOf(tag) > -1) {
19564                 return;
19565             }
19566             if (this.black.indexOf(tag) > -1) {
19567                 return;
19568             }
19569             this.black.push(tag);
19570             
19571         }, this);
19572         
19573         
19574         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19575         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19576         
19577         this.cwhite = [];
19578         this.cblack = [];
19579         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19580             if (b.indexOf(tag) > -1) {
19581                 return;
19582             }
19583             this.cwhite.push(tag);
19584             
19585         }, this);
19586         
19587         Roo.each(w, function(tag) {
19588             if (b.indexOf(tag) > -1) {
19589                 return;
19590             }
19591             if (this.cwhite.indexOf(tag) > -1) {
19592                 return;
19593             }
19594             this.cwhite.push(tag);
19595             
19596         }, this);
19597         
19598         
19599         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19600             if (w.indexOf(tag) > -1) {
19601                 return;
19602             }
19603             this.cblack.push(tag);
19604             
19605         }, this);
19606         
19607         Roo.each(b, function(tag) {
19608             if (w.indexOf(tag) > -1) {
19609                 return;
19610             }
19611             if (this.cblack.indexOf(tag) > -1) {
19612                 return;
19613             }
19614             this.cblack.push(tag);
19615             
19616         }, this);
19617     },
19618     
19619     setStylesheets : function(stylesheets)
19620     {
19621         if(typeof(stylesheets) == 'string'){
19622             Roo.get(this.iframe.contentDocument.head).createChild({
19623                 tag : 'link',
19624                 rel : 'stylesheet',
19625                 type : 'text/css',
19626                 href : stylesheets
19627             });
19628             
19629             return;
19630         }
19631         var _this = this;
19632      
19633         Roo.each(stylesheets, function(s) {
19634             if(!s.length){
19635                 return;
19636             }
19637             
19638             Roo.get(_this.iframe.contentDocument.head).createChild({
19639                 tag : 'link',
19640                 rel : 'stylesheet',
19641                 type : 'text/css',
19642                 href : s
19643             });
19644         });
19645
19646         
19647     },
19648     
19649     removeStylesheets : function()
19650     {
19651         var _this = this;
19652         
19653         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19654             s.remove();
19655         });
19656     }
19657     
19658     // hide stuff that is not compatible
19659     /**
19660      * @event blur
19661      * @hide
19662      */
19663     /**
19664      * @event change
19665      * @hide
19666      */
19667     /**
19668      * @event focus
19669      * @hide
19670      */
19671     /**
19672      * @event specialkey
19673      * @hide
19674      */
19675     /**
19676      * @cfg {String} fieldClass @hide
19677      */
19678     /**
19679      * @cfg {String} focusClass @hide
19680      */
19681     /**
19682      * @cfg {String} autoCreate @hide
19683      */
19684     /**
19685      * @cfg {String} inputType @hide
19686      */
19687     /**
19688      * @cfg {String} invalidClass @hide
19689      */
19690     /**
19691      * @cfg {String} invalidText @hide
19692      */
19693     /**
19694      * @cfg {String} msgFx @hide
19695      */
19696     /**
19697      * @cfg {String} validateOnBlur @hide
19698      */
19699 });
19700
19701 Roo.HtmlEditorCore.white = [
19702         'area', 'br', 'img', 'input', 'hr', 'wbr',
19703         
19704        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19705        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19706        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19707        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19708        'table',   'ul',         'xmp', 
19709        
19710        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19711       'thead',   'tr', 
19712      
19713       'dir', 'menu', 'ol', 'ul', 'dl',
19714        
19715       'embed',  'object'
19716 ];
19717
19718
19719 Roo.HtmlEditorCore.black = [
19720     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19721         'applet', // 
19722         'base',   'basefont', 'bgsound', 'blink',  'body', 
19723         'frame',  'frameset', 'head',    'html',   'ilayer', 
19724         'iframe', 'layer',  'link',     'meta',    'object',   
19725         'script', 'style' ,'title',  'xml' // clean later..
19726 ];
19727 Roo.HtmlEditorCore.clean = [
19728     'script', 'style', 'title', 'xml'
19729 ];
19730 Roo.HtmlEditorCore.remove = [
19731     'font'
19732 ];
19733 // attributes..
19734
19735 Roo.HtmlEditorCore.ablack = [
19736     'on'
19737 ];
19738     
19739 Roo.HtmlEditorCore.aclean = [ 
19740     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19741 ];
19742
19743 // protocols..
19744 Roo.HtmlEditorCore.pwhite= [
19745         'http',  'https',  'mailto'
19746 ];
19747
19748 // white listed style attributes.
19749 Roo.HtmlEditorCore.cwhite= [
19750       //  'text-align', /// default is to allow most things..
19751       
19752          
19753 //        'font-size'//??
19754 ];
19755
19756 // black listed style attributes.
19757 Roo.HtmlEditorCore.cblack= [
19758       //  'font-size' -- this can be set by the project 
19759 ];
19760
19761
19762 Roo.HtmlEditorCore.swapCodes   =[ 
19763     [    8211, "--" ], 
19764     [    8212, "--" ], 
19765     [    8216,  "'" ],  
19766     [    8217, "'" ],  
19767     [    8220, '"' ],  
19768     [    8221, '"' ],  
19769     [    8226, "*" ],  
19770     [    8230, "..." ]
19771 ]; 
19772
19773     /*
19774  * - LGPL
19775  *
19776  * HtmlEditor
19777  * 
19778  */
19779
19780 /**
19781  * @class Roo.bootstrap.HtmlEditor
19782  * @extends Roo.bootstrap.TextArea
19783  * Bootstrap HtmlEditor class
19784
19785  * @constructor
19786  * Create a new HtmlEditor
19787  * @param {Object} config The config object
19788  */
19789
19790 Roo.bootstrap.HtmlEditor = function(config){
19791     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19792     if (!this.toolbars) {
19793         this.toolbars = [];
19794     }
19795     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19796     this.addEvents({
19797             /**
19798              * @event initialize
19799              * Fires when the editor is fully initialized (including the iframe)
19800              * @param {HtmlEditor} this
19801              */
19802             initialize: true,
19803             /**
19804              * @event activate
19805              * Fires when the editor is first receives the focus. Any insertion must wait
19806              * until after this event.
19807              * @param {HtmlEditor} this
19808              */
19809             activate: true,
19810              /**
19811              * @event beforesync
19812              * Fires before the textarea is updated with content from the editor iframe. Return false
19813              * to cancel the sync.
19814              * @param {HtmlEditor} this
19815              * @param {String} html
19816              */
19817             beforesync: true,
19818              /**
19819              * @event beforepush
19820              * Fires before the iframe editor is updated with content from the textarea. Return false
19821              * to cancel the push.
19822              * @param {HtmlEditor} this
19823              * @param {String} html
19824              */
19825             beforepush: true,
19826              /**
19827              * @event sync
19828              * Fires when the textarea is updated with content from the editor iframe.
19829              * @param {HtmlEditor} this
19830              * @param {String} html
19831              */
19832             sync: true,
19833              /**
19834              * @event push
19835              * Fires when the iframe editor is updated with content from the textarea.
19836              * @param {HtmlEditor} this
19837              * @param {String} html
19838              */
19839             push: true,
19840              /**
19841              * @event editmodechange
19842              * Fires when the editor switches edit modes
19843              * @param {HtmlEditor} this
19844              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19845              */
19846             editmodechange: true,
19847             /**
19848              * @event editorevent
19849              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19850              * @param {HtmlEditor} this
19851              */
19852             editorevent: true,
19853             /**
19854              * @event firstfocus
19855              * Fires when on first focus - needed by toolbars..
19856              * @param {HtmlEditor} this
19857              */
19858             firstfocus: true,
19859             /**
19860              * @event autosave
19861              * Auto save the htmlEditor value as a file into Events
19862              * @param {HtmlEditor} this
19863              */
19864             autosave: true,
19865             /**
19866              * @event savedpreview
19867              * preview the saved version of htmlEditor
19868              * @param {HtmlEditor} this
19869              */
19870             savedpreview: true
19871         });
19872 };
19873
19874
19875 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19876     
19877     
19878       /**
19879      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19880      */
19881     toolbars : false,
19882    
19883      /**
19884      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19885      *                        Roo.resizable.
19886      */
19887     resizable : false,
19888      /**
19889      * @cfg {Number} height (in pixels)
19890      */   
19891     height: 300,
19892    /**
19893      * @cfg {Number} width (in pixels)
19894      */   
19895     width: false,
19896     
19897     /**
19898      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19899      * 
19900      */
19901     stylesheets: false,
19902     
19903     // id of frame..
19904     frameId: false,
19905     
19906     // private properties
19907     validationEvent : false,
19908     deferHeight: true,
19909     initialized : false,
19910     activated : false,
19911     
19912     onFocus : Roo.emptyFn,
19913     iframePad:3,
19914     hideMode:'offsets',
19915     
19916     
19917     tbContainer : false,
19918     
19919     toolbarContainer :function() {
19920         return this.wrap.select('.x-html-editor-tb',true).first();
19921     },
19922
19923     /**
19924      * Protected method that will not generally be called directly. It
19925      * is called when the editor creates its toolbar. Override this method if you need to
19926      * add custom toolbar buttons.
19927      * @param {HtmlEditor} editor
19928      */
19929     createToolbar : function(){
19930         
19931         Roo.log("create toolbars");
19932         
19933         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19934         this.toolbars[0].render(this.toolbarContainer());
19935         
19936         return;
19937         
19938 //        if (!editor.toolbars || !editor.toolbars.length) {
19939 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19940 //        }
19941 //        
19942 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19943 //            editor.toolbars[i] = Roo.factory(
19944 //                    typeof(editor.toolbars[i]) == 'string' ?
19945 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19946 //                Roo.bootstrap.HtmlEditor);
19947 //            editor.toolbars[i].init(editor);
19948 //        }
19949     },
19950
19951      
19952     // private
19953     onRender : function(ct, position)
19954     {
19955        // Roo.log("Call onRender: " + this.xtype);
19956         var _t = this;
19957         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19958       
19959         this.wrap = this.inputEl().wrap({
19960             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19961         });
19962         
19963         this.editorcore.onRender(ct, position);
19964          
19965         if (this.resizable) {
19966             this.resizeEl = new Roo.Resizable(this.wrap, {
19967                 pinned : true,
19968                 wrap: true,
19969                 dynamic : true,
19970                 minHeight : this.height,
19971                 height: this.height,
19972                 handles : this.resizable,
19973                 width: this.width,
19974                 listeners : {
19975                     resize : function(r, w, h) {
19976                         _t.onResize(w,h); // -something
19977                     }
19978                 }
19979             });
19980             
19981         }
19982         this.createToolbar(this);
19983        
19984         
19985         if(!this.width && this.resizable){
19986             this.setSize(this.wrap.getSize());
19987         }
19988         if (this.resizeEl) {
19989             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19990             // should trigger onReize..
19991         }
19992         
19993     },
19994
19995     // private
19996     onResize : function(w, h)
19997     {
19998         Roo.log('resize: ' +w + ',' + h );
19999         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20000         var ew = false;
20001         var eh = false;
20002         
20003         if(this.inputEl() ){
20004             if(typeof w == 'number'){
20005                 var aw = w - this.wrap.getFrameWidth('lr');
20006                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20007                 ew = aw;
20008             }
20009             if(typeof h == 'number'){
20010                  var tbh = -11;  // fixme it needs to tool bar size!
20011                 for (var i =0; i < this.toolbars.length;i++) {
20012                     // fixme - ask toolbars for heights?
20013                     tbh += this.toolbars[i].el.getHeight();
20014                     //if (this.toolbars[i].footer) {
20015                     //    tbh += this.toolbars[i].footer.el.getHeight();
20016                     //}
20017                 }
20018               
20019                 
20020                 
20021                 
20022                 
20023                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20024                 ah -= 5; // knock a few pixes off for look..
20025                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20026                 var eh = ah;
20027             }
20028         }
20029         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20030         this.editorcore.onResize(ew,eh);
20031         
20032     },
20033
20034     /**
20035      * Toggles the editor between standard and source edit mode.
20036      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20037      */
20038     toggleSourceEdit : function(sourceEditMode)
20039     {
20040         this.editorcore.toggleSourceEdit(sourceEditMode);
20041         
20042         if(this.editorcore.sourceEditMode){
20043             Roo.log('editor - showing textarea');
20044             
20045 //            Roo.log('in');
20046 //            Roo.log(this.syncValue());
20047             this.syncValue();
20048             this.inputEl().removeClass(['hide', 'x-hidden']);
20049             this.inputEl().dom.removeAttribute('tabIndex');
20050             this.inputEl().focus();
20051         }else{
20052             Roo.log('editor - hiding textarea');
20053 //            Roo.log('out')
20054 //            Roo.log(this.pushValue()); 
20055             this.pushValue();
20056             
20057             this.inputEl().addClass(['hide', 'x-hidden']);
20058             this.inputEl().dom.setAttribute('tabIndex', -1);
20059             //this.deferFocus();
20060         }
20061          
20062         if(this.resizable){
20063             this.setSize(this.wrap.getSize());
20064         }
20065         
20066         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20067     },
20068  
20069     // private (for BoxComponent)
20070     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20071
20072     // private (for BoxComponent)
20073     getResizeEl : function(){
20074         return this.wrap;
20075     },
20076
20077     // private (for BoxComponent)
20078     getPositionEl : function(){
20079         return this.wrap;
20080     },
20081
20082     // private
20083     initEvents : function(){
20084         this.originalValue = this.getValue();
20085     },
20086
20087 //    /**
20088 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20089 //     * @method
20090 //     */
20091 //    markInvalid : Roo.emptyFn,
20092 //    /**
20093 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20094 //     * @method
20095 //     */
20096 //    clearInvalid : Roo.emptyFn,
20097
20098     setValue : function(v){
20099         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20100         this.editorcore.pushValue();
20101     },
20102
20103      
20104     // private
20105     deferFocus : function(){
20106         this.focus.defer(10, this);
20107     },
20108
20109     // doc'ed in Field
20110     focus : function(){
20111         this.editorcore.focus();
20112         
20113     },
20114       
20115
20116     // private
20117     onDestroy : function(){
20118         
20119         
20120         
20121         if(this.rendered){
20122             
20123             for (var i =0; i < this.toolbars.length;i++) {
20124                 // fixme - ask toolbars for heights?
20125                 this.toolbars[i].onDestroy();
20126             }
20127             
20128             this.wrap.dom.innerHTML = '';
20129             this.wrap.remove();
20130         }
20131     },
20132
20133     // private
20134     onFirstFocus : function(){
20135         //Roo.log("onFirstFocus");
20136         this.editorcore.onFirstFocus();
20137          for (var i =0; i < this.toolbars.length;i++) {
20138             this.toolbars[i].onFirstFocus();
20139         }
20140         
20141     },
20142     
20143     // private
20144     syncValue : function()
20145     {   
20146         this.editorcore.syncValue();
20147     },
20148     
20149     pushValue : function()
20150     {   
20151         this.editorcore.pushValue();
20152     }
20153      
20154     
20155     // hide stuff that is not compatible
20156     /**
20157      * @event blur
20158      * @hide
20159      */
20160     /**
20161      * @event change
20162      * @hide
20163      */
20164     /**
20165      * @event focus
20166      * @hide
20167      */
20168     /**
20169      * @event specialkey
20170      * @hide
20171      */
20172     /**
20173      * @cfg {String} fieldClass @hide
20174      */
20175     /**
20176      * @cfg {String} focusClass @hide
20177      */
20178     /**
20179      * @cfg {String} autoCreate @hide
20180      */
20181     /**
20182      * @cfg {String} inputType @hide
20183      */
20184     /**
20185      * @cfg {String} invalidClass @hide
20186      */
20187     /**
20188      * @cfg {String} invalidText @hide
20189      */
20190     /**
20191      * @cfg {String} msgFx @hide
20192      */
20193     /**
20194      * @cfg {String} validateOnBlur @hide
20195      */
20196 });
20197  
20198     
20199    
20200    
20201    
20202       
20203 Roo.namespace('Roo.bootstrap.htmleditor');
20204 /**
20205  * @class Roo.bootstrap.HtmlEditorToolbar1
20206  * Basic Toolbar
20207  * 
20208  * Usage:
20209  *
20210  new Roo.bootstrap.HtmlEditor({
20211     ....
20212     toolbars : [
20213         new Roo.bootstrap.HtmlEditorToolbar1({
20214             disable : { fonts: 1 , format: 1, ..., ... , ...],
20215             btns : [ .... ]
20216         })
20217     }
20218      
20219  * 
20220  * @cfg {Object} disable List of elements to disable..
20221  * @cfg {Array} btns List of additional buttons.
20222  * 
20223  * 
20224  * NEEDS Extra CSS? 
20225  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20226  */
20227  
20228 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20229 {
20230     
20231     Roo.apply(this, config);
20232     
20233     // default disabled, based on 'good practice'..
20234     this.disable = this.disable || {};
20235     Roo.applyIf(this.disable, {
20236         fontSize : true,
20237         colors : true,
20238         specialElements : true
20239     });
20240     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20241     
20242     this.editor = config.editor;
20243     this.editorcore = config.editor.editorcore;
20244     
20245     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20246     
20247     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20248     // dont call parent... till later.
20249 }
20250 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20251      
20252     bar : true,
20253     
20254     editor : false,
20255     editorcore : false,
20256     
20257     
20258     formats : [
20259         "p" ,  
20260         "h1","h2","h3","h4","h5","h6", 
20261         "pre", "code", 
20262         "abbr", "acronym", "address", "cite", "samp", "var",
20263         'div','span'
20264     ],
20265     
20266     onRender : function(ct, position)
20267     {
20268        // Roo.log("Call onRender: " + this.xtype);
20269         
20270        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20271        Roo.log(this.el);
20272        this.el.dom.style.marginBottom = '0';
20273        var _this = this;
20274        var editorcore = this.editorcore;
20275        var editor= this.editor;
20276        
20277        var children = [];
20278        var btn = function(id,cmd , toggle, handler){
20279        
20280             var  event = toggle ? 'toggle' : 'click';
20281        
20282             var a = {
20283                 size : 'sm',
20284                 xtype: 'Button',
20285                 xns: Roo.bootstrap,
20286                 glyphicon : id,
20287                 cmd : id || cmd,
20288                 enableToggle:toggle !== false,
20289                 //html : 'submit'
20290                 pressed : toggle ? false : null,
20291                 listeners : {}
20292             }
20293             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20294                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20295             }
20296             children.push(a);
20297             return a;
20298        }
20299         
20300         var style = {
20301                 xtype: 'Button',
20302                 size : 'sm',
20303                 xns: Roo.bootstrap,
20304                 glyphicon : 'font',
20305                 //html : 'submit'
20306                 menu : {
20307                     xtype: 'Menu',
20308                     xns: Roo.bootstrap,
20309                     items:  []
20310                 }
20311         };
20312         Roo.each(this.formats, function(f) {
20313             style.menu.items.push({
20314                 xtype :'MenuItem',
20315                 xns: Roo.bootstrap,
20316                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20317                 tagname : f,
20318                 listeners : {
20319                     click : function()
20320                     {
20321                         editorcore.insertTag(this.tagname);
20322                         editor.focus();
20323                     }
20324                 }
20325                 
20326             });
20327         });
20328          children.push(style);   
20329             
20330             
20331         btn('bold',false,true);
20332         btn('italic',false,true);
20333         btn('align-left', 'justifyleft',true);
20334         btn('align-center', 'justifycenter',true);
20335         btn('align-right' , 'justifyright',true);
20336         btn('link', false, false, function(btn) {
20337             //Roo.log("create link?");
20338             var url = prompt(this.createLinkText, this.defaultLinkValue);
20339             if(url && url != 'http:/'+'/'){
20340                 this.editorcore.relayCmd('createlink', url);
20341             }
20342         }),
20343         btn('list','insertunorderedlist',true);
20344         btn('pencil', false,true, function(btn){
20345                 Roo.log(this);
20346                 
20347                 this.toggleSourceEdit(btn.pressed);
20348         });
20349         /*
20350         var cog = {
20351                 xtype: 'Button',
20352                 size : 'sm',
20353                 xns: Roo.bootstrap,
20354                 glyphicon : 'cog',
20355                 //html : 'submit'
20356                 menu : {
20357                     xtype: 'Menu',
20358                     xns: Roo.bootstrap,
20359                     items:  []
20360                 }
20361         };
20362         
20363         cog.menu.items.push({
20364             xtype :'MenuItem',
20365             xns: Roo.bootstrap,
20366             html : Clean styles,
20367             tagname : f,
20368             listeners : {
20369                 click : function()
20370                 {
20371                     editorcore.insertTag(this.tagname);
20372                     editor.focus();
20373                 }
20374             }
20375             
20376         });
20377        */
20378         
20379          
20380        this.xtype = 'NavSimplebar';
20381         
20382         for(var i=0;i< children.length;i++) {
20383             
20384             this.buttons.add(this.addxtypeChild(children[i]));
20385             
20386         }
20387         
20388         editor.on('editorevent', this.updateToolbar, this);
20389     },
20390     onBtnClick : function(id)
20391     {
20392        this.editorcore.relayCmd(id);
20393        this.editorcore.focus();
20394     },
20395     
20396     /**
20397      * Protected method that will not generally be called directly. It triggers
20398      * a toolbar update by reading the markup state of the current selection in the editor.
20399      */
20400     updateToolbar: function(){
20401
20402         if(!this.editorcore.activated){
20403             this.editor.onFirstFocus(); // is this neeed?
20404             return;
20405         }
20406
20407         var btns = this.buttons; 
20408         var doc = this.editorcore.doc;
20409         btns.get('bold').setActive(doc.queryCommandState('bold'));
20410         btns.get('italic').setActive(doc.queryCommandState('italic'));
20411         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20412         
20413         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20414         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20415         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20416         
20417         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20418         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20419          /*
20420         
20421         var ans = this.editorcore.getAllAncestors();
20422         if (this.formatCombo) {
20423             
20424             
20425             var store = this.formatCombo.store;
20426             this.formatCombo.setValue("");
20427             for (var i =0; i < ans.length;i++) {
20428                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20429                     // select it..
20430                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20431                     break;
20432                 }
20433             }
20434         }
20435         
20436         
20437         
20438         // hides menus... - so this cant be on a menu...
20439         Roo.bootstrap.MenuMgr.hideAll();
20440         */
20441         Roo.bootstrap.MenuMgr.hideAll();
20442         //this.editorsyncValue();
20443     },
20444     onFirstFocus: function() {
20445         this.buttons.each(function(item){
20446            item.enable();
20447         });
20448     },
20449     toggleSourceEdit : function(sourceEditMode){
20450         
20451           
20452         if(sourceEditMode){
20453             Roo.log("disabling buttons");
20454            this.buttons.each( function(item){
20455                 if(item.cmd != 'pencil'){
20456                     item.disable();
20457                 }
20458             });
20459           
20460         }else{
20461             Roo.log("enabling buttons");
20462             if(this.editorcore.initialized){
20463                 this.buttons.each( function(item){
20464                     item.enable();
20465                 });
20466             }
20467             
20468         }
20469         Roo.log("calling toggole on editor");
20470         // tell the editor that it's been pressed..
20471         this.editor.toggleSourceEdit(sourceEditMode);
20472        
20473     }
20474 });
20475
20476
20477
20478
20479
20480 /**
20481  * @class Roo.bootstrap.Table.AbstractSelectionModel
20482  * @extends Roo.util.Observable
20483  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20484  * implemented by descendant classes.  This class should not be directly instantiated.
20485  * @constructor
20486  */
20487 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20488     this.locked = false;
20489     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20490 };
20491
20492
20493 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20494     /** @ignore Called by the grid automatically. Do not call directly. */
20495     init : function(grid){
20496         this.grid = grid;
20497         this.initEvents();
20498     },
20499
20500     /**
20501      * Locks the selections.
20502      */
20503     lock : function(){
20504         this.locked = true;
20505     },
20506
20507     /**
20508      * Unlocks the selections.
20509      */
20510     unlock : function(){
20511         this.locked = false;
20512     },
20513
20514     /**
20515      * Returns true if the selections are locked.
20516      * @return {Boolean}
20517      */
20518     isLocked : function(){
20519         return this.locked;
20520     }
20521 });
20522 /**
20523  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20524  * @class Roo.bootstrap.Table.RowSelectionModel
20525  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20526  * It supports multiple selections and keyboard selection/navigation. 
20527  * @constructor
20528  * @param {Object} config
20529  */
20530
20531 Roo.bootstrap.Table.RowSelectionModel = function(config){
20532     Roo.apply(this, config);
20533     this.selections = new Roo.util.MixedCollection(false, function(o){
20534         return o.id;
20535     });
20536
20537     this.last = false;
20538     this.lastActive = false;
20539
20540     this.addEvents({
20541         /**
20542              * @event selectionchange
20543              * Fires when the selection changes
20544              * @param {SelectionModel} this
20545              */
20546             "selectionchange" : true,
20547         /**
20548              * @event afterselectionchange
20549              * Fires after the selection changes (eg. by key press or clicking)
20550              * @param {SelectionModel} this
20551              */
20552             "afterselectionchange" : true,
20553         /**
20554              * @event beforerowselect
20555              * Fires when a row is selected being selected, return false to cancel.
20556              * @param {SelectionModel} this
20557              * @param {Number} rowIndex The selected index
20558              * @param {Boolean} keepExisting False if other selections will be cleared
20559              */
20560             "beforerowselect" : true,
20561         /**
20562              * @event rowselect
20563              * Fires when a row is selected.
20564              * @param {SelectionModel} this
20565              * @param {Number} rowIndex The selected index
20566              * @param {Roo.data.Record} r The record
20567              */
20568             "rowselect" : true,
20569         /**
20570              * @event rowdeselect
20571              * Fires when a row is deselected.
20572              * @param {SelectionModel} this
20573              * @param {Number} rowIndex The selected index
20574              */
20575         "rowdeselect" : true
20576     });
20577     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20578     this.locked = false;
20579 };
20580
20581 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20582     /**
20583      * @cfg {Boolean} singleSelect
20584      * True to allow selection of only one row at a time (defaults to false)
20585      */
20586     singleSelect : false,
20587
20588     // private
20589     initEvents : function(){
20590
20591         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20592             this.grid.on("mousedown", this.handleMouseDown, this);
20593         }else{ // allow click to work like normal
20594             this.grid.on("rowclick", this.handleDragableRowClick, this);
20595         }
20596
20597         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20598             "up" : function(e){
20599                 if(!e.shiftKey){
20600                     this.selectPrevious(e.shiftKey);
20601                 }else if(this.last !== false && this.lastActive !== false){
20602                     var last = this.last;
20603                     this.selectRange(this.last,  this.lastActive-1);
20604                     this.grid.getView().focusRow(this.lastActive);
20605                     if(last !== false){
20606                         this.last = last;
20607                     }
20608                 }else{
20609                     this.selectFirstRow();
20610                 }
20611                 this.fireEvent("afterselectionchange", this);
20612             },
20613             "down" : function(e){
20614                 if(!e.shiftKey){
20615                     this.selectNext(e.shiftKey);
20616                 }else if(this.last !== false && this.lastActive !== false){
20617                     var last = this.last;
20618                     this.selectRange(this.last,  this.lastActive+1);
20619                     this.grid.getView().focusRow(this.lastActive);
20620                     if(last !== false){
20621                         this.last = last;
20622                     }
20623                 }else{
20624                     this.selectFirstRow();
20625                 }
20626                 this.fireEvent("afterselectionchange", this);
20627             },
20628             scope: this
20629         });
20630
20631         var view = this.grid.view;
20632         view.on("refresh", this.onRefresh, this);
20633         view.on("rowupdated", this.onRowUpdated, this);
20634         view.on("rowremoved", this.onRemove, this);
20635     },
20636
20637     // private
20638     onRefresh : function(){
20639         var ds = this.grid.dataSource, i, v = this.grid.view;
20640         var s = this.selections;
20641         s.each(function(r){
20642             if((i = ds.indexOfId(r.id)) != -1){
20643                 v.onRowSelect(i);
20644             }else{
20645                 s.remove(r);
20646             }
20647         });
20648     },
20649
20650     // private
20651     onRemove : function(v, index, r){
20652         this.selections.remove(r);
20653     },
20654
20655     // private
20656     onRowUpdated : function(v, index, r){
20657         if(this.isSelected(r)){
20658             v.onRowSelect(index);
20659         }
20660     },
20661
20662     /**
20663      * Select records.
20664      * @param {Array} records The records to select
20665      * @param {Boolean} keepExisting (optional) True to keep existing selections
20666      */
20667     selectRecords : function(records, keepExisting){
20668         if(!keepExisting){
20669             this.clearSelections();
20670         }
20671         var ds = this.grid.dataSource;
20672         for(var i = 0, len = records.length; i < len; i++){
20673             this.selectRow(ds.indexOf(records[i]), true);
20674         }
20675     },
20676
20677     /**
20678      * Gets the number of selected rows.
20679      * @return {Number}
20680      */
20681     getCount : function(){
20682         return this.selections.length;
20683     },
20684
20685     /**
20686      * Selects the first row in the grid.
20687      */
20688     selectFirstRow : function(){
20689         this.selectRow(0);
20690     },
20691
20692     /**
20693      * Select the last row.
20694      * @param {Boolean} keepExisting (optional) True to keep existing selections
20695      */
20696     selectLastRow : function(keepExisting){
20697         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20698     },
20699
20700     /**
20701      * Selects the row immediately following the last selected row.
20702      * @param {Boolean} keepExisting (optional) True to keep existing selections
20703      */
20704     selectNext : function(keepExisting){
20705         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20706             this.selectRow(this.last+1, keepExisting);
20707             this.grid.getView().focusRow(this.last);
20708         }
20709     },
20710
20711     /**
20712      * Selects the row that precedes the last selected row.
20713      * @param {Boolean} keepExisting (optional) True to keep existing selections
20714      */
20715     selectPrevious : function(keepExisting){
20716         if(this.last){
20717             this.selectRow(this.last-1, keepExisting);
20718             this.grid.getView().focusRow(this.last);
20719         }
20720     },
20721
20722     /**
20723      * Returns the selected records
20724      * @return {Array} Array of selected records
20725      */
20726     getSelections : function(){
20727         return [].concat(this.selections.items);
20728     },
20729
20730     /**
20731      * Returns the first selected record.
20732      * @return {Record}
20733      */
20734     getSelected : function(){
20735         return this.selections.itemAt(0);
20736     },
20737
20738
20739     /**
20740      * Clears all selections.
20741      */
20742     clearSelections : function(fast){
20743         if(this.locked) return;
20744         if(fast !== true){
20745             var ds = this.grid.dataSource;
20746             var s = this.selections;
20747             s.each(function(r){
20748                 this.deselectRow(ds.indexOfId(r.id));
20749             }, this);
20750             s.clear();
20751         }else{
20752             this.selections.clear();
20753         }
20754         this.last = false;
20755     },
20756
20757
20758     /**
20759      * Selects all rows.
20760      */
20761     selectAll : function(){
20762         if(this.locked) return;
20763         this.selections.clear();
20764         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20765             this.selectRow(i, true);
20766         }
20767     },
20768
20769     /**
20770      * Returns True if there is a selection.
20771      * @return {Boolean}
20772      */
20773     hasSelection : function(){
20774         return this.selections.length > 0;
20775     },
20776
20777     /**
20778      * Returns True if the specified row is selected.
20779      * @param {Number/Record} record The record or index of the record to check
20780      * @return {Boolean}
20781      */
20782     isSelected : function(index){
20783         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20784         return (r && this.selections.key(r.id) ? true : false);
20785     },
20786
20787     /**
20788      * Returns True if the specified record id is selected.
20789      * @param {String} id The id of record to check
20790      * @return {Boolean}
20791      */
20792     isIdSelected : function(id){
20793         return (this.selections.key(id) ? true : false);
20794     },
20795
20796     // private
20797     handleMouseDown : function(e, t){
20798         var view = this.grid.getView(), rowIndex;
20799         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20800             return;
20801         };
20802         if(e.shiftKey && this.last !== false){
20803             var last = this.last;
20804             this.selectRange(last, rowIndex, e.ctrlKey);
20805             this.last = last; // reset the last
20806             view.focusRow(rowIndex);
20807         }else{
20808             var isSelected = this.isSelected(rowIndex);
20809             if(e.button !== 0 && isSelected){
20810                 view.focusRow(rowIndex);
20811             }else if(e.ctrlKey && isSelected){
20812                 this.deselectRow(rowIndex);
20813             }else if(!isSelected){
20814                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20815                 view.focusRow(rowIndex);
20816             }
20817         }
20818         this.fireEvent("afterselectionchange", this);
20819     },
20820     // private
20821     handleDragableRowClick :  function(grid, rowIndex, e) 
20822     {
20823         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20824             this.selectRow(rowIndex, false);
20825             grid.view.focusRow(rowIndex);
20826              this.fireEvent("afterselectionchange", this);
20827         }
20828     },
20829     
20830     /**
20831      * Selects multiple rows.
20832      * @param {Array} rows Array of the indexes of the row to select
20833      * @param {Boolean} keepExisting (optional) True to keep existing selections
20834      */
20835     selectRows : function(rows, keepExisting){
20836         if(!keepExisting){
20837             this.clearSelections();
20838         }
20839         for(var i = 0, len = rows.length; i < len; i++){
20840             this.selectRow(rows[i], true);
20841         }
20842     },
20843
20844     /**
20845      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20846      * @param {Number} startRow The index of the first row in the range
20847      * @param {Number} endRow The index of the last row in the range
20848      * @param {Boolean} keepExisting (optional) True to retain existing selections
20849      */
20850     selectRange : function(startRow, endRow, keepExisting){
20851         if(this.locked) return;
20852         if(!keepExisting){
20853             this.clearSelections();
20854         }
20855         if(startRow <= endRow){
20856             for(var i = startRow; i <= endRow; i++){
20857                 this.selectRow(i, true);
20858             }
20859         }else{
20860             for(var i = startRow; i >= endRow; i--){
20861                 this.selectRow(i, true);
20862             }
20863         }
20864     },
20865
20866     /**
20867      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20868      * @param {Number} startRow The index of the first row in the range
20869      * @param {Number} endRow The index of the last row in the range
20870      */
20871     deselectRange : function(startRow, endRow, preventViewNotify){
20872         if(this.locked) return;
20873         for(var i = startRow; i <= endRow; i++){
20874             this.deselectRow(i, preventViewNotify);
20875         }
20876     },
20877
20878     /**
20879      * Selects a row.
20880      * @param {Number} row The index of the row to select
20881      * @param {Boolean} keepExisting (optional) True to keep existing selections
20882      */
20883     selectRow : function(index, keepExisting, preventViewNotify){
20884         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20885         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20886             if(!keepExisting || this.singleSelect){
20887                 this.clearSelections();
20888             }
20889             var r = this.grid.dataSource.getAt(index);
20890             this.selections.add(r);
20891             this.last = this.lastActive = index;
20892             if(!preventViewNotify){
20893                 this.grid.getView().onRowSelect(index);
20894             }
20895             this.fireEvent("rowselect", this, index, r);
20896             this.fireEvent("selectionchange", this);
20897         }
20898     },
20899
20900     /**
20901      * Deselects a row.
20902      * @param {Number} row The index of the row to deselect
20903      */
20904     deselectRow : function(index, preventViewNotify){
20905         if(this.locked) return;
20906         if(this.last == index){
20907             this.last = false;
20908         }
20909         if(this.lastActive == index){
20910             this.lastActive = false;
20911         }
20912         var r = this.grid.dataSource.getAt(index);
20913         this.selections.remove(r);
20914         if(!preventViewNotify){
20915             this.grid.getView().onRowDeselect(index);
20916         }
20917         this.fireEvent("rowdeselect", this, index);
20918         this.fireEvent("selectionchange", this);
20919     },
20920
20921     // private
20922     restoreLast : function(){
20923         if(this._last){
20924             this.last = this._last;
20925         }
20926     },
20927
20928     // private
20929     acceptsNav : function(row, col, cm){
20930         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20931     },
20932
20933     // private
20934     onEditorKey : function(field, e){
20935         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20936         if(k == e.TAB){
20937             e.stopEvent();
20938             ed.completeEdit();
20939             if(e.shiftKey){
20940                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20941             }else{
20942                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20943             }
20944         }else if(k == e.ENTER && !e.ctrlKey){
20945             e.stopEvent();
20946             ed.completeEdit();
20947             if(e.shiftKey){
20948                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20949             }else{
20950                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20951             }
20952         }else if(k == e.ESC){
20953             ed.cancelEdit();
20954         }
20955         if(newCell){
20956             g.startEditing(newCell[0], newCell[1]);
20957         }
20958     }
20959 });/*
20960  * Based on:
20961  * Ext JS Library 1.1.1
20962  * Copyright(c) 2006-2007, Ext JS, LLC.
20963  *
20964  * Originally Released Under LGPL - original licence link has changed is not relivant.
20965  *
20966  * Fork - LGPL
20967  * <script type="text/javascript">
20968  */
20969  
20970 /**
20971  * @class Roo.bootstrap.PagingToolbar
20972  * @extends Roo.Row
20973  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20974  * @constructor
20975  * Create a new PagingToolbar
20976  * @param {Object} config The config object
20977  */
20978 Roo.bootstrap.PagingToolbar = function(config)
20979 {
20980     // old args format still supported... - xtype is prefered..
20981         // created from xtype...
20982     var ds = config.dataSource;
20983     this.toolbarItems = [];
20984     if (config.items) {
20985         this.toolbarItems = config.items;
20986 //        config.items = [];
20987     }
20988     
20989     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20990     this.ds = ds;
20991     this.cursor = 0;
20992     if (ds) { 
20993         this.bind(ds);
20994     }
20995     
20996     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20997     
20998 };
20999
21000 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21001     /**
21002      * @cfg {Roo.data.Store} dataSource
21003      * The underlying data store providing the paged data
21004      */
21005     /**
21006      * @cfg {String/HTMLElement/Element} container
21007      * container The id or element that will contain the toolbar
21008      */
21009     /**
21010      * @cfg {Boolean} displayInfo
21011      * True to display the displayMsg (defaults to false)
21012      */
21013     /**
21014      * @cfg {Number} pageSize
21015      * The number of records to display per page (defaults to 20)
21016      */
21017     pageSize: 20,
21018     /**
21019      * @cfg {String} displayMsg
21020      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21021      */
21022     displayMsg : 'Displaying {0} - {1} of {2}',
21023     /**
21024      * @cfg {String} emptyMsg
21025      * The message to display when no records are found (defaults to "No data to display")
21026      */
21027     emptyMsg : 'No data to display',
21028     /**
21029      * Customizable piece of the default paging text (defaults to "Page")
21030      * @type String
21031      */
21032     beforePageText : "Page",
21033     /**
21034      * Customizable piece of the default paging text (defaults to "of %0")
21035      * @type String
21036      */
21037     afterPageText : "of {0}",
21038     /**
21039      * Customizable piece of the default paging text (defaults to "First Page")
21040      * @type String
21041      */
21042     firstText : "First Page",
21043     /**
21044      * Customizable piece of the default paging text (defaults to "Previous Page")
21045      * @type String
21046      */
21047     prevText : "Previous Page",
21048     /**
21049      * Customizable piece of the default paging text (defaults to "Next Page")
21050      * @type String
21051      */
21052     nextText : "Next Page",
21053     /**
21054      * Customizable piece of the default paging text (defaults to "Last Page")
21055      * @type String
21056      */
21057     lastText : "Last Page",
21058     /**
21059      * Customizable piece of the default paging text (defaults to "Refresh")
21060      * @type String
21061      */
21062     refreshText : "Refresh",
21063
21064     buttons : false,
21065     // private
21066     onRender : function(ct, position) 
21067     {
21068         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21069         this.navgroup.parentId = this.id;
21070         this.navgroup.onRender(this.el, null);
21071         // add the buttons to the navgroup
21072         
21073         if(this.displayInfo){
21074             Roo.log(this.el.select('ul.navbar-nav',true).first());
21075             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21076             this.displayEl = this.el.select('.x-paging-info', true).first();
21077 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21078 //            this.displayEl = navel.el.select('span',true).first();
21079         }
21080         
21081         var _this = this;
21082         
21083         if(this.buttons){
21084             Roo.each(_this.buttons, function(e){
21085                Roo.factory(e).onRender(_this.el, null);
21086             });
21087         }
21088             
21089         Roo.each(_this.toolbarItems, function(e) {
21090             _this.navgroup.addItem(e);
21091         });
21092         
21093         
21094         this.first = this.navgroup.addItem({
21095             tooltip: this.firstText,
21096             cls: "prev",
21097             icon : 'fa fa-backward',
21098             disabled: true,
21099             preventDefault: true,
21100             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21101         });
21102         
21103         this.prev =  this.navgroup.addItem({
21104             tooltip: this.prevText,
21105             cls: "prev",
21106             icon : 'fa fa-step-backward',
21107             disabled: true,
21108             preventDefault: true,
21109             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21110         });
21111     //this.addSeparator();
21112         
21113         
21114         var field = this.navgroup.addItem( {
21115             tagtype : 'span',
21116             cls : 'x-paging-position',
21117             
21118             html : this.beforePageText  +
21119                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21120                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21121          } ); //?? escaped?
21122         
21123         this.field = field.el.select('input', true).first();
21124         this.field.on("keydown", this.onPagingKeydown, this);
21125         this.field.on("focus", function(){this.dom.select();});
21126     
21127     
21128         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21129         //this.field.setHeight(18);
21130         //this.addSeparator();
21131         this.next = this.navgroup.addItem({
21132             tooltip: this.nextText,
21133             cls: "next",
21134             html : ' <i class="fa fa-step-forward">',
21135             disabled: true,
21136             preventDefault: true,
21137             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21138         });
21139         this.last = this.navgroup.addItem({
21140             tooltip: this.lastText,
21141             icon : 'fa fa-forward',
21142             cls: "next",
21143             disabled: true,
21144             preventDefault: true,
21145             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21146         });
21147     //this.addSeparator();
21148         this.loading = this.navgroup.addItem({
21149             tooltip: this.refreshText,
21150             icon: 'fa fa-refresh',
21151             preventDefault: true,
21152             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21153         });
21154
21155     },
21156
21157     // private
21158     updateInfo : function(){
21159         if(this.displayEl){
21160             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21161             var msg = count == 0 ?
21162                 this.emptyMsg :
21163                 String.format(
21164                     this.displayMsg,
21165                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21166                 );
21167             this.displayEl.update(msg);
21168         }
21169     },
21170
21171     // private
21172     onLoad : function(ds, r, o){
21173        this.cursor = o.params ? o.params.start : 0;
21174        var d = this.getPageData(),
21175             ap = d.activePage,
21176             ps = d.pages;
21177         
21178        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21179        this.field.dom.value = ap;
21180        this.first.setDisabled(ap == 1);
21181        this.prev.setDisabled(ap == 1);
21182        this.next.setDisabled(ap == ps);
21183        this.last.setDisabled(ap == ps);
21184        this.loading.enable();
21185        this.updateInfo();
21186     },
21187
21188     // private
21189     getPageData : function(){
21190         var total = this.ds.getTotalCount();
21191         return {
21192             total : total,
21193             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21194             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21195         };
21196     },
21197
21198     // private
21199     onLoadError : function(){
21200         this.loading.enable();
21201     },
21202
21203     // private
21204     onPagingKeydown : function(e){
21205         var k = e.getKey();
21206         var d = this.getPageData();
21207         if(k == e.RETURN){
21208             var v = this.field.dom.value, pageNum;
21209             if(!v || isNaN(pageNum = parseInt(v, 10))){
21210                 this.field.dom.value = d.activePage;
21211                 return;
21212             }
21213             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21214             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21215             e.stopEvent();
21216         }
21217         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))
21218         {
21219           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21220           this.field.dom.value = pageNum;
21221           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21222           e.stopEvent();
21223         }
21224         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21225         {
21226           var v = this.field.dom.value, pageNum; 
21227           var increment = (e.shiftKey) ? 10 : 1;
21228           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21229             increment *= -1;
21230           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21231             this.field.dom.value = d.activePage;
21232             return;
21233           }
21234           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21235           {
21236             this.field.dom.value = parseInt(v, 10) + increment;
21237             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21238             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21239           }
21240           e.stopEvent();
21241         }
21242     },
21243
21244     // private
21245     beforeLoad : function(){
21246         if(this.loading){
21247             this.loading.disable();
21248         }
21249     },
21250
21251     // private
21252     onClick : function(which){
21253         
21254         var ds = this.ds;
21255         if (!ds) {
21256             return;
21257         }
21258         
21259         switch(which){
21260             case "first":
21261                 ds.load({params:{start: 0, limit: this.pageSize}});
21262             break;
21263             case "prev":
21264                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21265             break;
21266             case "next":
21267                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21268             break;
21269             case "last":
21270                 var total = ds.getTotalCount();
21271                 var extra = total % this.pageSize;
21272                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21273                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21274             break;
21275             case "refresh":
21276                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21277             break;
21278         }
21279     },
21280
21281     /**
21282      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21283      * @param {Roo.data.Store} store The data store to unbind
21284      */
21285     unbind : function(ds){
21286         ds.un("beforeload", this.beforeLoad, this);
21287         ds.un("load", this.onLoad, this);
21288         ds.un("loadexception", this.onLoadError, this);
21289         ds.un("remove", this.updateInfo, this);
21290         ds.un("add", this.updateInfo, this);
21291         this.ds = undefined;
21292     },
21293
21294     /**
21295      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21296      * @param {Roo.data.Store} store The data store to bind
21297      */
21298     bind : function(ds){
21299         ds.on("beforeload", this.beforeLoad, this);
21300         ds.on("load", this.onLoad, this);
21301         ds.on("loadexception", this.onLoadError, this);
21302         ds.on("remove", this.updateInfo, this);
21303         ds.on("add", this.updateInfo, this);
21304         this.ds = ds;
21305     }
21306 });/*
21307  * - LGPL
21308  *
21309  * element
21310  * 
21311  */
21312
21313 /**
21314  * @class Roo.bootstrap.MessageBar
21315  * @extends Roo.bootstrap.Component
21316  * Bootstrap MessageBar class
21317  * @cfg {String} html contents of the MessageBar
21318  * @cfg {String} weight (info | success | warning | danger) default info
21319  * @cfg {String} beforeClass insert the bar before the given class
21320  * @cfg {Boolean} closable (true | false) default false
21321  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21322  * 
21323  * @constructor
21324  * Create a new Element
21325  * @param {Object} config The config object
21326  */
21327
21328 Roo.bootstrap.MessageBar = function(config){
21329     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21330 };
21331
21332 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21333     
21334     html: '',
21335     weight: 'info',
21336     closable: false,
21337     fixed: false,
21338     beforeClass: 'bootstrap-sticky-wrap',
21339     
21340     getAutoCreate : function(){
21341         
21342         var cfg = {
21343             tag: 'div',
21344             cls: 'alert alert-dismissable alert-' + this.weight,
21345             cn: [
21346                 {
21347                     tag: 'span',
21348                     cls: 'message',
21349                     html: this.html || ''
21350                 }
21351             ]
21352         }
21353         
21354         if(this.fixed){
21355             cfg.cls += ' alert-messages-fixed';
21356         }
21357         
21358         if(this.closable){
21359             cfg.cn.push({
21360                 tag: 'button',
21361                 cls: 'close',
21362                 html: 'x'
21363             });
21364         }
21365         
21366         return cfg;
21367     },
21368     
21369     onRender : function(ct, position)
21370     {
21371         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21372         
21373         if(!this.el){
21374             var cfg = Roo.apply({},  this.getAutoCreate());
21375             cfg.id = Roo.id();
21376             
21377             if (this.cls) {
21378                 cfg.cls += ' ' + this.cls;
21379             }
21380             if (this.style) {
21381                 cfg.style = this.style;
21382             }
21383             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21384             
21385             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21386         }
21387         
21388         this.el.select('>button.close').on('click', this.hide, this);
21389         
21390     },
21391     
21392     show : function()
21393     {
21394         if (!this.rendered) {
21395             this.render();
21396         }
21397         
21398         this.el.show();
21399         
21400         this.fireEvent('show', this);
21401         
21402     },
21403     
21404     hide : function()
21405     {
21406         if (!this.rendered) {
21407             this.render();
21408         }
21409         
21410         this.el.hide();
21411         
21412         this.fireEvent('hide', this);
21413     },
21414     
21415     update : function()
21416     {
21417 //        var e = this.el.dom.firstChild;
21418 //        
21419 //        if(this.closable){
21420 //            e = e.nextSibling;
21421 //        }
21422 //        
21423 //        e.data = this.html || '';
21424
21425         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21426     }
21427    
21428 });
21429
21430  
21431
21432      /*
21433  * - LGPL
21434  *
21435  * Graph
21436  * 
21437  */
21438
21439
21440 /**
21441  * @class Roo.bootstrap.Graph
21442  * @extends Roo.bootstrap.Component
21443  * Bootstrap Graph class
21444 > Prameters
21445  -sm {number} sm 4
21446  -md {number} md 5
21447  @cfg {String} graphtype  bar | vbar | pie
21448  @cfg {number} g_x coodinator | centre x (pie)
21449  @cfg {number} g_y coodinator | centre y (pie)
21450  @cfg {number} g_r radius (pie)
21451  @cfg {number} g_height height of the chart (respected by all elements in the set)
21452  @cfg {number} g_width width of the chart (respected by all elements in the set)
21453  @cfg {Object} title The title of the chart
21454     
21455  -{Array}  values
21456  -opts (object) options for the chart 
21457      o {
21458      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21459      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21460      o vgutter (number)
21461      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.
21462      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21463      o to
21464      o stretch (boolean)
21465      o }
21466  -opts (object) options for the pie
21467      o{
21468      o cut
21469      o startAngle (number)
21470      o endAngle (number)
21471      } 
21472  *
21473  * @constructor
21474  * Create a new Input
21475  * @param {Object} config The config object
21476  */
21477
21478 Roo.bootstrap.Graph = function(config){
21479     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21480     
21481     this.addEvents({
21482         // img events
21483         /**
21484          * @event click
21485          * The img click event for the img.
21486          * @param {Roo.EventObject} e
21487          */
21488         "click" : true
21489     });
21490 };
21491
21492 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21493     
21494     sm: 4,
21495     md: 5,
21496     graphtype: 'bar',
21497     g_height: 250,
21498     g_width: 400,
21499     g_x: 50,
21500     g_y: 50,
21501     g_r: 30,
21502     opts:{
21503         //g_colors: this.colors,
21504         g_type: 'soft',
21505         g_gutter: '20%'
21506
21507     },
21508     title : false,
21509
21510     getAutoCreate : function(){
21511         
21512         var cfg = {
21513             tag: 'div',
21514             html : null
21515         }
21516         
21517         
21518         return  cfg;
21519     },
21520
21521     onRender : function(ct,position){
21522         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21523         this.raphael = Raphael(this.el.dom);
21524         
21525                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21526                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21527                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21528                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21529                 /*
21530                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21531                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21532                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21533                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21534                 
21535                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21536                 r.barchart(330, 10, 300, 220, data1);
21537                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21538                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21539                 */
21540                 
21541                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21542                 // r.barchart(30, 30, 560, 250,  xdata, {
21543                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21544                 //     axis : "0 0 1 1",
21545                 //     axisxlabels :  xdata
21546                 //     //yvalues : cols,
21547                    
21548                 // });
21549 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21550 //        
21551 //        this.load(null,xdata,{
21552 //                axis : "0 0 1 1",
21553 //                axisxlabels :  xdata
21554 //                });
21555
21556     },
21557
21558     load : function(graphtype,xdata,opts){
21559         this.raphael.clear();
21560         if(!graphtype) {
21561             graphtype = this.graphtype;
21562         }
21563         if(!opts){
21564             opts = this.opts;
21565         }
21566         var r = this.raphael,
21567             fin = function () {
21568                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21569             },
21570             fout = function () {
21571                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21572             },
21573             pfin = function() {
21574                 this.sector.stop();
21575                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21576
21577                 if (this.label) {
21578                     this.label[0].stop();
21579                     this.label[0].attr({ r: 7.5 });
21580                     this.label[1].attr({ "font-weight": 800 });
21581                 }
21582             },
21583             pfout = function() {
21584                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21585
21586                 if (this.label) {
21587                     this.label[0].animate({ r: 5 }, 500, "bounce");
21588                     this.label[1].attr({ "font-weight": 400 });
21589                 }
21590             };
21591
21592         switch(graphtype){
21593             case 'bar':
21594                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21595                 break;
21596             case 'hbar':
21597                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21598                 break;
21599             case 'pie':
21600 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21601 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21602 //            
21603                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21604                 
21605                 break;
21606
21607         }
21608         
21609         if(this.title){
21610             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21611         }
21612         
21613     },
21614     
21615     setTitle: function(o)
21616     {
21617         this.title = o;
21618     },
21619     
21620     initEvents: function() {
21621         
21622         if(!this.href){
21623             this.el.on('click', this.onClick, this);
21624         }
21625     },
21626     
21627     onClick : function(e)
21628     {
21629         Roo.log('img onclick');
21630         this.fireEvent('click', this, e);
21631     }
21632    
21633 });
21634
21635  
21636 /*
21637  * - LGPL
21638  *
21639  * numberBox
21640  * 
21641  */
21642 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21643
21644 /**
21645  * @class Roo.bootstrap.dash.NumberBox
21646  * @extends Roo.bootstrap.Component
21647  * Bootstrap NumberBox class
21648  * @cfg {String} headline Box headline
21649  * @cfg {String} content Box content
21650  * @cfg {String} icon Box icon
21651  * @cfg {String} footer Footer text
21652  * @cfg {String} fhref Footer href
21653  * 
21654  * @constructor
21655  * Create a new NumberBox
21656  * @param {Object} config The config object
21657  */
21658
21659
21660 Roo.bootstrap.dash.NumberBox = function(config){
21661     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21662     
21663 };
21664
21665 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21666     
21667     headline : '',
21668     content : '',
21669     icon : '',
21670     footer : '',
21671     fhref : '',
21672     ficon : '',
21673     
21674     getAutoCreate : function(){
21675         
21676         var cfg = {
21677             tag : 'div',
21678             cls : 'small-box ',
21679             cn : [
21680                 {
21681                     tag : 'div',
21682                     cls : 'inner',
21683                     cn :[
21684                         {
21685                             tag : 'h3',
21686                             cls : 'roo-headline',
21687                             html : this.headline
21688                         },
21689                         {
21690                             tag : 'p',
21691                             cls : 'roo-content',
21692                             html : this.content
21693                         }
21694                     ]
21695                 }
21696             ]
21697         }
21698         
21699         if(this.icon){
21700             cfg.cn.push({
21701                 tag : 'div',
21702                 cls : 'icon',
21703                 cn :[
21704                     {
21705                         tag : 'i',
21706                         cls : 'ion ' + this.icon
21707                     }
21708                 ]
21709             });
21710         }
21711         
21712         if(this.footer){
21713             var footer = {
21714                 tag : 'a',
21715                 cls : 'small-box-footer',
21716                 href : this.fhref || '#',
21717                 html : this.footer
21718             };
21719             
21720             cfg.cn.push(footer);
21721             
21722         }
21723         
21724         return  cfg;
21725     },
21726
21727     onRender : function(ct,position){
21728         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21729
21730
21731        
21732                 
21733     },
21734
21735     setHeadline: function (value)
21736     {
21737         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21738     },
21739     
21740     setFooter: function (value, href)
21741     {
21742         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21743         
21744         if(href){
21745             this.el.select('a.small-box-footer',true).first().attr('href', href);
21746         }
21747         
21748     },
21749
21750     setContent: function (value)
21751     {
21752         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21753     },
21754
21755     initEvents: function() 
21756     {   
21757         
21758     }
21759     
21760 });
21761
21762  
21763 /*
21764  * - LGPL
21765  *
21766  * TabBox
21767  * 
21768  */
21769 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21770
21771 /**
21772  * @class Roo.bootstrap.dash.TabBox
21773  * @extends Roo.bootstrap.Component
21774  * Bootstrap TabBox class
21775  * @cfg {String} title Title of the TabBox
21776  * @cfg {String} icon Icon of the TabBox
21777  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21778  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21779  * 
21780  * @constructor
21781  * Create a new TabBox
21782  * @param {Object} config The config object
21783  */
21784
21785
21786 Roo.bootstrap.dash.TabBox = function(config){
21787     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21788     this.addEvents({
21789         // raw events
21790         /**
21791          * @event addpane
21792          * When a pane is added
21793          * @param {Roo.bootstrap.dash.TabPane} pane
21794          */
21795         "addpane" : true,
21796         /**
21797          * @event activatepane
21798          * When a pane is activated
21799          * @param {Roo.bootstrap.dash.TabPane} pane
21800          */
21801         "activatepane" : true
21802         
21803          
21804     });
21805     
21806     this.panes = [];
21807 };
21808
21809 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21810
21811     title : '',
21812     icon : false,
21813     showtabs : true,
21814     tabScrollable : false,
21815     
21816     getChildContainer : function()
21817     {
21818         return this.el.select('.tab-content', true).first();
21819     },
21820     
21821     getAutoCreate : function(){
21822         
21823         var header = {
21824             tag: 'li',
21825             cls: 'pull-left header',
21826             html: this.title,
21827             cn : []
21828         };
21829         
21830         if(this.icon){
21831             header.cn.push({
21832                 tag: 'i',
21833                 cls: 'fa ' + this.icon
21834             });
21835         }
21836         
21837         var h = {
21838             tag: 'ul',
21839             cls: 'nav nav-tabs pull-right',
21840             cn: [
21841                 header
21842             ]
21843         };
21844         
21845         if(this.tabScrollable){
21846             h = {
21847                 tag: 'div',
21848                 cls: 'tab-header',
21849                 cn: [
21850                     {
21851                         tag: 'ul',
21852                         cls: 'nav nav-tabs pull-right',
21853                         cn: [
21854                             header
21855                         ]
21856                     }
21857                 ]
21858             }
21859         }
21860         
21861         var cfg = {
21862             tag: 'div',
21863             cls: 'nav-tabs-custom',
21864             cn: [
21865                 h,
21866                 {
21867                     tag: 'div',
21868                     cls: 'tab-content no-padding',
21869                     cn: []
21870                 }
21871             ]
21872         }
21873
21874         return  cfg;
21875     },
21876     initEvents : function()
21877     {
21878         //Roo.log('add add pane handler');
21879         this.on('addpane', this.onAddPane, this);
21880     },
21881      /**
21882      * Updates the box title
21883      * @param {String} html to set the title to.
21884      */
21885     setTitle : function(value)
21886     {
21887         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21888     },
21889     onAddPane : function(pane)
21890     {
21891         this.panes.push(pane);
21892         //Roo.log('addpane');
21893         //Roo.log(pane);
21894         // tabs are rendere left to right..
21895         if(!this.showtabs){
21896             return;
21897         }
21898         
21899         var ctr = this.el.select('.nav-tabs', true).first();
21900          
21901          
21902         var existing = ctr.select('.nav-tab',true);
21903         var qty = existing.getCount();;
21904         
21905         
21906         var tab = ctr.createChild({
21907             tag : 'li',
21908             cls : 'nav-tab' + (qty ? '' : ' active'),
21909             cn : [
21910                 {
21911                     tag : 'a',
21912                     href:'#',
21913                     html : pane.title
21914                 }
21915             ]
21916         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21917         pane.tab = tab;
21918         
21919         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21920         if (!qty) {
21921             pane.el.addClass('active');
21922         }
21923         
21924                 
21925     },
21926     onTabClick : function(ev,un,ob,pane)
21927     {
21928         //Roo.log('tab - prev default');
21929         ev.preventDefault();
21930         
21931         
21932         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21933         pane.tab.addClass('active');
21934         //Roo.log(pane.title);
21935         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21936         // technically we should have a deactivate event.. but maybe add later.
21937         // and it should not de-activate the selected tab...
21938         this.fireEvent('activatepane', pane);
21939         pane.el.addClass('active');
21940         pane.fireEvent('activate');
21941         
21942         
21943     },
21944     
21945     getActivePane : function()
21946     {
21947         var r = false;
21948         Roo.each(this.panes, function(p) {
21949             if(p.el.hasClass('active')){
21950                 r = p;
21951                 return false;
21952             }
21953             
21954             return;
21955         });
21956         
21957         return r;
21958     }
21959     
21960     
21961 });
21962
21963  
21964 /*
21965  * - LGPL
21966  *
21967  * Tab pane
21968  * 
21969  */
21970 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21971 /**
21972  * @class Roo.bootstrap.TabPane
21973  * @extends Roo.bootstrap.Component
21974  * Bootstrap TabPane class
21975  * @cfg {Boolean} active (false | true) Default false
21976  * @cfg {String} title title of panel
21977
21978  * 
21979  * @constructor
21980  * Create a new TabPane
21981  * @param {Object} config The config object
21982  */
21983
21984 Roo.bootstrap.dash.TabPane = function(config){
21985     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21986     
21987     this.addEvents({
21988         // raw events
21989         /**
21990          * @event activate
21991          * When a pane is activated
21992          * @param {Roo.bootstrap.dash.TabPane} pane
21993          */
21994         "activate" : true
21995          
21996     });
21997 };
21998
21999 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22000     
22001     active : false,
22002     title : '',
22003     
22004     // the tabBox that this is attached to.
22005     tab : false,
22006      
22007     getAutoCreate : function() 
22008     {
22009         var cfg = {
22010             tag: 'div',
22011             cls: 'tab-pane'
22012         }
22013         
22014         if(this.active){
22015             cfg.cls += ' active';
22016         }
22017         
22018         return cfg;
22019     },
22020     initEvents  : function()
22021     {
22022         //Roo.log('trigger add pane handler');
22023         this.parent().fireEvent('addpane', this)
22024     },
22025     
22026      /**
22027      * Updates the tab title 
22028      * @param {String} html to set the title to.
22029      */
22030     setTitle: function(str)
22031     {
22032         if (!this.tab) {
22033             return;
22034         }
22035         this.title = str;
22036         this.tab.select('a', true).first().dom.innerHTML = str;
22037         
22038     }
22039     
22040     
22041     
22042 });
22043
22044  
22045
22046
22047  /*
22048  * - LGPL
22049  *
22050  * menu
22051  * 
22052  */
22053 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22054
22055 /**
22056  * @class Roo.bootstrap.menu.Menu
22057  * @extends Roo.bootstrap.Component
22058  * Bootstrap Menu class - container for Menu
22059  * @cfg {String} html Text of the menu
22060  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22061  * @cfg {String} icon Font awesome icon
22062  * @cfg {String} pos Menu align to (top | bottom) default bottom
22063  * 
22064  * 
22065  * @constructor
22066  * Create a new Menu
22067  * @param {Object} config The config object
22068  */
22069
22070
22071 Roo.bootstrap.menu.Menu = function(config){
22072     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22073     
22074     this.addEvents({
22075         /**
22076          * @event beforeshow
22077          * Fires before this menu is displayed
22078          * @param {Roo.bootstrap.menu.Menu} this
22079          */
22080         beforeshow : true,
22081         /**
22082          * @event beforehide
22083          * Fires before this menu is hidden
22084          * @param {Roo.bootstrap.menu.Menu} this
22085          */
22086         beforehide : true,
22087         /**
22088          * @event show
22089          * Fires after this menu is displayed
22090          * @param {Roo.bootstrap.menu.Menu} this
22091          */
22092         show : true,
22093         /**
22094          * @event hide
22095          * Fires after this menu is hidden
22096          * @param {Roo.bootstrap.menu.Menu} this
22097          */
22098         hide : true,
22099         /**
22100          * @event click
22101          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22102          * @param {Roo.bootstrap.menu.Menu} this
22103          * @param {Roo.EventObject} e
22104          */
22105         click : true
22106     });
22107     
22108 };
22109
22110 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22111     
22112     submenu : false,
22113     html : '',
22114     weight : 'default',
22115     icon : false,
22116     pos : 'bottom',
22117     
22118     
22119     getChildContainer : function() {
22120         if(this.isSubMenu){
22121             return this.el;
22122         }
22123         
22124         return this.el.select('ul.dropdown-menu', true).first();  
22125     },
22126     
22127     getAutoCreate : function()
22128     {
22129         var text = [
22130             {
22131                 tag : 'span',
22132                 cls : 'roo-menu-text',
22133                 html : this.html
22134             }
22135         ];
22136         
22137         if(this.icon){
22138             text.unshift({
22139                 tag : 'i',
22140                 cls : 'fa ' + this.icon
22141             })
22142         }
22143         
22144         
22145         var cfg = {
22146             tag : 'div',
22147             cls : 'btn-group',
22148             cn : [
22149                 {
22150                     tag : 'button',
22151                     cls : 'dropdown-button btn btn-' + this.weight,
22152                     cn : text
22153                 },
22154                 {
22155                     tag : 'button',
22156                     cls : 'dropdown-toggle btn btn-' + this.weight,
22157                     cn : [
22158                         {
22159                             tag : 'span',
22160                             cls : 'caret'
22161                         }
22162                     ]
22163                 },
22164                 {
22165                     tag : 'ul',
22166                     cls : 'dropdown-menu'
22167                 }
22168             ]
22169             
22170         };
22171         
22172         if(this.pos == 'top'){
22173             cfg.cls += ' dropup';
22174         }
22175         
22176         if(this.isSubMenu){
22177             cfg = {
22178                 tag : 'ul',
22179                 cls : 'dropdown-menu'
22180             }
22181         }
22182         
22183         return cfg;
22184     },
22185     
22186     onRender : function(ct, position)
22187     {
22188         this.isSubMenu = ct.hasClass('dropdown-submenu');
22189         
22190         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22191     },
22192     
22193     initEvents : function() 
22194     {
22195         if(this.isSubMenu){
22196             return;
22197         }
22198         
22199         this.hidden = true;
22200         
22201         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22202         this.triggerEl.on('click', this.onTriggerPress, this);
22203         
22204         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22205         this.buttonEl.on('click', this.onClick, this);
22206         
22207     },
22208     
22209     list : function()
22210     {
22211         if(this.isSubMenu){
22212             return this.el;
22213         }
22214         
22215         return this.el.select('ul.dropdown-menu', true).first();
22216     },
22217     
22218     onClick : function(e)
22219     {
22220         this.fireEvent("click", this, e);
22221     },
22222     
22223     onTriggerPress  : function(e)
22224     {   
22225         if (this.isVisible()) {
22226             this.hide();
22227         } else {
22228             this.show();
22229         }
22230     },
22231     
22232     isVisible : function(){
22233         return !this.hidden;
22234     },
22235     
22236     show : function()
22237     {
22238         this.fireEvent("beforeshow", this);
22239         
22240         this.hidden = false;
22241         this.el.addClass('open');
22242         
22243         Roo.get(document).on("mouseup", this.onMouseUp, this);
22244         
22245         this.fireEvent("show", this);
22246         
22247         
22248     },
22249     
22250     hide : function()
22251     {
22252         this.fireEvent("beforehide", this);
22253         
22254         this.hidden = true;
22255         this.el.removeClass('open');
22256         
22257         Roo.get(document).un("mouseup", this.onMouseUp);
22258         
22259         this.fireEvent("hide", this);
22260     },
22261     
22262     onMouseUp : function()
22263     {
22264         this.hide();
22265     }
22266     
22267 });
22268
22269  
22270  /*
22271  * - LGPL
22272  *
22273  * menu item
22274  * 
22275  */
22276 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22277
22278 /**
22279  * @class Roo.bootstrap.menu.Item
22280  * @extends Roo.bootstrap.Component
22281  * Bootstrap MenuItem class
22282  * @cfg {Boolean} submenu (true | false) default false
22283  * @cfg {String} html text of the item
22284  * @cfg {String} href the link
22285  * @cfg {Boolean} disable (true | false) default false
22286  * @cfg {Boolean} preventDefault (true | false) default true
22287  * @cfg {String} icon Font awesome icon
22288  * @cfg {String} pos Submenu align to (left | right) default right 
22289  * 
22290  * 
22291  * @constructor
22292  * Create a new Item
22293  * @param {Object} config The config object
22294  */
22295
22296
22297 Roo.bootstrap.menu.Item = function(config){
22298     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22299     this.addEvents({
22300         /**
22301          * @event mouseover
22302          * Fires when the mouse is hovering over this menu
22303          * @param {Roo.bootstrap.menu.Item} this
22304          * @param {Roo.EventObject} e
22305          */
22306         mouseover : true,
22307         /**
22308          * @event mouseout
22309          * Fires when the mouse exits this menu
22310          * @param {Roo.bootstrap.menu.Item} this
22311          * @param {Roo.EventObject} e
22312          */
22313         mouseout : true,
22314         // raw events
22315         /**
22316          * @event click
22317          * The raw click event for the entire grid.
22318          * @param {Roo.EventObject} e
22319          */
22320         click : true
22321     });
22322 };
22323
22324 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22325     
22326     submenu : false,
22327     href : '',
22328     html : '',
22329     preventDefault: true,
22330     disable : false,
22331     icon : false,
22332     pos : 'right',
22333     
22334     getAutoCreate : function()
22335     {
22336         var text = [
22337             {
22338                 tag : 'span',
22339                 cls : 'roo-menu-item-text',
22340                 html : this.html
22341             }
22342         ];
22343         
22344         if(this.icon){
22345             text.unshift({
22346                 tag : 'i',
22347                 cls : 'fa ' + this.icon
22348             })
22349         }
22350         
22351         var cfg = {
22352             tag : 'li',
22353             cn : [
22354                 {
22355                     tag : 'a',
22356                     href : this.href || '#',
22357                     cn : text
22358                 }
22359             ]
22360         };
22361         
22362         if(this.disable){
22363             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22364         }
22365         
22366         if(this.submenu){
22367             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22368             
22369             if(this.pos == 'left'){
22370                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22371             }
22372         }
22373         
22374         return cfg;
22375     },
22376     
22377     initEvents : function() 
22378     {
22379         this.el.on('mouseover', this.onMouseOver, this);
22380         this.el.on('mouseout', this.onMouseOut, this);
22381         
22382         this.el.select('a', true).first().on('click', this.onClick, this);
22383         
22384     },
22385     
22386     onClick : function(e)
22387     {
22388         if(this.preventDefault){
22389             e.preventDefault();
22390         }
22391         
22392         this.fireEvent("click", this, e);
22393     },
22394     
22395     onMouseOver : function(e)
22396     {
22397         if(this.submenu && this.pos == 'left'){
22398             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22399         }
22400         
22401         this.fireEvent("mouseover", this, e);
22402     },
22403     
22404     onMouseOut : function(e)
22405     {
22406         this.fireEvent("mouseout", this, e);
22407     }
22408 });
22409
22410  
22411
22412  /*
22413  * - LGPL
22414  *
22415  * menu separator
22416  * 
22417  */
22418 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22419
22420 /**
22421  * @class Roo.bootstrap.menu.Separator
22422  * @extends Roo.bootstrap.Component
22423  * Bootstrap Separator class
22424  * 
22425  * @constructor
22426  * Create a new Separator
22427  * @param {Object} config The config object
22428  */
22429
22430
22431 Roo.bootstrap.menu.Separator = function(config){
22432     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22433 };
22434
22435 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22436     
22437     getAutoCreate : function(){
22438         var cfg = {
22439             tag : 'li',
22440             cls: 'divider'
22441         };
22442         
22443         return cfg;
22444     }
22445    
22446 });
22447
22448  
22449
22450  /*
22451  * - LGPL
22452  *
22453  * Tooltip
22454  * 
22455  */
22456
22457 /**
22458  * @class Roo.bootstrap.Tooltip
22459  * Bootstrap Tooltip class
22460  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22461  * to determine which dom element triggers the tooltip.
22462  * 
22463  * It needs to add support for additional attributes like tooltip-position
22464  * 
22465  * @constructor
22466  * Create a new Toolti
22467  * @param {Object} config The config object
22468  */
22469
22470 Roo.bootstrap.Tooltip = function(config){
22471     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22472 };
22473
22474 Roo.apply(Roo.bootstrap.Tooltip, {
22475     /**
22476      * @function init initialize tooltip monitoring.
22477      * @static
22478      */
22479     currentEl : false,
22480     currentTip : false,
22481     currentRegion : false,
22482     
22483     //  init : delay?
22484     
22485     init : function()
22486     {
22487         Roo.get(document).on('mouseover', this.enter ,this);
22488         Roo.get(document).on('mouseout', this.leave, this);
22489          
22490         
22491         this.currentTip = new Roo.bootstrap.Tooltip();
22492     },
22493     
22494     enter : function(ev)
22495     {
22496         var dom = ev.getTarget();
22497         
22498         //Roo.log(['enter',dom]);
22499         var el = Roo.fly(dom);
22500         if (this.currentEl) {
22501             //Roo.log(dom);
22502             //Roo.log(this.currentEl);
22503             //Roo.log(this.currentEl.contains(dom));
22504             if (this.currentEl == el) {
22505                 return;
22506             }
22507             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22508                 return;
22509             }
22510
22511         }
22512         
22513         
22514         
22515         if (this.currentTip.el) {
22516             this.currentTip.el.hide(); // force hiding...
22517         }    
22518         //Roo.log(ev);
22519         var bindEl = el;
22520         
22521         // you can not look for children, as if el is the body.. then everythign is the child..
22522         if (!el.attr('tooltip')) { //
22523             if (!el.select("[tooltip]").elements.length) {
22524                 return;
22525             }
22526             // is the mouse over this child...?
22527             bindEl = el.select("[tooltip]").first();
22528             var xy = ev.getXY();
22529             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22530                 //Roo.log("not in region.");
22531                 return;
22532             }
22533             //Roo.log("child element over..");
22534             
22535         }
22536         this.currentEl = bindEl;
22537         this.currentTip.bind(bindEl);
22538         this.currentRegion = Roo.lib.Region.getRegion(dom);
22539         this.currentTip.enter();
22540         
22541     },
22542     leave : function(ev)
22543     {
22544         var dom = ev.getTarget();
22545         //Roo.log(['leave',dom]);
22546         if (!this.currentEl) {
22547             return;
22548         }
22549         
22550         
22551         if (dom != this.currentEl.dom) {
22552             return;
22553         }
22554         var xy = ev.getXY();
22555         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22556             return;
22557         }
22558         // only activate leave if mouse cursor is outside... bounding box..
22559         
22560         
22561         
22562         
22563         if (this.currentTip) {
22564             this.currentTip.leave();
22565         }
22566         //Roo.log('clear currentEl');
22567         this.currentEl = false;
22568         
22569         
22570     },
22571     alignment : {
22572         'left' : ['r-l', [-2,0], 'right'],
22573         'right' : ['l-r', [2,0], 'left'],
22574         'bottom' : ['t-b', [0,2], 'top'],
22575         'top' : [ 'b-t', [0,-2], 'bottom']
22576     }
22577     
22578 });
22579
22580
22581 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22582     
22583     
22584     bindEl : false,
22585     
22586     delay : null, // can be { show : 300 , hide: 500}
22587     
22588     timeout : null,
22589     
22590     hoverState : null, //???
22591     
22592     placement : 'bottom', 
22593     
22594     getAutoCreate : function(){
22595     
22596         var cfg = {
22597            cls : 'tooltip',
22598            role : 'tooltip',
22599            cn : [
22600                 {
22601                     cls : 'tooltip-arrow'
22602                 },
22603                 {
22604                     cls : 'tooltip-inner'
22605                 }
22606            ]
22607         };
22608         
22609         return cfg;
22610     },
22611     bind : function(el)
22612     {
22613         this.bindEl = el;
22614     },
22615       
22616     
22617     enter : function () {
22618        
22619         if (this.timeout != null) {
22620             clearTimeout(this.timeout);
22621         }
22622         
22623         this.hoverState = 'in';
22624          //Roo.log("enter - show");
22625         if (!this.delay || !this.delay.show) {
22626             this.show();
22627             return;
22628         }
22629         var _t = this;
22630         this.timeout = setTimeout(function () {
22631             if (_t.hoverState == 'in') {
22632                 _t.show();
22633             }
22634         }, this.delay.show);
22635     },
22636     leave : function()
22637     {
22638         clearTimeout(this.timeout);
22639     
22640         this.hoverState = 'out';
22641          if (!this.delay || !this.delay.hide) {
22642             this.hide();
22643             return;
22644         }
22645        
22646         var _t = this;
22647         this.timeout = setTimeout(function () {
22648             //Roo.log("leave - timeout");
22649             
22650             if (_t.hoverState == 'out') {
22651                 _t.hide();
22652                 Roo.bootstrap.Tooltip.currentEl = false;
22653             }
22654         }, delay);
22655     },
22656     
22657     show : function ()
22658     {
22659         if (!this.el) {
22660             this.render(document.body);
22661         }
22662         // set content.
22663         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22664         
22665         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22666         
22667         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22668         
22669         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22670         
22671         var placement = typeof this.placement == 'function' ?
22672             this.placement.call(this, this.el, on_el) :
22673             this.placement;
22674             
22675         var autoToken = /\s?auto?\s?/i;
22676         var autoPlace = autoToken.test(placement);
22677         if (autoPlace) {
22678             placement = placement.replace(autoToken, '') || 'top';
22679         }
22680         
22681         //this.el.detach()
22682         //this.el.setXY([0,0]);
22683         this.el.show();
22684         //this.el.dom.style.display='block';
22685         this.el.addClass(placement);
22686         
22687         //this.el.appendTo(on_el);
22688         
22689         var p = this.getPosition();
22690         var box = this.el.getBox();
22691         
22692         if (autoPlace) {
22693             // fixme..
22694         }
22695         var align = Roo.bootstrap.Tooltip.alignment[placement];
22696         this.el.alignTo(this.bindEl, align[0],align[1]);
22697         //var arrow = this.el.select('.arrow',true).first();
22698         //arrow.set(align[2], 
22699         
22700         this.el.addClass('in fade');
22701         this.hoverState = null;
22702         
22703         if (this.el.hasClass('fade')) {
22704             // fade it?
22705         }
22706         
22707     },
22708     hide : function()
22709     {
22710          
22711         if (!this.el) {
22712             return;
22713         }
22714         //this.el.setXY([0,0]);
22715         this.el.removeClass('in');
22716         //this.el.hide();
22717         
22718     }
22719     
22720 });
22721  
22722
22723  /*
22724  * - LGPL
22725  *
22726  * Location Picker
22727  * 
22728  */
22729
22730 /**
22731  * @class Roo.bootstrap.LocationPicker
22732  * @extends Roo.bootstrap.Component
22733  * Bootstrap LocationPicker class
22734  * @cfg {Number} latitude Position when init default 0
22735  * @cfg {Number} longitude Position when init default 0
22736  * @cfg {Number} zoom default 15
22737  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22738  * @cfg {Boolean} mapTypeControl default false
22739  * @cfg {Boolean} disableDoubleClickZoom default false
22740  * @cfg {Boolean} scrollwheel default true
22741  * @cfg {Boolean} streetViewControl default false
22742  * @cfg {Number} radius default 0
22743  * @cfg {String} locationName
22744  * @cfg {Boolean} draggable default true
22745  * @cfg {Boolean} enableAutocomplete default false
22746  * @cfg {Boolean} enableReverseGeocode default true
22747  * @cfg {String} markerTitle
22748  * 
22749  * @constructor
22750  * Create a new LocationPicker
22751  * @param {Object} config The config object
22752  */
22753
22754
22755 Roo.bootstrap.LocationPicker = function(config){
22756     
22757     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22758     
22759     this.addEvents({
22760         /**
22761          * @event initial
22762          * Fires when the picker initialized.
22763          * @param {Roo.bootstrap.LocationPicker} this
22764          * @param {Google Location} location
22765          */
22766         initial : true,
22767         /**
22768          * @event positionchanged
22769          * Fires when the picker position changed.
22770          * @param {Roo.bootstrap.LocationPicker} this
22771          * @param {Google Location} location
22772          */
22773         positionchanged : true,
22774         /**
22775          * @event resize
22776          * Fires when the map resize.
22777          * @param {Roo.bootstrap.LocationPicker} this
22778          */
22779         resize : true,
22780         /**
22781          * @event show
22782          * Fires when the map show.
22783          * @param {Roo.bootstrap.LocationPicker} this
22784          */
22785         show : true,
22786         /**
22787          * @event hide
22788          * Fires when the map hide.
22789          * @param {Roo.bootstrap.LocationPicker} this
22790          */
22791         hide : true,
22792         /**
22793          * @event mapClick
22794          * Fires when click the map.
22795          * @param {Roo.bootstrap.LocationPicker} this
22796          * @param {Map event} e
22797          */
22798         mapClick : true,
22799         /**
22800          * @event mapRightClick
22801          * Fires when right click the map.
22802          * @param {Roo.bootstrap.LocationPicker} this
22803          * @param {Map event} e
22804          */
22805         mapRightClick : true,
22806         /**
22807          * @event markerClick
22808          * Fires when click the marker.
22809          * @param {Roo.bootstrap.LocationPicker} this
22810          * @param {Map event} e
22811          */
22812         markerClick : true,
22813         /**
22814          * @event markerRightClick
22815          * Fires when right click the marker.
22816          * @param {Roo.bootstrap.LocationPicker} this
22817          * @param {Map event} e
22818          */
22819         markerRightClick : true,
22820         /**
22821          * @event OverlayViewDraw
22822          * Fires when OverlayView Draw
22823          * @param {Roo.bootstrap.LocationPicker} this
22824          */
22825         OverlayViewDraw : true,
22826         /**
22827          * @event OverlayViewOnAdd
22828          * Fires when OverlayView Draw
22829          * @param {Roo.bootstrap.LocationPicker} this
22830          */
22831         OverlayViewOnAdd : true,
22832         /**
22833          * @event OverlayViewOnRemove
22834          * Fires when OverlayView Draw
22835          * @param {Roo.bootstrap.LocationPicker} this
22836          */
22837         OverlayViewOnRemove : true,
22838         /**
22839          * @event OverlayViewShow
22840          * Fires when OverlayView Draw
22841          * @param {Roo.bootstrap.LocationPicker} this
22842          * @param {Pixel} cpx
22843          */
22844         OverlayViewShow : true,
22845         /**
22846          * @event OverlayViewHide
22847          * Fires when OverlayView Draw
22848          * @param {Roo.bootstrap.LocationPicker} this
22849          */
22850         OverlayViewHide : true
22851     });
22852         
22853 };
22854
22855 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22856     
22857     gMapContext: false,
22858     
22859     latitude: 0,
22860     longitude: 0,
22861     zoom: 15,
22862     mapTypeId: false,
22863     mapTypeControl: false,
22864     disableDoubleClickZoom: false,
22865     scrollwheel: true,
22866     streetViewControl: false,
22867     radius: 0,
22868     locationName: '',
22869     draggable: true,
22870     enableAutocomplete: false,
22871     enableReverseGeocode: true,
22872     markerTitle: '',
22873     
22874     getAutoCreate: function()
22875     {
22876
22877         var cfg = {
22878             tag: 'div',
22879             cls: 'roo-location-picker'
22880         };
22881         
22882         return cfg
22883     },
22884     
22885     initEvents: function(ct, position)
22886     {       
22887         if(!this.el.getWidth() || this.isApplied()){
22888             return;
22889         }
22890         
22891         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22892         
22893         this.initial();
22894     },
22895     
22896     initial: function()
22897     {
22898         if(!this.mapTypeId){
22899             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22900         }
22901         
22902         this.gMapContext = this.GMapContext();
22903         
22904         this.initOverlayView();
22905         
22906         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22907         
22908         var _this = this;
22909                 
22910         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22911             _this.setPosition(_this.gMapContext.marker.position);
22912         });
22913         
22914         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22915             _this.fireEvent('mapClick', this, event);
22916             
22917         });
22918
22919         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22920             _this.fireEvent('mapRightClick', this, event);
22921             
22922         });
22923         
22924         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22925             _this.fireEvent('markerClick', this, event);
22926             
22927         });
22928
22929         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22930             _this.fireEvent('markerRightClick', this, event);
22931             
22932         });
22933         
22934         this.setPosition(this.gMapContext.location);
22935         
22936         this.fireEvent('initial', this, this.gMapContext.location);
22937     },
22938     
22939     initOverlayView: function()
22940     {
22941         var _this = this;
22942         
22943         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22944             
22945             draw: function()
22946             {
22947                 _this.fireEvent('OverlayViewDraw', _this);
22948             },
22949             
22950             onAdd: function()
22951             {
22952                 _this.fireEvent('OverlayViewOnAdd', _this);
22953             },
22954             
22955             onRemove: function()
22956             {
22957                 _this.fireEvent('OverlayViewOnRemove', _this);
22958             },
22959             
22960             show: function(cpx)
22961             {
22962                 _this.fireEvent('OverlayViewShow', _this, cpx);
22963             },
22964             
22965             hide: function()
22966             {
22967                 _this.fireEvent('OverlayViewHide', _this);
22968             }
22969             
22970         });
22971     },
22972     
22973     fromLatLngToContainerPixel: function(event)
22974     {
22975         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22976     },
22977     
22978     isApplied: function() 
22979     {
22980         return this.getGmapContext() == false ? false : true;
22981     },
22982     
22983     getGmapContext: function() 
22984     {
22985         return this.gMapContext
22986     },
22987     
22988     GMapContext: function() 
22989     {
22990         var position = new google.maps.LatLng(this.latitude, this.longitude);
22991         
22992         var _map = new google.maps.Map(this.el.dom, {
22993             center: position,
22994             zoom: this.zoom,
22995             mapTypeId: this.mapTypeId,
22996             mapTypeControl: this.mapTypeControl,
22997             disableDoubleClickZoom: this.disableDoubleClickZoom,
22998             scrollwheel: this.scrollwheel,
22999             streetViewControl: this.streetViewControl,
23000             locationName: this.locationName,
23001             draggable: this.draggable,
23002             enableAutocomplete: this.enableAutocomplete,
23003             enableReverseGeocode: this.enableReverseGeocode
23004         });
23005         
23006         var _marker = new google.maps.Marker({
23007             position: position,
23008             map: _map,
23009             title: this.markerTitle,
23010             draggable: this.draggable
23011         });
23012         
23013         return {
23014             map: _map,
23015             marker: _marker,
23016             circle: null,
23017             location: position,
23018             radius: this.radius,
23019             locationName: this.locationName,
23020             addressComponents: {
23021                 formatted_address: null,
23022                 addressLine1: null,
23023                 addressLine2: null,
23024                 streetName: null,
23025                 streetNumber: null,
23026                 city: null,
23027                 district: null,
23028                 state: null,
23029                 stateOrProvince: null
23030             },
23031             settings: this,
23032             domContainer: this.el.dom,
23033             geodecoder: new google.maps.Geocoder()
23034         };
23035     },
23036     
23037     drawCircle: function(center, radius, options) 
23038     {
23039         if (this.gMapContext.circle != null) {
23040             this.gMapContext.circle.setMap(null);
23041         }
23042         if (radius > 0) {
23043             radius *= 1;
23044             options = Roo.apply({}, options, {
23045                 strokeColor: "#0000FF",
23046                 strokeOpacity: .35,
23047                 strokeWeight: 2,
23048                 fillColor: "#0000FF",
23049                 fillOpacity: .2
23050             });
23051             
23052             options.map = this.gMapContext.map;
23053             options.radius = radius;
23054             options.center = center;
23055             this.gMapContext.circle = new google.maps.Circle(options);
23056             return this.gMapContext.circle;
23057         }
23058         
23059         return null;
23060     },
23061     
23062     setPosition: function(location) 
23063     {
23064         this.gMapContext.location = location;
23065         this.gMapContext.marker.setPosition(location);
23066         this.gMapContext.map.panTo(location);
23067         this.drawCircle(location, this.gMapContext.radius, {});
23068         
23069         var _this = this;
23070         
23071         if (this.gMapContext.settings.enableReverseGeocode) {
23072             this.gMapContext.geodecoder.geocode({
23073                 latLng: this.gMapContext.location
23074             }, function(results, status) {
23075                 
23076                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23077                     _this.gMapContext.locationName = results[0].formatted_address;
23078                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23079                     
23080                     _this.fireEvent('positionchanged', this, location);
23081                 }
23082             });
23083             
23084             return;
23085         }
23086         
23087         this.fireEvent('positionchanged', this, location);
23088     },
23089     
23090     resize: function()
23091     {
23092         google.maps.event.trigger(this.gMapContext.map, "resize");
23093         
23094         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23095         
23096         this.fireEvent('resize', this);
23097     },
23098     
23099     setPositionByLatLng: function(latitude, longitude)
23100     {
23101         this.setPosition(new google.maps.LatLng(latitude, longitude));
23102     },
23103     
23104     getCurrentPosition: function() 
23105     {
23106         return {
23107             latitude: this.gMapContext.location.lat(),
23108             longitude: this.gMapContext.location.lng()
23109         };
23110     },
23111     
23112     getAddressName: function() 
23113     {
23114         return this.gMapContext.locationName;
23115     },
23116     
23117     getAddressComponents: function() 
23118     {
23119         return this.gMapContext.addressComponents;
23120     },
23121     
23122     address_component_from_google_geocode: function(address_components) 
23123     {
23124         var result = {};
23125         
23126         for (var i = 0; i < address_components.length; i++) {
23127             var component = address_components[i];
23128             if (component.types.indexOf("postal_code") >= 0) {
23129                 result.postalCode = component.short_name;
23130             } else if (component.types.indexOf("street_number") >= 0) {
23131                 result.streetNumber = component.short_name;
23132             } else if (component.types.indexOf("route") >= 0) {
23133                 result.streetName = component.short_name;
23134             } else if (component.types.indexOf("neighborhood") >= 0) {
23135                 result.city = component.short_name;
23136             } else if (component.types.indexOf("locality") >= 0) {
23137                 result.city = component.short_name;
23138             } else if (component.types.indexOf("sublocality") >= 0) {
23139                 result.district = component.short_name;
23140             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23141                 result.stateOrProvince = component.short_name;
23142             } else if (component.types.indexOf("country") >= 0) {
23143                 result.country = component.short_name;
23144             }
23145         }
23146         
23147         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23148         result.addressLine2 = "";
23149         return result;
23150     },
23151     
23152     setZoomLevel: function(zoom)
23153     {
23154         this.gMapContext.map.setZoom(zoom);
23155     },
23156     
23157     show: function()
23158     {
23159         if(!this.el){
23160             return;
23161         }
23162         
23163         this.el.show();
23164         
23165         this.resize();
23166         
23167         this.fireEvent('show', this);
23168     },
23169     
23170     hide: function()
23171     {
23172         if(!this.el){
23173             return;
23174         }
23175         
23176         this.el.hide();
23177         
23178         this.fireEvent('hide', this);
23179     }
23180     
23181 });
23182
23183 Roo.apply(Roo.bootstrap.LocationPicker, {
23184     
23185     OverlayView : function(map, options)
23186     {
23187         options = options || {};
23188         
23189         this.setMap(map);
23190     }
23191     
23192     
23193 });/*
23194  * - LGPL
23195  *
23196  * Alert
23197  * 
23198  */
23199
23200 /**
23201  * @class Roo.bootstrap.Alert
23202  * @extends Roo.bootstrap.Component
23203  * Bootstrap Alert class
23204  * @cfg {String} title The title of alert
23205  * @cfg {String} html The content of alert
23206  * @cfg {String} weight (  success | info | warning | danger )
23207  * @cfg {String} faicon font-awesomeicon
23208  * 
23209  * @constructor
23210  * Create a new alert
23211  * @param {Object} config The config object
23212  */
23213
23214
23215 Roo.bootstrap.Alert = function(config){
23216     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23217     
23218 };
23219
23220 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23221     
23222     title: '',
23223     html: '',
23224     weight: false,
23225     faicon: false,
23226     
23227     getAutoCreate : function()
23228     {
23229         
23230         var cfg = {
23231             tag : 'div',
23232             cls : 'alert',
23233             cn : [
23234                 {
23235                     tag : 'i',
23236                     cls : 'roo-alert-icon'
23237                     
23238                 },
23239                 {
23240                     tag : 'b',
23241                     cls : 'roo-alert-title',
23242                     html : this.title
23243                 },
23244                 {
23245                     tag : 'span',
23246                     cls : 'roo-alert-text',
23247                     html : this.html
23248                 }
23249             ]
23250         };
23251         
23252         if(this.faicon){
23253             cfg.cn[0].cls += ' fa ' + this.faicon;
23254         }
23255         
23256         if(this.weight){
23257             cfg.cls += ' alert-' + this.weight;
23258         }
23259         
23260         return cfg;
23261     },
23262     
23263     initEvents: function() 
23264     {
23265         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23266     },
23267     
23268     setTitle : function(str)
23269     {
23270         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23271     },
23272     
23273     setText : function(str)
23274     {
23275         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23276     },
23277     
23278     setWeight : function(weight)
23279     {
23280         if(this.weight){
23281             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23282         }
23283         
23284         this.weight = weight;
23285         
23286         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23287     },
23288     
23289     setIcon : function(icon)
23290     {
23291         if(this.faicon){
23292             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23293         }
23294         
23295         this.faicon = icon
23296         
23297         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23298     },
23299     
23300     hide: function() 
23301     {
23302         this.el.hide();   
23303     },
23304     
23305     show: function() 
23306     {  
23307         this.el.show();   
23308     }
23309     
23310 });
23311
23312