Roo/bootstrap/Img.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * @cfg {String} xsUrl xs image source
1330  * @cfg {String} smUrl sm image source
1331  * @cfg {String} mdUrl md image source
1332  * @cfg {String} lgUrl lg image source
1333  * 
1334  * @constructor
1335  * Create a new Input
1336  * @param {Object} config The config object
1337  */
1338
1339 Roo.bootstrap.Img = function(config){
1340     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1341     
1342     this.addEvents({
1343         // img events
1344         /**
1345          * @event click
1346          * The img click event for the img.
1347          * @param {Roo.EventObject} e
1348          */
1349         "click" : true
1350     });
1351 };
1352
1353 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1354     
1355     imgResponsive: true,
1356     border: '',
1357     src: '',
1358     href: false,
1359     target: false,
1360     xsUrl: '',
1361     smUrl: '',
1362     mdUrl: '',
1363     lgUrl: '',
1364
1365     getAutoCreate : function()
1366     {   
1367         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1368             return this.createSingleImg();
1369         }
1370         
1371         var cfg = {
1372             tag: 'div',
1373             cls: 'roo-image-responsive-group',
1374             cn: []
1375         }
1376         var _this = this;
1377         
1378         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1379             var target = size . 'Url';
1380             if(!_this[target]){
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     
1468     onClick : function(e)
1469     {
1470         Roo.log('img onclick');
1471         this.fireEvent('click', this, e);
1472     }
1473    
1474 });
1475
1476  /*
1477  * - LGPL
1478  *
1479  * image
1480  * 
1481  */
1482
1483
1484 /**
1485  * @class Roo.bootstrap.Link
1486  * @extends Roo.bootstrap.Component
1487  * Bootstrap Link Class
1488  * @cfg {String} alt image alternative text
1489  * @cfg {String} href a tag href
1490  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1491  * @cfg {String} html the content of the link.
1492  * @cfg {String} anchor name for the anchor link
1493
1494  * @cfg {Boolean} preventDefault (true | false) default false
1495
1496  * 
1497  * @constructor
1498  * Create a new Input
1499  * @param {Object} config The config object
1500  */
1501
1502 Roo.bootstrap.Link = function(config){
1503     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1504     
1505     this.addEvents({
1506         // img events
1507         /**
1508          * @event click
1509          * The img click event for the img.
1510          * @param {Roo.EventObject} e
1511          */
1512         "click" : true
1513     });
1514 };
1515
1516 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1517     
1518     href: false,
1519     target: false,
1520     preventDefault: false,
1521     anchor : false,
1522     alt : false,
1523
1524     getAutoCreate : function()
1525     {
1526         
1527         var cfg = {
1528             tag: 'a'
1529         };
1530         // anchor's do not require html/href...
1531         if (this.anchor === false) {
1532             cfg.html = this.html || '';
1533             cfg.href = this.href || '#';
1534         } else {
1535             cfg.name = this.anchor;
1536             if (this.html !== false) {
1537                 cfg.html = this.html;
1538             }
1539             if (this.href !== false) {
1540                 cfg.href = this.href;
1541             }
1542         }
1543         
1544         if(this.alt !== false){
1545             cfg.alt = this.alt;
1546         }
1547         
1548         
1549         if(this.target !== false) {
1550             cfg.target = this.target;
1551         }
1552         
1553         return cfg;
1554     },
1555     
1556     initEvents: function() {
1557         
1558         if(!this.href || this.preventDefault){
1559             this.el.on('click', this.onClick, this);
1560         }
1561     },
1562     
1563     onClick : function(e)
1564     {
1565         if(this.preventDefault){
1566             e.preventDefault();
1567         }
1568         //Roo.log('img onclick');
1569         this.fireEvent('click', this, e);
1570     }
1571    
1572 });
1573
1574  /*
1575  * - LGPL
1576  *
1577  * header
1578  * 
1579  */
1580
1581 /**
1582  * @class Roo.bootstrap.Header
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Header class
1585  * @cfg {String} html content of header
1586  * @cfg {Number} level (1|2|3|4|5|6) default 1
1587  * 
1588  * @constructor
1589  * Create a new Header
1590  * @param {Object} config The config object
1591  */
1592
1593
1594 Roo.bootstrap.Header  = function(config){
1595     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1596 };
1597
1598 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1599     
1600     //href : false,
1601     html : false,
1602     level : 1,
1603     
1604     
1605     
1606     getAutoCreate : function(){
1607         
1608         
1609         
1610         var cfg = {
1611             tag: 'h' + (1 *this.level),
1612             html: this.html || ''
1613         } ;
1614         
1615         return cfg;
1616     }
1617    
1618 });
1619
1620  
1621
1622  /*
1623  * Based on:
1624  * Ext JS Library 1.1.1
1625  * Copyright(c) 2006-2007, Ext JS, LLC.
1626  *
1627  * Originally Released Under LGPL - original licence link has changed is not relivant.
1628  *
1629  * Fork - LGPL
1630  * <script type="text/javascript">
1631  */
1632  
1633 /**
1634  * @class Roo.bootstrap.MenuMgr
1635  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1636  * @singleton
1637  */
1638 Roo.bootstrap.MenuMgr = function(){
1639    var menus, active, groups = {}, attached = false, lastShow = new Date();
1640
1641    // private - called when first menu is created
1642    function init(){
1643        menus = {};
1644        active = new Roo.util.MixedCollection();
1645        Roo.get(document).addKeyListener(27, function(){
1646            if(active.length > 0){
1647                hideAll();
1648            }
1649        });
1650    }
1651
1652    // private
1653    function hideAll(){
1654        if(active && active.length > 0){
1655            var c = active.clone();
1656            c.each(function(m){
1657                m.hide();
1658            });
1659        }
1660    }
1661
1662    // private
1663    function onHide(m){
1664        active.remove(m);
1665        if(active.length < 1){
1666            Roo.get(document).un("mouseup", onMouseDown);
1667             
1668            attached = false;
1669        }
1670    }
1671
1672    // private
1673    function onShow(m){
1674        var last = active.last();
1675        lastShow = new Date();
1676        active.add(m);
1677        if(!attached){
1678           Roo.get(document).on("mouseup", onMouseDown);
1679            
1680            attached = true;
1681        }
1682        if(m.parentMenu){
1683           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1684           m.parentMenu.activeChild = m;
1685        }else if(last && last.isVisible()){
1686           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1687        }
1688    }
1689
1690    // private
1691    function onBeforeHide(m){
1692        if(m.activeChild){
1693            m.activeChild.hide();
1694        }
1695        if(m.autoHideTimer){
1696            clearTimeout(m.autoHideTimer);
1697            delete m.autoHideTimer;
1698        }
1699    }
1700
1701    // private
1702    function onBeforeShow(m){
1703        var pm = m.parentMenu;
1704        if(!pm && !m.allowOtherMenus){
1705            hideAll();
1706        }else if(pm && pm.activeChild && active != m){
1707            pm.activeChild.hide();
1708        }
1709    }
1710
1711    // private this should really trigger on mouseup..
1712    function onMouseDown(e){
1713         Roo.log("on Mouse Up");
1714         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1715             Roo.log("hideAll");
1716             hideAll();
1717             e.stopEvent();
1718         }
1719         
1720         
1721    }
1722
1723    // private
1724    function onBeforeCheck(mi, state){
1725        if(state){
1726            var g = groups[mi.group];
1727            for(var i = 0, l = g.length; i < l; i++){
1728                if(g[i] != mi){
1729                    g[i].setChecked(false);
1730                }
1731            }
1732        }
1733    }
1734
1735    return {
1736
1737        /**
1738         * Hides all menus that are currently visible
1739         */
1740        hideAll : function(){
1741             hideAll();  
1742        },
1743
1744        // private
1745        register : function(menu){
1746            if(!menus){
1747                init();
1748            }
1749            menus[menu.id] = menu;
1750            menu.on("beforehide", onBeforeHide);
1751            menu.on("hide", onHide);
1752            menu.on("beforeshow", onBeforeShow);
1753            menu.on("show", onShow);
1754            var g = menu.group;
1755            if(g && menu.events["checkchange"]){
1756                if(!groups[g]){
1757                    groups[g] = [];
1758                }
1759                groups[g].push(menu);
1760                menu.on("checkchange", onCheck);
1761            }
1762        },
1763
1764         /**
1765          * Returns a {@link Roo.menu.Menu} object
1766          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1767          * be used to generate and return a new Menu instance.
1768          */
1769        get : function(menu){
1770            if(typeof menu == "string"){ // menu id
1771                return menus[menu];
1772            }else if(menu.events){  // menu instance
1773                return menu;
1774            }
1775            /*else if(typeof menu.length == 'number'){ // array of menu items?
1776                return new Roo.bootstrap.Menu({items:menu});
1777            }else{ // otherwise, must be a config
1778                return new Roo.bootstrap.Menu(menu);
1779            }
1780            */
1781            return false;
1782        },
1783
1784        // private
1785        unregister : function(menu){
1786            delete menus[menu.id];
1787            menu.un("beforehide", onBeforeHide);
1788            menu.un("hide", onHide);
1789            menu.un("beforeshow", onBeforeShow);
1790            menu.un("show", onShow);
1791            var g = menu.group;
1792            if(g && menu.events["checkchange"]){
1793                groups[g].remove(menu);
1794                menu.un("checkchange", onCheck);
1795            }
1796        },
1797
1798        // private
1799        registerCheckable : function(menuItem){
1800            var g = menuItem.group;
1801            if(g){
1802                if(!groups[g]){
1803                    groups[g] = [];
1804                }
1805                groups[g].push(menuItem);
1806                menuItem.on("beforecheckchange", onBeforeCheck);
1807            }
1808        },
1809
1810        // private
1811        unregisterCheckable : function(menuItem){
1812            var g = menuItem.group;
1813            if(g){
1814                groups[g].remove(menuItem);
1815                menuItem.un("beforecheckchange", onBeforeCheck);
1816            }
1817        }
1818    };
1819 }();/*
1820  * - LGPL
1821  *
1822  * menu
1823  * 
1824  */
1825
1826 /**
1827  * @class Roo.bootstrap.Menu
1828  * @extends Roo.bootstrap.Component
1829  * Bootstrap Menu class - container for MenuItems
1830  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1831  * 
1832  * @constructor
1833  * Create a new Menu
1834  * @param {Object} config The config object
1835  */
1836
1837
1838 Roo.bootstrap.Menu = function(config){
1839     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1840     if (this.registerMenu) {
1841         Roo.bootstrap.MenuMgr.register(this);
1842     }
1843     this.addEvents({
1844         /**
1845          * @event beforeshow
1846          * Fires before this menu is displayed
1847          * @param {Roo.menu.Menu} this
1848          */
1849         beforeshow : true,
1850         /**
1851          * @event beforehide
1852          * Fires before this menu is hidden
1853          * @param {Roo.menu.Menu} this
1854          */
1855         beforehide : true,
1856         /**
1857          * @event show
1858          * Fires after this menu is displayed
1859          * @param {Roo.menu.Menu} this
1860          */
1861         show : true,
1862         /**
1863          * @event hide
1864          * Fires after this menu is hidden
1865          * @param {Roo.menu.Menu} this
1866          */
1867         hide : true,
1868         /**
1869          * @event click
1870          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1871          * @param {Roo.menu.Menu} this
1872          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1873          * @param {Roo.EventObject} e
1874          */
1875         click : true,
1876         /**
1877          * @event mouseover
1878          * Fires when the mouse is hovering over this menu
1879          * @param {Roo.menu.Menu} this
1880          * @param {Roo.EventObject} e
1881          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1882          */
1883         mouseover : true,
1884         /**
1885          * @event mouseout
1886          * Fires when the mouse exits this menu
1887          * @param {Roo.menu.Menu} this
1888          * @param {Roo.EventObject} e
1889          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1890          */
1891         mouseout : true,
1892         /**
1893          * @event itemclick
1894          * Fires when a menu item contained in this menu is clicked
1895          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1896          * @param {Roo.EventObject} e
1897          */
1898         itemclick: true
1899     });
1900     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1901 };
1902
1903 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1904     
1905    /// html : false,
1906     //align : '',
1907     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1908     type: false,
1909     /**
1910      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1911      */
1912     registerMenu : true,
1913     
1914     menuItems :false, // stores the menu items..
1915     
1916     hidden:true,
1917     
1918     parentMenu : false,
1919     
1920     getChildContainer : function() {
1921         return this.el;  
1922     },
1923     
1924     getAutoCreate : function(){
1925          
1926         //if (['right'].indexOf(this.align)!==-1) {
1927         //    cfg.cn[1].cls += ' pull-right'
1928         //}
1929         
1930         
1931         var cfg = {
1932             tag : 'ul',
1933             cls : 'dropdown-menu' ,
1934             style : 'z-index:1000'
1935             
1936         }
1937         
1938         if (this.type === 'submenu') {
1939             cfg.cls = 'submenu active';
1940         }
1941         if (this.type === 'treeview') {
1942             cfg.cls = 'treeview-menu';
1943         }
1944         
1945         return cfg;
1946     },
1947     initEvents : function() {
1948         
1949        // Roo.log("ADD event");
1950        // Roo.log(this.triggerEl.dom);
1951         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1952         
1953         this.triggerEl.addClass('dropdown-toggle');
1954         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1955
1956         this.el.on("mouseover", this.onMouseOver, this);
1957         this.el.on("mouseout", this.onMouseOut, this);
1958         
1959         
1960     },
1961     findTargetItem : function(e){
1962         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1963         if(!t){
1964             return false;
1965         }
1966         //Roo.log(t);         Roo.log(t.id);
1967         if(t && t.id){
1968             //Roo.log(this.menuitems);
1969             return this.menuitems.get(t.id);
1970             
1971             //return this.items.get(t.menuItemId);
1972         }
1973         
1974         return false;
1975     },
1976     onClick : function(e){
1977         Roo.log("menu.onClick");
1978         var t = this.findTargetItem(e);
1979         if(!t || t.isContainer){
1980             return;
1981         }
1982         Roo.log(e);
1983         /*
1984         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1985             if(t == this.activeItem && t.shouldDeactivate(e)){
1986                 this.activeItem.deactivate();
1987                 delete this.activeItem;
1988                 return;
1989             }
1990             if(t.canActivate){
1991                 this.setActiveItem(t, true);
1992             }
1993             return;
1994             
1995             
1996         }
1997         */
1998        
1999         Roo.log('pass click event');
2000         
2001         t.onClick(e);
2002         
2003         this.fireEvent("click", this, t, e);
2004         
2005         this.hide();
2006     },
2007      onMouseOver : function(e){
2008         var t  = this.findTargetItem(e);
2009         //Roo.log(t);
2010         //if(t){
2011         //    if(t.canActivate && !t.disabled){
2012         //        this.setActiveItem(t, true);
2013         //    }
2014         //}
2015         
2016         this.fireEvent("mouseover", this, e, t);
2017     },
2018     isVisible : function(){
2019         return !this.hidden;
2020     },
2021      onMouseOut : function(e){
2022         var t  = this.findTargetItem(e);
2023         
2024         //if(t ){
2025         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2026         //        this.activeItem.deactivate();
2027         //        delete this.activeItem;
2028         //    }
2029         //}
2030         this.fireEvent("mouseout", this, e, t);
2031     },
2032     
2033     
2034     /**
2035      * Displays this menu relative to another element
2036      * @param {String/HTMLElement/Roo.Element} element The element to align to
2037      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2038      * the element (defaults to this.defaultAlign)
2039      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2040      */
2041     show : function(el, pos, parentMenu){
2042         this.parentMenu = parentMenu;
2043         if(!this.el){
2044             this.render();
2045         }
2046         this.fireEvent("beforeshow", this);
2047         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2048     },
2049      /**
2050      * Displays this menu at a specific xy position
2051      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2052      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2053      */
2054     showAt : function(xy, parentMenu, /* private: */_e){
2055         this.parentMenu = parentMenu;
2056         if(!this.el){
2057             this.render();
2058         }
2059         if(_e !== false){
2060             this.fireEvent("beforeshow", this);
2061             //xy = this.el.adjustForConstraints(xy);
2062         }
2063         
2064         //this.el.show();
2065         this.hideMenuItems();
2066         this.hidden = false;
2067         this.triggerEl.addClass('open');
2068         
2069         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2070             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2071         }
2072         
2073         this.el.setXY(xy);
2074         this.focus();
2075         this.fireEvent("show", this);
2076     },
2077     
2078     focus : function(){
2079         return;
2080         if(!this.hidden){
2081             this.doFocus.defer(50, this);
2082         }
2083     },
2084
2085     doFocus : function(){
2086         if(!this.hidden){
2087             this.focusEl.focus();
2088         }
2089     },
2090
2091     /**
2092      * Hides this menu and optionally all parent menus
2093      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2094      */
2095     hide : function(deep){
2096         
2097         this.hideMenuItems();
2098         if(this.el && this.isVisible()){
2099             this.fireEvent("beforehide", this);
2100             if(this.activeItem){
2101                 this.activeItem.deactivate();
2102                 this.activeItem = null;
2103             }
2104             this.triggerEl.removeClass('open');;
2105             this.hidden = true;
2106             this.fireEvent("hide", this);
2107         }
2108         if(deep === true && this.parentMenu){
2109             this.parentMenu.hide(true);
2110         }
2111     },
2112     
2113     onTriggerPress  : function(e)
2114     {
2115         
2116         Roo.log('trigger press');
2117         //Roo.log(e.getTarget());
2118        // Roo.log(this.triggerEl.dom);
2119         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2120             return;
2121         }
2122         
2123         if (this.isVisible()) {
2124             Roo.log('hide');
2125             this.hide();
2126         } else {
2127             Roo.log('show');
2128             this.show(this.triggerEl, false, false);
2129         }
2130         
2131         e.stopEvent();
2132     },
2133     
2134          
2135        
2136     
2137     hideMenuItems : function()
2138     {
2139         //$(backdrop).remove()
2140         Roo.select('.open',true).each(function(aa) {
2141             
2142             aa.removeClass('open');
2143           //var parent = getParent($(this))
2144           //var relatedTarget = { relatedTarget: this }
2145           
2146            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2147           //if (e.isDefaultPrevented()) return
2148            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2149         })
2150     },
2151     addxtypeChild : function (tree, cntr) {
2152         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2153           
2154         this.menuitems.add(comp);
2155         return comp;
2156
2157     },
2158     getEl : function()
2159     {
2160         Roo.log(this.el);
2161         return this.el;
2162     }
2163 });
2164
2165  
2166  /*
2167  * - LGPL
2168  *
2169  * menu item
2170  * 
2171  */
2172
2173
2174 /**
2175  * @class Roo.bootstrap.MenuItem
2176  * @extends Roo.bootstrap.Component
2177  * Bootstrap MenuItem class
2178  * @cfg {String} html the menu label
2179  * @cfg {String} href the link
2180  * @cfg {Boolean} preventDefault (true | false) default true
2181  * @cfg {Boolean} isContainer (true | false) default false
2182  * 
2183  * 
2184  * @constructor
2185  * Create a new MenuItem
2186  * @param {Object} config The config object
2187  */
2188
2189
2190 Roo.bootstrap.MenuItem = function(config){
2191     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2192     this.addEvents({
2193         // raw events
2194         /**
2195          * @event click
2196          * The raw click event for the entire grid.
2197          * @param {Roo.bootstrap.MenuItem} this
2198          * @param {Roo.EventObject} e
2199          */
2200         "click" : true
2201     });
2202 };
2203
2204 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2205     
2206     href : false,
2207     html : false,
2208     preventDefault: true,
2209     isContainer : false,
2210     
2211     getAutoCreate : function(){
2212         
2213         if(this.isContainer){
2214             return {
2215                 tag: 'li',
2216                 cls: 'dropdown-menu-item'
2217             };
2218         }
2219         
2220         var cfg= {
2221             tag: 'li',
2222             cls: 'dropdown-menu-item',
2223             cn: [
2224                     {
2225                         tag : 'a',
2226                         href : '#',
2227                         html : 'Link'
2228                     }
2229                 ]
2230         };
2231         if (this.parent().type == 'treeview') {
2232             cfg.cls = 'treeview-menu';
2233         }
2234         
2235         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2236         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2237         return cfg;
2238     },
2239     
2240     initEvents: function() {
2241         
2242         //this.el.select('a').on('click', this.onClick, this);
2243         
2244     },
2245     onClick : function(e)
2246     {
2247         Roo.log('item on click ');
2248         //if(this.preventDefault){
2249         //    e.preventDefault();
2250         //}
2251         //this.parent().hideMenuItems();
2252         
2253         this.fireEvent('click', this, e);
2254     },
2255     getEl : function()
2256     {
2257         return this.el;
2258     }
2259 });
2260
2261  
2262
2263  /*
2264  * - LGPL
2265  *
2266  * menu separator
2267  * 
2268  */
2269
2270
2271 /**
2272  * @class Roo.bootstrap.MenuSeparator
2273  * @extends Roo.bootstrap.Component
2274  * Bootstrap MenuSeparator class
2275  * 
2276  * @constructor
2277  * Create a new MenuItem
2278  * @param {Object} config The config object
2279  */
2280
2281
2282 Roo.bootstrap.MenuSeparator = function(config){
2283     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2284 };
2285
2286 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2287     
2288     getAutoCreate : function(){
2289         var cfg = {
2290             cls: 'divider',
2291             tag : 'li'
2292         };
2293         
2294         return cfg;
2295     }
2296    
2297 });
2298
2299  
2300
2301  
2302 /*
2303 * Licence: LGPL
2304 */
2305
2306 /**
2307  * @class Roo.bootstrap.Modal
2308  * @extends Roo.bootstrap.Component
2309  * Bootstrap Modal class
2310  * @cfg {String} title Title of dialog
2311  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2312  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2313  * @cfg {Boolean} specificTitle default false
2314  * @cfg {Array} buttons Array of buttons or standard button set..
2315  * @cfg {String} buttonPosition (left|right|center) default right
2316  * @cfg {Boolean} animate default true
2317  * @cfg {Boolean} allow_close default true
2318  * 
2319  * @constructor
2320  * Create a new Modal Dialog
2321  * @param {Object} config The config object
2322  */
2323
2324 Roo.bootstrap.Modal = function(config){
2325     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2326     this.addEvents({
2327         // raw events
2328         /**
2329          * @event btnclick
2330          * The raw btnclick event for the button
2331          * @param {Roo.EventObject} e
2332          */
2333         "btnclick" : true
2334     });
2335     this.buttons = this.buttons || [];
2336      
2337     if (this.tmpl) {
2338         this.tmpl = Roo.factory(this.tmpl);
2339     }
2340     
2341 };
2342
2343 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2344     
2345     title : 'test dialog',
2346    
2347     buttons : false,
2348     
2349     // set on load...
2350      
2351     html: false,
2352     
2353     tmp: false,
2354     
2355     specificTitle: false,
2356     
2357     buttonPosition: 'right',
2358     
2359     allow_close : true,
2360     
2361     animate : true,
2362     
2363     
2364      // private
2365     bodyEl:  false,
2366     footerEl:  false,
2367     titleEl:  false,
2368     closeEl:  false,
2369     
2370     
2371     onRender : function(ct, position)
2372     {
2373         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2374      
2375         if(!this.el){
2376             var cfg = Roo.apply({},  this.getAutoCreate());
2377             cfg.id = Roo.id();
2378             //if(!cfg.name){
2379             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2380             //}
2381             //if (!cfg.name.length) {
2382             //    delete cfg.name;
2383            // }
2384             if (this.cls) {
2385                 cfg.cls += ' ' + this.cls;
2386             }
2387             if (this.style) {
2388                 cfg.style = this.style;
2389             }
2390             this.el = Roo.get(document.body).createChild(cfg, position);
2391         }
2392         //var type = this.el.dom.type;
2393         
2394         
2395         
2396         
2397         if(this.tabIndex !== undefined){
2398             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2399         }
2400         
2401         
2402         this.bodyEl = this.el.select('.modal-body',true).first();
2403         this.closeEl = this.el.select('.modal-header .close', true).first();
2404         this.footerEl = this.el.select('.modal-footer',true).first();
2405         this.titleEl = this.el.select('.modal-title',true).first();
2406         
2407         
2408          
2409         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2410         this.maskEl.enableDisplayMode("block");
2411         this.maskEl.hide();
2412         //this.el.addClass("x-dlg-modal");
2413     
2414         if (this.buttons.length) {
2415             Roo.each(this.buttons, function(bb) {
2416                 b = Roo.apply({}, bb);
2417                 b.xns = b.xns || Roo.bootstrap;
2418                 b.xtype = b.xtype || 'Button';
2419                 if (typeof(b.listeners) == 'undefined') {
2420                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2421                 }
2422                 
2423                 var btn = Roo.factory(b);
2424                 
2425                 btn.onRender(this.el.select('.modal-footer div').first());
2426                 
2427             },this);
2428         }
2429         // render the children.
2430         var nitems = [];
2431         
2432         if(typeof(this.items) != 'undefined'){
2433             var items = this.items;
2434             delete this.items;
2435
2436             for(var i =0;i < items.length;i++) {
2437                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2438             }
2439         }
2440         
2441         this.items = nitems;
2442         
2443         // where are these used - they used to be body/close/footer
2444         
2445        
2446         this.initEvents();
2447         //this.el.addClass([this.fieldClass, this.cls]);
2448         
2449     },
2450     getAutoCreate : function(){
2451         
2452         
2453         var bdy = {
2454                 cls : 'modal-body',
2455                 html : this.html || ''
2456         };
2457         
2458         var title = {
2459             tag: 'h4',
2460             cls : 'modal-title',
2461             html : this.title
2462         };
2463         
2464         if(this.specificTitle){
2465             title = this.title;
2466             
2467         };
2468         
2469         var header = [];
2470         if (this.allow_close) {
2471             header.push({
2472                 tag: 'button',
2473                 cls : 'close',
2474                 html : '&times'
2475             });
2476         }
2477         header.push(title);
2478         
2479         var modal = {
2480             cls: "modal",
2481             style : 'display: none',
2482             cn : [
2483                 {
2484                     cls: "modal-dialog",
2485                     cn : [
2486                         {
2487                             cls : "modal-content",
2488                             cn : [
2489                                 {
2490                                     cls : 'modal-header',
2491                                     cn : header
2492                                 },
2493                                 bdy,
2494                                 {
2495                                     cls : 'modal-footer',
2496                                     cn : [
2497                                         {
2498                                             tag: 'div',
2499                                             cls: 'btn-' + this.buttonPosition
2500                                         }
2501                                     ]
2502                                     
2503                                 }
2504                                 
2505                                 
2506                             ]
2507                             
2508                         }
2509                     ]
2510                         
2511                 }
2512             ]
2513         };
2514         
2515         if(this.animate){
2516             modal.cls += ' fade';
2517         }
2518         
2519         return modal;
2520           
2521     },
2522     getChildContainer : function() {
2523          
2524          return this.bodyEl;
2525         
2526     },
2527     getButtonContainer : function() {
2528          return this.el.select('.modal-footer div',true).first();
2529         
2530     },
2531     initEvents : function()
2532     {
2533         if (this.allow_close) {
2534             this.closeEl.on('click', this.hide, this);
2535         }
2536
2537     },
2538     show : function() {
2539         
2540         if (!this.rendered) {
2541             this.render();
2542         }
2543         
2544         this.el.setStyle('display', 'block');
2545         
2546         if(this.animate){
2547             var _this = this;
2548             (function(){ _this.el.addClass('in'); }).defer(50);
2549         }else{
2550             this.el.addClass('in');
2551         }
2552         
2553         // not sure how we can show data in here.. 
2554         //if (this.tmpl) {
2555         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2556         //}
2557         
2558         Roo.get(document.body).addClass("x-body-masked");
2559         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2560         this.maskEl.show();
2561         this.el.setStyle('zIndex', '10001');
2562        
2563         this.fireEvent('show', this);
2564         
2565         
2566     },
2567     hide : function()
2568     {
2569         this.maskEl.hide();
2570         Roo.get(document.body).removeClass("x-body-masked");
2571         this.el.removeClass('in');
2572         
2573         if(this.animate){
2574             var _this = this;
2575             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2576         }else{
2577             this.el.setStyle('display', 'none');
2578         }
2579         
2580         this.fireEvent('hide', this);
2581     },
2582     
2583     addButton : function(str, cb)
2584     {
2585          
2586         
2587         var b = Roo.apply({}, { html : str } );
2588         b.xns = b.xns || Roo.bootstrap;
2589         b.xtype = b.xtype || 'Button';
2590         if (typeof(b.listeners) == 'undefined') {
2591             b.listeners = { click : cb.createDelegate(this)  };
2592         }
2593         
2594         var btn = Roo.factory(b);
2595            
2596         btn.onRender(this.el.select('.modal-footer div').first());
2597         
2598         return btn;   
2599        
2600     },
2601     
2602     setDefaultButton : function(btn)
2603     {
2604         //this.el.select('.modal-footer').()
2605     },
2606     resizeTo: function(w,h)
2607     {
2608         // skip..
2609     },
2610     setContentSize  : function(w, h)
2611     {
2612         
2613     },
2614     onButtonClick: function(btn,e)
2615     {
2616         //Roo.log([a,b,c]);
2617         this.fireEvent('btnclick', btn.name, e);
2618     },
2619      /**
2620      * Set the title of the Dialog
2621      * @param {String} str new Title
2622      */
2623     setTitle: function(str) {
2624         this.titleEl.dom.innerHTML = str;    
2625     },
2626     /**
2627      * Set the body of the Dialog
2628      * @param {String} str new Title
2629      */
2630     setBody: function(str) {
2631         this.bodyEl.dom.innerHTML = str;    
2632     },
2633     /**
2634      * Set the body of the Dialog using the template
2635      * @param {Obj} data - apply this data to the template and replace the body contents.
2636      */
2637     applyBody: function(obj)
2638     {
2639         if (!this.tmpl) {
2640             Roo.log("Error - using apply Body without a template");
2641             //code
2642         }
2643         this.tmpl.overwrite(this.bodyEl, obj);
2644     }
2645     
2646 });
2647
2648
2649 Roo.apply(Roo.bootstrap.Modal,  {
2650     /**
2651          * Button config that displays a single OK button
2652          * @type Object
2653          */
2654         OK :  [{
2655             name : 'ok',
2656             weight : 'primary',
2657             html : 'OK'
2658         }], 
2659         /**
2660          * Button config that displays Yes and No buttons
2661          * @type Object
2662          */
2663         YESNO : [
2664             {
2665                 name  : 'no',
2666                 html : 'No'
2667             },
2668             {
2669                 name  :'yes',
2670                 weight : 'primary',
2671                 html : 'Yes'
2672             }
2673         ],
2674         
2675         /**
2676          * Button config that displays OK and Cancel buttons
2677          * @type Object
2678          */
2679         OKCANCEL : [
2680             {
2681                name : 'cancel',
2682                 html : 'Cancel'
2683             },
2684             {
2685                 name : 'ok',
2686                 weight : 'primary',
2687                 html : 'OK'
2688             }
2689         ],
2690         /**
2691          * Button config that displays Yes, No and Cancel buttons
2692          * @type Object
2693          */
2694         YESNOCANCEL : [
2695             {
2696                 name : 'yes',
2697                 weight : 'primary',
2698                 html : 'Yes'
2699             },
2700             {
2701                 name : 'no',
2702                 html : 'No'
2703             },
2704             {
2705                 name : 'cancel',
2706                 html : 'Cancel'
2707             }
2708         ]
2709 });
2710  
2711  /*
2712  * - LGPL
2713  *
2714  * messagebox - can be used as a replace
2715  * 
2716  */
2717 /**
2718  * @class Roo.MessageBox
2719  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2720  * Example usage:
2721  *<pre><code>
2722 // Basic alert:
2723 Roo.Msg.alert('Status', 'Changes saved successfully.');
2724
2725 // Prompt for user data:
2726 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2727     if (btn == 'ok'){
2728         // process text value...
2729     }
2730 });
2731
2732 // Show a dialog using config options:
2733 Roo.Msg.show({
2734    title:'Save Changes?',
2735    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2736    buttons: Roo.Msg.YESNOCANCEL,
2737    fn: processResult,
2738    animEl: 'elId'
2739 });
2740 </code></pre>
2741  * @singleton
2742  */
2743 Roo.bootstrap.MessageBox = function(){
2744     var dlg, opt, mask, waitTimer;
2745     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2746     var buttons, activeTextEl, bwidth;
2747
2748     
2749     // private
2750     var handleButton = function(button){
2751         dlg.hide();
2752         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2753     };
2754
2755     // private
2756     var handleHide = function(){
2757         if(opt && opt.cls){
2758             dlg.el.removeClass(opt.cls);
2759         }
2760         //if(waitTimer){
2761         //    Roo.TaskMgr.stop(waitTimer);
2762         //    waitTimer = null;
2763         //}
2764     };
2765
2766     // private
2767     var updateButtons = function(b){
2768         var width = 0;
2769         if(!b){
2770             buttons["ok"].hide();
2771             buttons["cancel"].hide();
2772             buttons["yes"].hide();
2773             buttons["no"].hide();
2774             //dlg.footer.dom.style.display = 'none';
2775             return width;
2776         }
2777         dlg.footerEl.dom.style.display = '';
2778         for(var k in buttons){
2779             if(typeof buttons[k] != "function"){
2780                 if(b[k]){
2781                     buttons[k].show();
2782                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2783                     width += buttons[k].el.getWidth()+15;
2784                 }else{
2785                     buttons[k].hide();
2786                 }
2787             }
2788         }
2789         return width;
2790     };
2791
2792     // private
2793     var handleEsc = function(d, k, e){
2794         if(opt && opt.closable !== false){
2795             dlg.hide();
2796         }
2797         if(e){
2798             e.stopEvent();
2799         }
2800     };
2801
2802     return {
2803         /**
2804          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2805          * @return {Roo.BasicDialog} The BasicDialog element
2806          */
2807         getDialog : function(){
2808            if(!dlg){
2809                 dlg = new Roo.bootstrap.Modal( {
2810                     //draggable: true,
2811                     //resizable:false,
2812                     //constraintoviewport:false,
2813                     //fixedcenter:true,
2814                     //collapsible : false,
2815                     //shim:true,
2816                     //modal: true,
2817                   //  width:400,
2818                   //  height:100,
2819                     //buttonAlign:"center",
2820                     closeClick : function(){
2821                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2822                             handleButton("no");
2823                         }else{
2824                             handleButton("cancel");
2825                         }
2826                     }
2827                 });
2828                 dlg.render();
2829                 dlg.on("hide", handleHide);
2830                 mask = dlg.mask;
2831                 //dlg.addKeyListener(27, handleEsc);
2832                 buttons = {};
2833                 this.buttons = buttons;
2834                 var bt = this.buttonText;
2835                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2836                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2837                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2838                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2839                 Roo.log(buttons)
2840                 bodyEl = dlg.bodyEl.createChild({
2841
2842                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2843                         '<textarea class="roo-mb-textarea"></textarea>' +
2844                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2845                 });
2846                 msgEl = bodyEl.dom.firstChild;
2847                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2848                 textboxEl.enableDisplayMode();
2849                 textboxEl.addKeyListener([10,13], function(){
2850                     if(dlg.isVisible() && opt && opt.buttons){
2851                         if(opt.buttons.ok){
2852                             handleButton("ok");
2853                         }else if(opt.buttons.yes){
2854                             handleButton("yes");
2855                         }
2856                     }
2857                 });
2858                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2859                 textareaEl.enableDisplayMode();
2860                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2861                 progressEl.enableDisplayMode();
2862                 var pf = progressEl.dom.firstChild;
2863                 if (pf) {
2864                     pp = Roo.get(pf.firstChild);
2865                     pp.setHeight(pf.offsetHeight);
2866                 }
2867                 
2868             }
2869             return dlg;
2870         },
2871
2872         /**
2873          * Updates the message box body text
2874          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2875          * the XHTML-compliant non-breaking space character '&amp;#160;')
2876          * @return {Roo.MessageBox} This message box
2877          */
2878         updateText : function(text){
2879             if(!dlg.isVisible() && !opt.width){
2880                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2881             }
2882             msgEl.innerHTML = text || '&#160;';
2883       
2884             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2885             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2886             var w = Math.max(
2887                     Math.min(opt.width || cw , this.maxWidth), 
2888                     Math.max(opt.minWidth || this.minWidth, bwidth)
2889             );
2890             if(opt.prompt){
2891                 activeTextEl.setWidth(w);
2892             }
2893             if(dlg.isVisible()){
2894                 dlg.fixedcenter = false;
2895             }
2896             // to big, make it scroll. = But as usual stupid IE does not support
2897             // !important..
2898             
2899             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2900                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2901                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2902             } else {
2903                 bodyEl.dom.style.height = '';
2904                 bodyEl.dom.style.overflowY = '';
2905             }
2906             if (cw > w) {
2907                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2908             } else {
2909                 bodyEl.dom.style.overflowX = '';
2910             }
2911             
2912             dlg.setContentSize(w, bodyEl.getHeight());
2913             if(dlg.isVisible()){
2914                 dlg.fixedcenter = true;
2915             }
2916             return this;
2917         },
2918
2919         /**
2920          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2921          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2922          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2923          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2924          * @return {Roo.MessageBox} This message box
2925          */
2926         updateProgress : function(value, text){
2927             if(text){
2928                 this.updateText(text);
2929             }
2930             if (pp) { // weird bug on my firefox - for some reason this is not defined
2931                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2932             }
2933             return this;
2934         },        
2935
2936         /**
2937          * Returns true if the message box is currently displayed
2938          * @return {Boolean} True if the message box is visible, else false
2939          */
2940         isVisible : function(){
2941             return dlg && dlg.isVisible();  
2942         },
2943
2944         /**
2945          * Hides the message box if it is displayed
2946          */
2947         hide : function(){
2948             if(this.isVisible()){
2949                 dlg.hide();
2950             }  
2951         },
2952
2953         /**
2954          * Displays a new message box, or reinitializes an existing message box, based on the config options
2955          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2956          * The following config object properties are supported:
2957          * <pre>
2958 Property    Type             Description
2959 ----------  ---------------  ------------------------------------------------------------------------------------
2960 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2961                                    closes (defaults to undefined)
2962 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2963                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2964 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2965                                    progress and wait dialogs will ignore this property and always hide the
2966                                    close button as they can only be closed programmatically.
2967 cls               String           A custom CSS class to apply to the message box element
2968 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2969                                    displayed (defaults to 75)
2970 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2971                                    function will be btn (the name of the button that was clicked, if applicable,
2972                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2973                                    Progress and wait dialogs will ignore this option since they do not respond to
2974                                    user actions and can only be closed programmatically, so any required function
2975                                    should be called by the same code after it closes the dialog.
2976 icon              String           A CSS class that provides a background image to be used as an icon for
2977                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2978 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2979 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2980 modal             Boolean          False to allow user interaction with the page while the message box is
2981                                    displayed (defaults to true)
2982 msg               String           A string that will replace the existing message box body text (defaults
2983                                    to the XHTML-compliant non-breaking space character '&#160;')
2984 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2985 progress          Boolean          True to display a progress bar (defaults to false)
2986 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2987 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2988 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2989 title             String           The title text
2990 value             String           The string value to set into the active textbox element if displayed
2991 wait              Boolean          True to display a progress bar (defaults to false)
2992 width             Number           The width of the dialog in pixels
2993 </pre>
2994          *
2995          * Example usage:
2996          * <pre><code>
2997 Roo.Msg.show({
2998    title: 'Address',
2999    msg: 'Please enter your address:',
3000    width: 300,
3001    buttons: Roo.MessageBox.OKCANCEL,
3002    multiline: true,
3003    fn: saveAddress,
3004    animEl: 'addAddressBtn'
3005 });
3006 </code></pre>
3007          * @param {Object} config Configuration options
3008          * @return {Roo.MessageBox} This message box
3009          */
3010         show : function(options)
3011         {
3012             
3013             // this causes nightmares if you show one dialog after another
3014             // especially on callbacks..
3015              
3016             if(this.isVisible()){
3017                 
3018                 this.hide();
3019                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3020                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3021                 Roo.log("New Dialog Message:" +  options.msg )
3022                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3023                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3024                 
3025             }
3026             var d = this.getDialog();
3027             opt = options;
3028             d.setTitle(opt.title || "&#160;");
3029             d.closeEl.setDisplayed(opt.closable !== false);
3030             activeTextEl = textboxEl;
3031             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3032             if(opt.prompt){
3033                 if(opt.multiline){
3034                     textboxEl.hide();
3035                     textareaEl.show();
3036                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3037                         opt.multiline : this.defaultTextHeight);
3038                     activeTextEl = textareaEl;
3039                 }else{
3040                     textboxEl.show();
3041                     textareaEl.hide();
3042                 }
3043             }else{
3044                 textboxEl.hide();
3045                 textareaEl.hide();
3046             }
3047             progressEl.setDisplayed(opt.progress === true);
3048             this.updateProgress(0);
3049             activeTextEl.dom.value = opt.value || "";
3050             if(opt.prompt){
3051                 dlg.setDefaultButton(activeTextEl);
3052             }else{
3053                 var bs = opt.buttons;
3054                 var db = null;
3055                 if(bs && bs.ok){
3056                     db = buttons["ok"];
3057                 }else if(bs && bs.yes){
3058                     db = buttons["yes"];
3059                 }
3060                 dlg.setDefaultButton(db);
3061             }
3062             bwidth = updateButtons(opt.buttons);
3063             this.updateText(opt.msg);
3064             if(opt.cls){
3065                 d.el.addClass(opt.cls);
3066             }
3067             d.proxyDrag = opt.proxyDrag === true;
3068             d.modal = opt.modal !== false;
3069             d.mask = opt.modal !== false ? mask : false;
3070             if(!d.isVisible()){
3071                 // force it to the end of the z-index stack so it gets a cursor in FF
3072                 document.body.appendChild(dlg.el.dom);
3073                 d.animateTarget = null;
3074                 d.show(options.animEl);
3075             }
3076             return this;
3077         },
3078
3079         /**
3080          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3081          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3082          * and closing the message box when the process is complete.
3083          * @param {String} title The title bar text
3084          * @param {String} msg The message box body text
3085          * @return {Roo.MessageBox} This message box
3086          */
3087         progress : function(title, msg){
3088             this.show({
3089                 title : title,
3090                 msg : msg,
3091                 buttons: false,
3092                 progress:true,
3093                 closable:false,
3094                 minWidth: this.minProgressWidth,
3095                 modal : true
3096             });
3097             return this;
3098         },
3099
3100         /**
3101          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3102          * If a callback function is passed it will be called after the user clicks the button, and the
3103          * id of the button that was clicked will be passed as the only parameter to the callback
3104          * (could also be the top-right close button).
3105          * @param {String} title The title bar text
3106          * @param {String} msg The message box body text
3107          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3108          * @param {Object} scope (optional) The scope of the callback function
3109          * @return {Roo.MessageBox} This message box
3110          */
3111         alert : function(title, msg, fn, scope){
3112             this.show({
3113                 title : title,
3114                 msg : msg,
3115                 buttons: this.OK,
3116                 fn: fn,
3117                 scope : scope,
3118                 modal : true
3119             });
3120             return this;
3121         },
3122
3123         /**
3124          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3125          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3126          * You are responsible for closing the message box when the process is complete.
3127          * @param {String} msg The message box body text
3128          * @param {String} title (optional) The title bar text
3129          * @return {Roo.MessageBox} This message box
3130          */
3131         wait : function(msg, title){
3132             this.show({
3133                 title : title,
3134                 msg : msg,
3135                 buttons: false,
3136                 closable:false,
3137                 progress:true,
3138                 modal:true,
3139                 width:300,
3140                 wait:true
3141             });
3142             waitTimer = Roo.TaskMgr.start({
3143                 run: function(i){
3144                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3145                 },
3146                 interval: 1000
3147             });
3148             return this;
3149         },
3150
3151         /**
3152          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3153          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3154          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3155          * @param {String} title The title bar text
3156          * @param {String} msg The message box body text
3157          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3158          * @param {Object} scope (optional) The scope of the callback function
3159          * @return {Roo.MessageBox} This message box
3160          */
3161         confirm : function(title, msg, fn, scope){
3162             this.show({
3163                 title : title,
3164                 msg : msg,
3165                 buttons: this.YESNO,
3166                 fn: fn,
3167                 scope : scope,
3168                 modal : true
3169             });
3170             return this;
3171         },
3172
3173         /**
3174          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3175          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3176          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3177          * (could also be the top-right close button) and the text that was entered will be passed as the two
3178          * parameters to the callback.
3179          * @param {String} title The title bar text
3180          * @param {String} msg The message box body text
3181          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3182          * @param {Object} scope (optional) The scope of the callback function
3183          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3184          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3185          * @return {Roo.MessageBox} This message box
3186          */
3187         prompt : function(title, msg, fn, scope, multiline){
3188             this.show({
3189                 title : title,
3190                 msg : msg,
3191                 buttons: this.OKCANCEL,
3192                 fn: fn,
3193                 minWidth:250,
3194                 scope : scope,
3195                 prompt:true,
3196                 multiline: multiline,
3197                 modal : true
3198             });
3199             return this;
3200         },
3201
3202         /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK : {ok:true},
3207         /**
3208          * Button config that displays Yes and No buttons
3209          * @type Object
3210          */
3211         YESNO : {yes:true, no:true},
3212         /**
3213          * Button config that displays OK and Cancel buttons
3214          * @type Object
3215          */
3216         OKCANCEL : {ok:true, cancel:true},
3217         /**
3218          * Button config that displays Yes, No and Cancel buttons
3219          * @type Object
3220          */
3221         YESNOCANCEL : {yes:true, no:true, cancel:true},
3222
3223         /**
3224          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3225          * @type Number
3226          */
3227         defaultTextHeight : 75,
3228         /**
3229          * The maximum width in pixels of the message box (defaults to 600)
3230          * @type Number
3231          */
3232         maxWidth : 600,
3233         /**
3234          * The minimum width in pixels of the message box (defaults to 100)
3235          * @type Number
3236          */
3237         minWidth : 100,
3238         /**
3239          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3240          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3241          * @type Number
3242          */
3243         minProgressWidth : 250,
3244         /**
3245          * An object containing the default button text strings that can be overriden for localized language support.
3246          * Supported properties are: ok, cancel, yes and no.
3247          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3248          * @type Object
3249          */
3250         buttonText : {
3251             ok : "OK",
3252             cancel : "Cancel",
3253             yes : "Yes",
3254             no : "No"
3255         }
3256     };
3257 }();
3258
3259 /**
3260  * Shorthand for {@link Roo.MessageBox}
3261  */
3262 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3263 Roo.Msg = Roo.Msg || Roo.MessageBox;
3264 /*
3265  * - LGPL
3266  *
3267  * navbar
3268  * 
3269  */
3270
3271 /**
3272  * @class Roo.bootstrap.Navbar
3273  * @extends Roo.bootstrap.Component
3274  * Bootstrap Navbar class
3275
3276  * @constructor
3277  * Create a new Navbar
3278  * @param {Object} config The config object
3279  */
3280
3281
3282 Roo.bootstrap.Navbar = function(config){
3283     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3284     
3285 };
3286
3287 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3288     
3289     
3290    
3291     // private
3292     navItems : false,
3293     loadMask : false,
3294     
3295     
3296     getAutoCreate : function(){
3297         
3298         
3299         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3300         
3301     },
3302     
3303     initEvents :function ()
3304     {
3305         //Roo.log(this.el.select('.navbar-toggle',true));
3306         this.el.select('.navbar-toggle',true).on('click', function() {
3307            // Roo.log('click');
3308             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3309         }, this);
3310         
3311         var mark = {
3312             tag: "div",
3313             cls:"x-dlg-mask"
3314         }
3315         
3316         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3317         
3318         var size = this.el.getSize();
3319         this.maskEl.setSize(size.width, size.height);
3320         this.maskEl.enableDisplayMode("block");
3321         this.maskEl.hide();
3322         
3323         if(this.loadMask){
3324             this.maskEl.show();
3325         }
3326     },
3327     
3328     
3329     getChildContainer : function()
3330     {
3331         if (this.el.select('.collapse').getCount()) {
3332             return this.el.select('.collapse',true).first();
3333         }
3334         
3335         return this.el;
3336     },
3337     
3338     mask : function()
3339     {
3340         this.maskEl.show();
3341     },
3342     
3343     unmask : function()
3344     {
3345         this.maskEl.hide();
3346     } 
3347     
3348     
3349     
3350     
3351 });
3352
3353
3354
3355  
3356
3357  /*
3358  * - LGPL
3359  *
3360  * navbar
3361  * 
3362  */
3363
3364 /**
3365  * @class Roo.bootstrap.NavSimplebar
3366  * @extends Roo.bootstrap.Navbar
3367  * Bootstrap Sidebar class
3368  *
3369  * @cfg {Boolean} inverse is inverted color
3370  * 
3371  * @cfg {String} type (nav | pills | tabs)
3372  * @cfg {Boolean} arrangement stacked | justified
3373  * @cfg {String} align (left | right) alignment
3374  * 
3375  * @cfg {Boolean} main (true|false) main nav bar? default false
3376  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3377  * 
3378  * @cfg {String} tag (header|footer|nav|div) default is nav 
3379
3380  * 
3381  * 
3382  * 
3383  * @constructor
3384  * Create a new Sidebar
3385  * @param {Object} config The config object
3386  */
3387
3388
3389 Roo.bootstrap.NavSimplebar = function(config){
3390     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3391 };
3392
3393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3394     
3395     inverse: false,
3396     
3397     type: false,
3398     arrangement: '',
3399     align : false,
3400     
3401     
3402     
3403     main : false,
3404     
3405     
3406     tag : false,
3407     
3408     
3409     getAutoCreate : function(){
3410         
3411         
3412         var cfg = {
3413             tag : this.tag || 'div',
3414             cls : 'navbar'
3415         };
3416           
3417         
3418         cfg.cn = [
3419             {
3420                 cls: 'nav',
3421                 tag : 'ul'
3422             }
3423         ];
3424         
3425          
3426         this.type = this.type || 'nav';
3427         if (['tabs','pills'].indexOf(this.type)!==-1) {
3428             cfg.cn[0].cls += ' nav-' + this.type
3429         
3430         
3431         } else {
3432             if (this.type!=='nav') {
3433                 Roo.log('nav type must be nav/tabs/pills')
3434             }
3435             cfg.cn[0].cls += ' navbar-nav'
3436         }
3437         
3438         
3439         
3440         
3441         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3442             cfg.cn[0].cls += ' nav-' + this.arrangement;
3443         }
3444         
3445         
3446         if (this.align === 'right') {
3447             cfg.cn[0].cls += ' navbar-right';
3448         }
3449         
3450         if (this.inverse) {
3451             cfg.cls += ' navbar-inverse';
3452             
3453         }
3454         
3455         
3456         return cfg;
3457     
3458         
3459     }
3460     
3461     
3462     
3463 });
3464
3465
3466
3467  
3468
3469  
3470        /*
3471  * - LGPL
3472  *
3473  * navbar
3474  * 
3475  */
3476
3477 /**
3478  * @class Roo.bootstrap.NavHeaderbar
3479  * @extends Roo.bootstrap.NavSimplebar
3480  * Bootstrap Sidebar class
3481  *
3482  * @cfg {String} brand what is brand
3483  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3484  * @cfg {String} brand_href href of the brand
3485  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3486  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3487  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3488  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3489  * 
3490  * @constructor
3491  * Create a new Sidebar
3492  * @param {Object} config The config object
3493  */
3494
3495
3496 Roo.bootstrap.NavHeaderbar = function(config){
3497     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3498       
3499 };
3500
3501 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3502     
3503     position: '',
3504     brand: '',
3505     brand_href: false,
3506     srButton : true,
3507     autohide : false,
3508     desktopCenter : false,
3509    
3510     
3511     getAutoCreate : function(){
3512         
3513         var   cfg = {
3514             tag: this.nav || 'nav',
3515             cls: 'navbar',
3516             role: 'navigation',
3517             cn: []
3518         };
3519         
3520         var cn = cfg.cn;
3521         if (this.desktopCenter) {
3522             cn.push({cls : 'container', cn : []});
3523             cn = cn[0].cn;
3524         }
3525         
3526         if(this.srButton){
3527             cn.push({
3528                 tag: 'div',
3529                 cls: 'navbar-header',
3530                 cn: [
3531                     {
3532                         tag: 'button',
3533                         type: 'button',
3534                         cls: 'navbar-toggle',
3535                         'data-toggle': 'collapse',
3536                         cn: [
3537                             {
3538                                 tag: 'span',
3539                                 cls: 'sr-only',
3540                                 html: 'Toggle navigation'
3541                             },
3542                             {
3543                                 tag: 'span',
3544                                 cls: 'icon-bar'
3545                             },
3546                             {
3547                                 tag: 'span',
3548                                 cls: 'icon-bar'
3549                             },
3550                             {
3551                                 tag: 'span',
3552                                 cls: 'icon-bar'
3553                             }
3554                         ]
3555                     }
3556                 ]
3557             });
3558         }
3559         
3560         cn.push({
3561             tag: 'div',
3562             cls: 'collapse navbar-collapse',
3563             cn : []
3564         });
3565         
3566         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3567         
3568         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3569             cfg.cls += ' navbar-' + this.position;
3570             
3571             // tag can override this..
3572             
3573             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3574         }
3575         
3576         if (this.brand !== '') {
3577             cn[0].cn.push({
3578                 tag: 'a',
3579                 href: this.brand_href ? this.brand_href : '#',
3580                 cls: 'navbar-brand',
3581                 cn: [
3582                 this.brand
3583                 ]
3584             });
3585         }
3586         
3587         if(this.main){
3588             cfg.cls += ' main-nav';
3589         }
3590         
3591         
3592         return cfg;
3593
3594         
3595     },
3596     getHeaderChildContainer : function()
3597     {
3598         if (this.el.select('.navbar-header').getCount()) {
3599             return this.el.select('.navbar-header',true).first();
3600         }
3601         
3602         return this.getChildContainer();
3603     },
3604     
3605     
3606     initEvents : function()
3607     {
3608         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3609         
3610         if (this.autohide) {
3611             
3612             var prevScroll = 0;
3613             var ft = this.el;
3614             
3615             Roo.get(document).on('scroll',function(e) {
3616                 var ns = Roo.get(document).getScroll().top;
3617                 var os = prevScroll;
3618                 prevScroll = ns;
3619                 
3620                 if(ns > os){
3621                     ft.removeClass('slideDown');
3622                     ft.addClass('slideUp');
3623                     return;
3624                 }
3625                 ft.removeClass('slideUp');
3626                 ft.addClass('slideDown');
3627                  
3628               
3629           },this);
3630         }
3631     }    
3632     
3633 });
3634
3635
3636
3637  
3638
3639  /*
3640  * - LGPL
3641  *
3642  * navbar
3643  * 
3644  */
3645
3646 /**
3647  * @class Roo.bootstrap.NavSidebar
3648  * @extends Roo.bootstrap.Navbar
3649  * Bootstrap Sidebar class
3650  * 
3651  * @constructor
3652  * Create a new Sidebar
3653  * @param {Object} config The config object
3654  */
3655
3656
3657 Roo.bootstrap.NavSidebar = function(config){
3658     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3659 };
3660
3661 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3662     
3663     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3664     
3665     getAutoCreate : function(){
3666         
3667         
3668         return  {
3669             tag: 'div',
3670             cls: 'sidebar sidebar-nav'
3671         };
3672     
3673         
3674     }
3675     
3676     
3677     
3678 });
3679
3680
3681
3682  
3683
3684  /*
3685  * - LGPL
3686  *
3687  * nav group
3688  * 
3689  */
3690
3691 /**
3692  * @class Roo.bootstrap.NavGroup
3693  * @extends Roo.bootstrap.Component
3694  * Bootstrap NavGroup class
3695  * @cfg {String} align (left|right)
3696  * @cfg {Boolean} inverse
3697  * @cfg {String} type (nav|pills|tab) default nav
3698  * @cfg {String} navId - reference Id for navbar.
3699
3700  * 
3701  * @constructor
3702  * Create a new nav group
3703  * @param {Object} config The config object
3704  */
3705
3706 Roo.bootstrap.NavGroup = function(config){
3707     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3708     this.navItems = [];
3709    
3710     Roo.bootstrap.NavGroup.register(this);
3711      this.addEvents({
3712         /**
3713              * @event changed
3714              * Fires when the active item changes
3715              * @param {Roo.bootstrap.NavGroup} this
3716              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3717              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3718          */
3719         'changed': true
3720      });
3721     
3722 };
3723
3724 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3725     
3726     align: '',
3727     inverse: false,
3728     form: false,
3729     type: 'nav',
3730     navId : '',
3731     // private
3732     
3733     navItems : false, 
3734     
3735     getAutoCreate : function()
3736     {
3737         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3738         
3739         cfg = {
3740             tag : 'ul',
3741             cls: 'nav' 
3742         }
3743         
3744         if (['tabs','pills'].indexOf(this.type)!==-1) {
3745             cfg.cls += ' nav-' + this.type
3746         } else {
3747             if (this.type!=='nav') {
3748                 Roo.log('nav type must be nav/tabs/pills')
3749             }
3750             cfg.cls += ' navbar-nav'
3751         }
3752         
3753         if (this.parent().sidebar) {
3754             cfg = {
3755                 tag: 'ul',
3756                 cls: 'dashboard-menu sidebar-menu'
3757             }
3758             
3759             return cfg;
3760         }
3761         
3762         if (this.form === true) {
3763             cfg = {
3764                 tag: 'form',
3765                 cls: 'navbar-form'
3766             }
3767             
3768             if (this.align === 'right') {
3769                 cfg.cls += ' navbar-right';
3770             } else {
3771                 cfg.cls += ' navbar-left';
3772             }
3773         }
3774         
3775         if (this.align === 'right') {
3776             cfg.cls += ' navbar-right';
3777         }
3778         
3779         if (this.inverse) {
3780             cfg.cls += ' navbar-inverse';
3781             
3782         }
3783         
3784         
3785         return cfg;
3786     },
3787     /**
3788     * sets the active Navigation item
3789     * @param {Roo.bootstrap.NavItem} the new current navitem
3790     */
3791     setActiveItem : function(item)
3792     {
3793         var prev = false;
3794         Roo.each(this.navItems, function(v){
3795             if (v == item) {
3796                 return ;
3797             }
3798             if (v.isActive()) {
3799                 v.setActive(false, true);
3800                 prev = v;
3801                 
3802             }
3803             
3804         });
3805
3806         item.setActive(true, true);
3807         this.fireEvent('changed', this, item, prev);
3808         
3809         
3810     },
3811     /**
3812     * gets the active Navigation item
3813     * @return {Roo.bootstrap.NavItem} the current navitem
3814     */
3815     getActive : function()
3816     {
3817         
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             
3821             if (v.isActive()) {
3822                 prev = v;
3823                 
3824             }
3825             
3826         });
3827         return prev;
3828     },
3829     
3830     indexOfNav : function()
3831     {
3832         
3833         var prev = false;
3834         Roo.each(this.navItems, function(v,i){
3835             
3836             if (v.isActive()) {
3837                 prev = i;
3838                 
3839             }
3840             
3841         });
3842         return prev;
3843     },
3844     /**
3845     * adds a Navigation item
3846     * @param {Roo.bootstrap.NavItem} the navitem to add
3847     */
3848     addItem : function(cfg)
3849     {
3850         var cn = new Roo.bootstrap.NavItem(cfg);
3851         this.register(cn);
3852         cn.parentId = this.id;
3853         cn.onRender(this.el, null);
3854         return cn;
3855     },
3856     /**
3857     * register a Navigation item
3858     * @param {Roo.bootstrap.NavItem} the navitem to add
3859     */
3860     register : function(item)
3861     {
3862         this.navItems.push( item);
3863         item.navId = this.navId;
3864     
3865     },
3866     
3867     /**
3868     * clear all the Navigation item
3869     */
3870    
3871     clearAll : function()
3872     {
3873         this.navItems = [];
3874         this.el.dom.innerHTML = '';
3875     },
3876     
3877     getNavItem: function(tabId)
3878     {
3879         var ret = false;
3880         Roo.each(this.navItems, function(e) {
3881             if (e.tabId == tabId) {
3882                ret =  e;
3883                return false;
3884             }
3885             return true;
3886             
3887         });
3888         return ret;
3889     },
3890     
3891     setActiveNext : function()
3892     {
3893         var i = this.indexOfNav(this.getActive());
3894         if (i > this.navItems.length) {
3895             return;
3896         }
3897         this.setActiveItem(this.navItems[i+1]);
3898     },
3899     setActivePrev : function()
3900     {
3901         var i = this.indexOfNav(this.getActive());
3902         if (i  < 1) {
3903             return;
3904         }
3905         this.setActiveItem(this.navItems[i-1]);
3906     },
3907     clearWasActive : function(except) {
3908         Roo.each(this.navItems, function(e) {
3909             if (e.tabId != except.tabId && e.was_active) {
3910                e.was_active = false;
3911                return false;
3912             }
3913             return true;
3914             
3915         });
3916     },
3917     getWasActive : function ()
3918     {
3919         var r = false;
3920         Roo.each(this.navItems, function(e) {
3921             if (e.was_active) {
3922                r = e;
3923                return false;
3924             }
3925             return true;
3926             
3927         });
3928         return r;
3929     }
3930     
3931     
3932 });
3933
3934  
3935 Roo.apply(Roo.bootstrap.NavGroup, {
3936     
3937     groups: {},
3938      /**
3939     * register a Navigation Group
3940     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3941     */
3942     register : function(navgrp)
3943     {
3944         this.groups[navgrp.navId] = navgrp;
3945         
3946     },
3947     /**
3948     * fetch a Navigation Group based on the navigation ID
3949     * @param {string} the navgroup to add
3950     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3951     */
3952     get: function(navId) {
3953         if (typeof(this.groups[navId]) == 'undefined') {
3954             return false;
3955             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3956         }
3957         return this.groups[navId] ;
3958     }
3959     
3960     
3961     
3962 });
3963
3964  /*
3965  * - LGPL
3966  *
3967  * row
3968  * 
3969  */
3970
3971 /**
3972  * @class Roo.bootstrap.NavItem
3973  * @extends Roo.bootstrap.Component
3974  * Bootstrap Navbar.NavItem class
3975  * @cfg {String} href  link to
3976  * @cfg {String} html content of button
3977  * @cfg {String} badge text inside badge
3978  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3979  * @cfg {String} glyphicon name of glyphicon
3980  * @cfg {String} icon name of font awesome icon
3981  * @cfg {Boolean} active Is item active
3982  * @cfg {Boolean} disabled Is item disabled
3983  
3984  * @cfg {Boolean} preventDefault (true | false) default false
3985  * @cfg {String} tabId the tab that this item activates.
3986  * @cfg {String} tagtype (a|span) render as a href or span?
3987  * @cfg {Boolean} animateRef (true|false) link to element default false  
3988   
3989  * @constructor
3990  * Create a new Navbar Item
3991  * @param {Object} config The config object
3992  */
3993 Roo.bootstrap.NavItem = function(config){
3994     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3995     this.addEvents({
3996         // raw events
3997         /**
3998          * @event click
3999          * The raw click event for the entire grid.
4000          * @param {Roo.EventObject} e
4001          */
4002         "click" : true,
4003          /**
4004             * @event changed
4005             * Fires when the active item active state changes
4006             * @param {Roo.bootstrap.NavItem} this
4007             * @param {boolean} state the new state
4008              
4009          */
4010         'changed': true,
4011         /**
4012             * @event scrollto
4013             * Fires when scroll to element
4014             * @param {Roo.bootstrap.NavItem} this
4015             * @param {Object} options
4016             * @param {Roo.EventObject} e
4017              
4018          */
4019         'scrollto': true
4020     });
4021    
4022 };
4023
4024 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4025     
4026     href: false,
4027     html: '',
4028     badge: '',
4029     icon: false,
4030     glyphicon: false,
4031     active: false,
4032     preventDefault : false,
4033     tabId : false,
4034     tagtype : 'a',
4035     disabled : false,
4036     animateRef : false,
4037     was_active : false,
4038     
4039     getAutoCreate : function(){
4040          
4041         var cfg = {
4042             tag: 'li',
4043             cls: 'nav-item'
4044             
4045         }
4046         if (this.active) {
4047             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4048         }
4049         if (this.disabled) {
4050             cfg.cls += ' disabled';
4051         }
4052         
4053         if (this.href || this.html || this.glyphicon || this.icon) {
4054             cfg.cn = [
4055                 {
4056                     tag: this.tagtype,
4057                     href : this.href || "#",
4058                     html: this.html || ''
4059                 }
4060             ];
4061             
4062             if (this.icon) {
4063                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4064             }
4065
4066             if(this.glyphicon) {
4067                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4068             }
4069             
4070             if (this.menu) {
4071                 
4072                 cfg.cn[0].html += " <span class='caret'></span>";
4073              
4074             }
4075             
4076             if (this.badge !== '') {
4077                  
4078                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4079             }
4080         }
4081         
4082         
4083         
4084         return cfg;
4085     },
4086     initEvents: function() 
4087     {
4088         if (typeof (this.menu) != 'undefined') {
4089             this.menu.parentType = this.xtype;
4090             this.menu.triggerEl = this.el;
4091             this.menu = this.addxtype(Roo.apply({}, this.menu));
4092         }
4093         
4094         this.el.select('a',true).on('click', this.onClick, this);
4095         
4096         if(this.tagtype == 'span'){
4097             this.el.select('span',true).on('click', this.onClick, this);
4098         }
4099        
4100         // at this point parent should be available..
4101         this.parent().register(this);
4102     },
4103     
4104     onClick : function(e)
4105     {
4106         if(
4107                 this.preventDefault || 
4108                 this.href == '#' 
4109         ){
4110             
4111             e.preventDefault();
4112         }
4113         
4114         if (this.disabled) {
4115             return;
4116         }
4117         
4118         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4119         if (tg && tg.transition) {
4120             Roo.log("waiting for the transitionend");
4121             return;
4122         }
4123         
4124         
4125         
4126         //Roo.log("fire event clicked");
4127         if(this.fireEvent('click', this, e) === false){
4128             return;
4129         };
4130         
4131         if(this.tagtype == 'span'){
4132             return;
4133         }
4134         
4135         //Roo.log(this.href);
4136         var ael = this.el.select('a',true).first();
4137         //Roo.log(ael);
4138         
4139         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4140             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4141             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4142                 return; // ignore... - it's a 'hash' to another page.
4143             }
4144             
4145             e.preventDefault();
4146             this.scrollToElement(e);
4147         }
4148         
4149         
4150         var p =  this.parent();
4151    
4152         if (['tabs','pills'].indexOf(p.type)!==-1) {
4153             if (typeof(p.setActiveItem) !== 'undefined') {
4154                 p.setActiveItem(this);
4155             }
4156         }
4157         
4158         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4159         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4160             // remove the collapsed menu expand...
4161             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4162         }
4163     },
4164     
4165     isActive: function () {
4166         return this.active
4167     },
4168     setActive : function(state, fire, is_was_active)
4169     {
4170         if (this.active && !state & this.navId) {
4171             this.was_active = true;
4172             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4173             if (nv) {
4174                 nv.clearWasActive(this);
4175             }
4176             
4177         }
4178         this.active = state;
4179         
4180         if (!state ) {
4181             this.el.removeClass('active');
4182         } else if (!this.el.hasClass('active')) {
4183             this.el.addClass('active');
4184         }
4185         if (fire) {
4186             this.fireEvent('changed', this, state);
4187         }
4188         
4189         // show a panel if it's registered and related..
4190         
4191         if (!this.navId || !this.tabId || !state || is_was_active) {
4192             return;
4193         }
4194         
4195         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4196         if (!tg) {
4197             return;
4198         }
4199         var pan = tg.getPanelByName(this.tabId);
4200         if (!pan) {
4201             return;
4202         }
4203         // if we can not flip to new panel - go back to old nav highlight..
4204         if (false == tg.showPanel(pan)) {
4205             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4206             if (nv) {
4207                 var onav = nv.getWasActive();
4208                 if (onav) {
4209                     onav.setActive(true, false, true);
4210                 }
4211             }
4212             
4213         }
4214         
4215         
4216         
4217     },
4218      // this should not be here...
4219     setDisabled : function(state)
4220     {
4221         this.disabled = state;
4222         if (!state ) {
4223             this.el.removeClass('disabled');
4224         } else if (!this.el.hasClass('disabled')) {
4225             this.el.addClass('disabled');
4226         }
4227         
4228     },
4229     
4230     /**
4231      * Fetch the element to display the tooltip on.
4232      * @return {Roo.Element} defaults to this.el
4233      */
4234     tooltipEl : function()
4235     {
4236         return this.el.select('' + this.tagtype + '', true).first();
4237     },
4238     
4239     scrollToElement : function(e)
4240     {
4241         var c = document.body;
4242         
4243         /*
4244          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4245          */
4246         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4247             c = document.documentElement;
4248         }
4249         
4250         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4251         
4252         if(!target){
4253             return;
4254         }
4255
4256         var o = target.calcOffsetsTo(c);
4257         
4258         var options = {
4259             target : target,
4260             value : o[1]
4261         }
4262         
4263         this.fireEvent('scrollto', this, options, e);
4264         
4265         Roo.get(c).scrollTo('top', options.value, true);
4266         
4267         return;
4268     }
4269 });
4270  
4271
4272  /*
4273  * - LGPL
4274  *
4275  * sidebar item
4276  *
4277  *  li
4278  *    <span> icon </span>
4279  *    <span> text </span>
4280  *    <span>badge </span>
4281  */
4282
4283 /**
4284  * @class Roo.bootstrap.NavSidebarItem
4285  * @extends Roo.bootstrap.NavItem
4286  * Bootstrap Navbar.NavSidebarItem class
4287  * @constructor
4288  * Create a new Navbar Button
4289  * @param {Object} config The config object
4290  */
4291 Roo.bootstrap.NavSidebarItem = function(config){
4292     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4293     this.addEvents({
4294         // raw events
4295         /**
4296          * @event click
4297          * The raw click event for the entire grid.
4298          * @param {Roo.EventObject} e
4299          */
4300         "click" : true,
4301          /**
4302             * @event changed
4303             * Fires when the active item active state changes
4304             * @param {Roo.bootstrap.NavSidebarItem} this
4305             * @param {boolean} state the new state
4306              
4307          */
4308         'changed': true
4309     });
4310    
4311 };
4312
4313 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4314     
4315     
4316     getAutoCreate : function(){
4317         
4318         
4319         var a = {
4320                 tag: 'a',
4321                 href : this.href || '#',
4322                 cls: '',
4323                 html : '',
4324                 cn : []
4325         };
4326         var cfg = {
4327             tag: 'li',
4328             cls: '',
4329             cn: [ a ]
4330         }
4331         var span = {
4332             tag: 'span',
4333             html : this.html || ''
4334         }
4335         
4336         
4337         if (this.active) {
4338             cfg.cls += ' active';
4339         }
4340         
4341         // left icon..
4342         if (this.glyphicon || this.icon) {
4343             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4344             a.cn.push({ tag : 'i', cls : c }) ;
4345         }
4346         // html..
4347         a.cn.push(span);
4348         // then badge..
4349         if (this.badge !== '') {
4350             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4351         }
4352         // fi
4353         if (this.menu) {
4354             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4355             a.cls += 'dropdown-toggle treeview' ;
4356             
4357         }
4358         
4359         
4360         
4361         return cfg;
4362          
4363            
4364     }
4365    
4366      
4367  
4368 });
4369  
4370
4371  /*
4372  * - LGPL
4373  *
4374  * row
4375  * 
4376  */
4377
4378 /**
4379  * @class Roo.bootstrap.Row
4380  * @extends Roo.bootstrap.Component
4381  * Bootstrap Row class (contains columns...)
4382  * 
4383  * @constructor
4384  * Create a new Row
4385  * @param {Object} config The config object
4386  */
4387
4388 Roo.bootstrap.Row = function(config){
4389     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4390 };
4391
4392 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4393     
4394     getAutoCreate : function(){
4395        return {
4396             cls: 'row clearfix'
4397        };
4398     }
4399     
4400     
4401 });
4402
4403  
4404
4405  /*
4406  * - LGPL
4407  *
4408  * element
4409  * 
4410  */
4411
4412 /**
4413  * @class Roo.bootstrap.Element
4414  * @extends Roo.bootstrap.Component
4415  * Bootstrap Element class
4416  * @cfg {String} html contents of the element
4417  * @cfg {String} tag tag of the element
4418  * @cfg {String} cls class of the element
4419  * @cfg {Boolean} preventDefault (true|false) default false
4420  * @cfg {Boolean} clickable (true|false) default false
4421  * 
4422  * @constructor
4423  * Create a new Element
4424  * @param {Object} config The config object
4425  */
4426
4427 Roo.bootstrap.Element = function(config){
4428     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4429     
4430     this.addEvents({
4431         // raw events
4432         /**
4433          * @event click
4434          * When a element is chick
4435          * @param {Roo.bootstrap.Element} this
4436          * @param {Roo.EventObject} e
4437          */
4438         "click" : true
4439     });
4440 };
4441
4442 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4443     
4444     tag: 'div',
4445     cls: '',
4446     html: '',
4447     preventDefault: false, 
4448     clickable: false,
4449     
4450     getAutoCreate : function(){
4451         
4452         var cfg = {
4453             tag: this.tag,
4454             cls: this.cls,
4455             html: this.html
4456         }
4457         
4458         return cfg;
4459     },
4460     
4461     initEvents: function() 
4462     {
4463         Roo.bootstrap.Element.superclass.initEvents.call(this);
4464         
4465         if(this.clickable){
4466             this.el.on('click', this.onClick, this);
4467         }
4468         
4469     },
4470     
4471     onClick : function(e)
4472     {
4473         if(this.preventDefault){
4474             e.preventDefault();
4475         }
4476         
4477         this.fireEvent('click', this, e);
4478     },
4479     
4480     getValue : function()
4481     {
4482         return this.el.dom.innerHTML;
4483     },
4484     
4485     setValue : function(value)
4486     {
4487         this.el.dom.innerHTML = value;
4488     }
4489    
4490 });
4491
4492  
4493
4494  /*
4495  * - LGPL
4496  *
4497  * pagination
4498  * 
4499  */
4500
4501 /**
4502  * @class Roo.bootstrap.Pagination
4503  * @extends Roo.bootstrap.Component
4504  * Bootstrap Pagination class
4505  * @cfg {String} size xs | sm | md | lg
4506  * @cfg {Boolean} inverse false | true
4507  * 
4508  * @constructor
4509  * Create a new Pagination
4510  * @param {Object} config The config object
4511  */
4512
4513 Roo.bootstrap.Pagination = function(config){
4514     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4515 };
4516
4517 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4518     
4519     cls: false,
4520     size: false,
4521     inverse: false,
4522     
4523     getAutoCreate : function(){
4524         var cfg = {
4525             tag: 'ul',
4526                 cls: 'pagination'
4527         };
4528         if (this.inverse) {
4529             cfg.cls += ' inverse';
4530         }
4531         if (this.html) {
4532             cfg.html=this.html;
4533         }
4534         if (this.cls) {
4535             cfg.cls += " " + this.cls;
4536         }
4537         return cfg;
4538     }
4539    
4540 });
4541
4542  
4543
4544  /*
4545  * - LGPL
4546  *
4547  * Pagination item
4548  * 
4549  */
4550
4551
4552 /**
4553  * @class Roo.bootstrap.PaginationItem
4554  * @extends Roo.bootstrap.Component
4555  * Bootstrap PaginationItem class
4556  * @cfg {String} html text
4557  * @cfg {String} href the link
4558  * @cfg {Boolean} preventDefault (true | false) default true
4559  * @cfg {Boolean} active (true | false) default false
4560  * @cfg {Boolean} disabled default false
4561  * 
4562  * 
4563  * @constructor
4564  * Create a new PaginationItem
4565  * @param {Object} config The config object
4566  */
4567
4568
4569 Roo.bootstrap.PaginationItem = function(config){
4570     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4571     this.addEvents({
4572         // raw events
4573         /**
4574          * @event click
4575          * The raw click event for the entire grid.
4576          * @param {Roo.EventObject} e
4577          */
4578         "click" : true
4579     });
4580 };
4581
4582 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4583     
4584     href : false,
4585     html : false,
4586     preventDefault: true,
4587     active : false,
4588     cls : false,
4589     disabled: false,
4590     
4591     getAutoCreate : function(){
4592         var cfg= {
4593             tag: 'li',
4594             cn: [
4595                 {
4596                     tag : 'a',
4597                     href : this.href ? this.href : '#',
4598                     html : this.html ? this.html : ''
4599                 }
4600             ]
4601         };
4602         
4603         if(this.cls){
4604             cfg.cls = this.cls;
4605         }
4606         
4607         if(this.disabled){
4608             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4609         }
4610         
4611         if(this.active){
4612             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4613         }
4614         
4615         return cfg;
4616     },
4617     
4618     initEvents: function() {
4619         
4620         this.el.on('click', this.onClick, this);
4621         
4622     },
4623     onClick : function(e)
4624     {
4625         Roo.log('PaginationItem on click ');
4626         if(this.preventDefault){
4627             e.preventDefault();
4628         }
4629         
4630         if(this.disabled){
4631             return;
4632         }
4633         
4634         this.fireEvent('click', this, e);
4635     }
4636    
4637 });
4638
4639  
4640
4641  /*
4642  * - LGPL
4643  *
4644  * slider
4645  * 
4646  */
4647
4648
4649 /**
4650  * @class Roo.bootstrap.Slider
4651  * @extends Roo.bootstrap.Component
4652  * Bootstrap Slider class
4653  *    
4654  * @constructor
4655  * Create a new Slider
4656  * @param {Object} config The config object
4657  */
4658
4659 Roo.bootstrap.Slider = function(config){
4660     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4661 };
4662
4663 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4664     
4665     getAutoCreate : function(){
4666         
4667         var cfg = {
4668             tag: 'div',
4669             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4670             cn: [
4671                 {
4672                     tag: 'a',
4673                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4674                 }
4675             ]
4676         }
4677         
4678         return cfg;
4679     }
4680    
4681 });
4682
4683  /*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693  
4694
4695 /**
4696  * @class Roo.grid.ColumnModel
4697  * @extends Roo.util.Observable
4698  * This is the default implementation of a ColumnModel used by the Grid. It defines
4699  * the columns in the grid.
4700  * <br>Usage:<br>
4701  <pre><code>
4702  var colModel = new Roo.grid.ColumnModel([
4703         {header: "Ticker", width: 60, sortable: true, locked: true},
4704         {header: "Company Name", width: 150, sortable: true},
4705         {header: "Market Cap.", width: 100, sortable: true},
4706         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4707         {header: "Employees", width: 100, sortable: true, resizable: false}
4708  ]);
4709  </code></pre>
4710  * <p>
4711  
4712  * The config options listed for this class are options which may appear in each
4713  * individual column definition.
4714  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4715  * @constructor
4716  * @param {Object} config An Array of column config objects. See this class's
4717  * config objects for details.
4718 */
4719 Roo.grid.ColumnModel = function(config){
4720         /**
4721      * The config passed into the constructor
4722      */
4723     this.config = config;
4724     this.lookup = {};
4725
4726     // if no id, create one
4727     // if the column does not have a dataIndex mapping,
4728     // map it to the order it is in the config
4729     for(var i = 0, len = config.length; i < len; i++){
4730         var c = config[i];
4731         if(typeof c.dataIndex == "undefined"){
4732             c.dataIndex = i;
4733         }
4734         if(typeof c.renderer == "string"){
4735             c.renderer = Roo.util.Format[c.renderer];
4736         }
4737         if(typeof c.id == "undefined"){
4738             c.id = Roo.id();
4739         }
4740         if(c.editor && c.editor.xtype){
4741             c.editor  = Roo.factory(c.editor, Roo.grid);
4742         }
4743         if(c.editor && c.editor.isFormField){
4744             c.editor = new Roo.grid.GridEditor(c.editor);
4745         }
4746         this.lookup[c.id] = c;
4747     }
4748
4749     /**
4750      * The width of columns which have no width specified (defaults to 100)
4751      * @type Number
4752      */
4753     this.defaultWidth = 100;
4754
4755     /**
4756      * Default sortable of columns which have no sortable specified (defaults to false)
4757      * @type Boolean
4758      */
4759     this.defaultSortable = false;
4760
4761     this.addEvents({
4762         /**
4763              * @event widthchange
4764              * Fires when the width of a column changes.
4765              * @param {ColumnModel} this
4766              * @param {Number} columnIndex The column index
4767              * @param {Number} newWidth The new width
4768              */
4769             "widthchange": true,
4770         /**
4771              * @event headerchange
4772              * Fires when the text of a header changes.
4773              * @param {ColumnModel} this
4774              * @param {Number} columnIndex The column index
4775              * @param {Number} newText The new header text
4776              */
4777             "headerchange": true,
4778         /**
4779              * @event hiddenchange
4780              * Fires when a column is hidden or "unhidden".
4781              * @param {ColumnModel} this
4782              * @param {Number} columnIndex The column index
4783              * @param {Boolean} hidden true if hidden, false otherwise
4784              */
4785             "hiddenchange": true,
4786             /**
4787          * @event columnmoved
4788          * Fires when a column is moved.
4789          * @param {ColumnModel} this
4790          * @param {Number} oldIndex
4791          * @param {Number} newIndex
4792          */
4793         "columnmoved" : true,
4794         /**
4795          * @event columlockchange
4796          * Fires when a column's locked state is changed
4797          * @param {ColumnModel} this
4798          * @param {Number} colIndex
4799          * @param {Boolean} locked true if locked
4800          */
4801         "columnlockchange" : true
4802     });
4803     Roo.grid.ColumnModel.superclass.constructor.call(this);
4804 };
4805 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4806     /**
4807      * @cfg {String} header The header text to display in the Grid view.
4808      */
4809     /**
4810      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4811      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4812      * specified, the column's index is used as an index into the Record's data Array.
4813      */
4814     /**
4815      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4816      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4817      */
4818     /**
4819      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4820      * Defaults to the value of the {@link #defaultSortable} property.
4821      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4822      */
4823     /**
4824      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4825      */
4826     /**
4827      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4828      */
4829     /**
4830      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4831      */
4832     /**
4833      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4834      */
4835     /**
4836      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4837      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4838      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4839      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4840      */
4841        /**
4842      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4843      */
4844     /**
4845      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4846      */
4847     /**
4848      * @cfg {String} cursor (Optional)
4849      */
4850     /**
4851      * @cfg {String} tooltip (Optional)
4852      */
4853     /**
4854      * Returns the id of the column at the specified index.
4855      * @param {Number} index The column index
4856      * @return {String} the id
4857      */
4858     getColumnId : function(index){
4859         return this.config[index].id;
4860     },
4861
4862     /**
4863      * Returns the column for a specified id.
4864      * @param {String} id The column id
4865      * @return {Object} the column
4866      */
4867     getColumnById : function(id){
4868         return this.lookup[id];
4869     },
4870
4871     
4872     /**
4873      * Returns the column for a specified dataIndex.
4874      * @param {String} dataIndex The column dataIndex
4875      * @return {Object|Boolean} the column or false if not found
4876      */
4877     getColumnByDataIndex: function(dataIndex){
4878         var index = this.findColumnIndex(dataIndex);
4879         return index > -1 ? this.config[index] : false;
4880     },
4881     
4882     /**
4883      * Returns the index for a specified column id.
4884      * @param {String} id The column id
4885      * @return {Number} the index, or -1 if not found
4886      */
4887     getIndexById : function(id){
4888         for(var i = 0, len = this.config.length; i < len; i++){
4889             if(this.config[i].id == id){
4890                 return i;
4891             }
4892         }
4893         return -1;
4894     },
4895     
4896     /**
4897      * Returns the index for a specified column dataIndex.
4898      * @param {String} dataIndex The column dataIndex
4899      * @return {Number} the index, or -1 if not found
4900      */
4901     
4902     findColumnIndex : function(dataIndex){
4903         for(var i = 0, len = this.config.length; i < len; i++){
4904             if(this.config[i].dataIndex == dataIndex){
4905                 return i;
4906             }
4907         }
4908         return -1;
4909     },
4910     
4911     
4912     moveColumn : function(oldIndex, newIndex){
4913         var c = this.config[oldIndex];
4914         this.config.splice(oldIndex, 1);
4915         this.config.splice(newIndex, 0, c);
4916         this.dataMap = null;
4917         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4918     },
4919
4920     isLocked : function(colIndex){
4921         return this.config[colIndex].locked === true;
4922     },
4923
4924     setLocked : function(colIndex, value, suppressEvent){
4925         if(this.isLocked(colIndex) == value){
4926             return;
4927         }
4928         this.config[colIndex].locked = value;
4929         if(!suppressEvent){
4930             this.fireEvent("columnlockchange", this, colIndex, value);
4931         }
4932     },
4933
4934     getTotalLockedWidth : function(){
4935         var totalWidth = 0;
4936         for(var i = 0; i < this.config.length; i++){
4937             if(this.isLocked(i) && !this.isHidden(i)){
4938                 this.totalWidth += this.getColumnWidth(i);
4939             }
4940         }
4941         return totalWidth;
4942     },
4943
4944     getLockedCount : function(){
4945         for(var i = 0, len = this.config.length; i < len; i++){
4946             if(!this.isLocked(i)){
4947                 return i;
4948             }
4949         }
4950     },
4951
4952     /**
4953      * Returns the number of columns.
4954      * @return {Number}
4955      */
4956     getColumnCount : function(visibleOnly){
4957         if(visibleOnly === true){
4958             var c = 0;
4959             for(var i = 0, len = this.config.length; i < len; i++){
4960                 if(!this.isHidden(i)){
4961                     c++;
4962                 }
4963             }
4964             return c;
4965         }
4966         return this.config.length;
4967     },
4968
4969     /**
4970      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4971      * @param {Function} fn
4972      * @param {Object} scope (optional)
4973      * @return {Array} result
4974      */
4975     getColumnsBy : function(fn, scope){
4976         var r = [];
4977         for(var i = 0, len = this.config.length; i < len; i++){
4978             var c = this.config[i];
4979             if(fn.call(scope||this, c, i) === true){
4980                 r[r.length] = c;
4981             }
4982         }
4983         return r;
4984     },
4985
4986     /**
4987      * Returns true if the specified column is sortable.
4988      * @param {Number} col The column index
4989      * @return {Boolean}
4990      */
4991     isSortable : function(col){
4992         if(typeof this.config[col].sortable == "undefined"){
4993             return this.defaultSortable;
4994         }
4995         return this.config[col].sortable;
4996     },
4997
4998     /**
4999      * Returns the rendering (formatting) function defined for the column.
5000      * @param {Number} col The column index.
5001      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5002      */
5003     getRenderer : function(col){
5004         if(!this.config[col].renderer){
5005             return Roo.grid.ColumnModel.defaultRenderer;
5006         }
5007         return this.config[col].renderer;
5008     },
5009
5010     /**
5011      * Sets the rendering (formatting) function for a column.
5012      * @param {Number} col The column index
5013      * @param {Function} fn The function to use to process the cell's raw data
5014      * to return HTML markup for the grid view. The render function is called with
5015      * the following parameters:<ul>
5016      * <li>Data value.</li>
5017      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5018      * <li>css A CSS style string to apply to the table cell.</li>
5019      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5020      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5021      * <li>Row index</li>
5022      * <li>Column index</li>
5023      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5024      */
5025     setRenderer : function(col, fn){
5026         this.config[col].renderer = fn;
5027     },
5028
5029     /**
5030      * Returns the width for the specified column.
5031      * @param {Number} col The column index
5032      * @return {Number}
5033      */
5034     getColumnWidth : function(col){
5035         return this.config[col].width * 1 || this.defaultWidth;
5036     },
5037
5038     /**
5039      * Sets the width for a column.
5040      * @param {Number} col The column index
5041      * @param {Number} width The new width
5042      */
5043     setColumnWidth : function(col, width, suppressEvent){
5044         this.config[col].width = width;
5045         this.totalWidth = null;
5046         if(!suppressEvent){
5047              this.fireEvent("widthchange", this, col, width);
5048         }
5049     },
5050
5051     /**
5052      * Returns the total width of all columns.
5053      * @param {Boolean} includeHidden True to include hidden column widths
5054      * @return {Number}
5055      */
5056     getTotalWidth : function(includeHidden){
5057         if(!this.totalWidth){
5058             this.totalWidth = 0;
5059             for(var i = 0, len = this.config.length; i < len; i++){
5060                 if(includeHidden || !this.isHidden(i)){
5061                     this.totalWidth += this.getColumnWidth(i);
5062                 }
5063             }
5064         }
5065         return this.totalWidth;
5066     },
5067
5068     /**
5069      * Returns the header for the specified column.
5070      * @param {Number} col The column index
5071      * @return {String}
5072      */
5073     getColumnHeader : function(col){
5074         return this.config[col].header;
5075     },
5076
5077     /**
5078      * Sets the header for a column.
5079      * @param {Number} col The column index
5080      * @param {String} header The new header
5081      */
5082     setColumnHeader : function(col, header){
5083         this.config[col].header = header;
5084         this.fireEvent("headerchange", this, col, header);
5085     },
5086
5087     /**
5088      * Returns the tooltip for the specified column.
5089      * @param {Number} col The column index
5090      * @return {String}
5091      */
5092     getColumnTooltip : function(col){
5093             return this.config[col].tooltip;
5094     },
5095     /**
5096      * Sets the tooltip for a column.
5097      * @param {Number} col The column index
5098      * @param {String} tooltip The new tooltip
5099      */
5100     setColumnTooltip : function(col, tooltip){
5101             this.config[col].tooltip = tooltip;
5102     },
5103
5104     /**
5105      * Returns the dataIndex for the specified column.
5106      * @param {Number} col The column index
5107      * @return {Number}
5108      */
5109     getDataIndex : function(col){
5110         return this.config[col].dataIndex;
5111     },
5112
5113     /**
5114      * Sets the dataIndex for a column.
5115      * @param {Number} col The column index
5116      * @param {Number} dataIndex The new dataIndex
5117      */
5118     setDataIndex : function(col, dataIndex){
5119         this.config[col].dataIndex = dataIndex;
5120     },
5121
5122     
5123     
5124     /**
5125      * Returns true if the cell is editable.
5126      * @param {Number} colIndex The column index
5127      * @param {Number} rowIndex The row index
5128      * @return {Boolean}
5129      */
5130     isCellEditable : function(colIndex, rowIndex){
5131         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5132     },
5133
5134     /**
5135      * Returns the editor defined for the cell/column.
5136      * return false or null to disable editing.
5137      * @param {Number} colIndex The column index
5138      * @param {Number} rowIndex The row index
5139      * @return {Object}
5140      */
5141     getCellEditor : function(colIndex, rowIndex){
5142         return this.config[colIndex].editor;
5143     },
5144
5145     /**
5146      * Sets if a column is editable.
5147      * @param {Number} col The column index
5148      * @param {Boolean} editable True if the column is editable
5149      */
5150     setEditable : function(col, editable){
5151         this.config[col].editable = editable;
5152     },
5153
5154
5155     /**
5156      * Returns true if the column is hidden.
5157      * @param {Number} colIndex The column index
5158      * @return {Boolean}
5159      */
5160     isHidden : function(colIndex){
5161         return this.config[colIndex].hidden;
5162     },
5163
5164
5165     /**
5166      * Returns true if the column width cannot be changed
5167      */
5168     isFixed : function(colIndex){
5169         return this.config[colIndex].fixed;
5170     },
5171
5172     /**
5173      * Returns true if the column can be resized
5174      * @return {Boolean}
5175      */
5176     isResizable : function(colIndex){
5177         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5178     },
5179     /**
5180      * Sets if a column is hidden.
5181      * @param {Number} colIndex The column index
5182      * @param {Boolean} hidden True if the column is hidden
5183      */
5184     setHidden : function(colIndex, hidden){
5185         this.config[colIndex].hidden = hidden;
5186         this.totalWidth = null;
5187         this.fireEvent("hiddenchange", this, colIndex, hidden);
5188     },
5189
5190     /**
5191      * Sets the editor for a column.
5192      * @param {Number} col The column index
5193      * @param {Object} editor The editor object
5194      */
5195     setEditor : function(col, editor){
5196         this.config[col].editor = editor;
5197     }
5198 });
5199
5200 Roo.grid.ColumnModel.defaultRenderer = function(value){
5201         if(typeof value == "string" && value.length < 1){
5202             return "&#160;";
5203         }
5204         return value;
5205 };
5206
5207 // Alias for backwards compatibility
5208 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5209 /*
5210  * Based on:
5211  * Ext JS Library 1.1.1
5212  * Copyright(c) 2006-2007, Ext JS, LLC.
5213  *
5214  * Originally Released Under LGPL - original licence link has changed is not relivant.
5215  *
5216  * Fork - LGPL
5217  * <script type="text/javascript">
5218  */
5219  
5220 /**
5221  * @class Roo.LoadMask
5222  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5223  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5224  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5225  * element's UpdateManager load indicator and will be destroyed after the initial load.
5226  * @constructor
5227  * Create a new LoadMask
5228  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5229  * @param {Object} config The config object
5230  */
5231 Roo.LoadMask = function(el, config){
5232     this.el = Roo.get(el);
5233     Roo.apply(this, config);
5234     if(this.store){
5235         this.store.on('beforeload', this.onBeforeLoad, this);
5236         this.store.on('load', this.onLoad, this);
5237         this.store.on('loadexception', this.onLoadException, this);
5238         this.removeMask = false;
5239     }else{
5240         var um = this.el.getUpdateManager();
5241         um.showLoadIndicator = false; // disable the default indicator
5242         um.on('beforeupdate', this.onBeforeLoad, this);
5243         um.on('update', this.onLoad, this);
5244         um.on('failure', this.onLoad, this);
5245         this.removeMask = true;
5246     }
5247 };
5248
5249 Roo.LoadMask.prototype = {
5250     /**
5251      * @cfg {Boolean} removeMask
5252      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5253      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5254      */
5255     /**
5256      * @cfg {String} msg
5257      * The text to display in a centered loading message box (defaults to 'Loading...')
5258      */
5259     msg : 'Loading...',
5260     /**
5261      * @cfg {String} msgCls
5262      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5263      */
5264     msgCls : 'x-mask-loading',
5265
5266     /**
5267      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5268      * @type Boolean
5269      */
5270     disabled: false,
5271
5272     /**
5273      * Disables the mask to prevent it from being displayed
5274      */
5275     disable : function(){
5276        this.disabled = true;
5277     },
5278
5279     /**
5280      * Enables the mask so that it can be displayed
5281      */
5282     enable : function(){
5283         this.disabled = false;
5284     },
5285     
5286     onLoadException : function()
5287     {
5288         Roo.log(arguments);
5289         
5290         if (typeof(arguments[3]) != 'undefined') {
5291             Roo.MessageBox.alert("Error loading",arguments[3]);
5292         } 
5293         /*
5294         try {
5295             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5296                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5297             }   
5298         } catch(e) {
5299             
5300         }
5301         */
5302     
5303         
5304         
5305         this.el.unmask(this.removeMask);
5306     },
5307     // private
5308     onLoad : function()
5309     {
5310         this.el.unmask(this.removeMask);
5311     },
5312
5313     // private
5314     onBeforeLoad : function(){
5315         if(!this.disabled){
5316             this.el.mask(this.msg, this.msgCls);
5317         }
5318     },
5319
5320     // private
5321     destroy : function(){
5322         if(this.store){
5323             this.store.un('beforeload', this.onBeforeLoad, this);
5324             this.store.un('load', this.onLoad, this);
5325             this.store.un('loadexception', this.onLoadException, this);
5326         }else{
5327             var um = this.el.getUpdateManager();
5328             um.un('beforeupdate', this.onBeforeLoad, this);
5329             um.un('update', this.onLoad, this);
5330             um.un('failure', this.onLoad, this);
5331         }
5332     }
5333 };/*
5334  * - LGPL
5335  *
5336  * table
5337  * 
5338  */
5339
5340 /**
5341  * @class Roo.bootstrap.Table
5342  * @extends Roo.bootstrap.Component
5343  * Bootstrap Table class
5344  * @cfg {String} cls table class
5345  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5346  * @cfg {String} bgcolor Specifies the background color for a table
5347  * @cfg {Number} border Specifies whether the table cells should have borders or not
5348  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5349  * @cfg {Number} cellspacing Specifies the space between cells
5350  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5351  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5352  * @cfg {String} sortable Specifies that the table should be sortable
5353  * @cfg {String} summary Specifies a summary of the content of a table
5354  * @cfg {Number} width Specifies the width of a table
5355  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5356  * 
5357  * @cfg {boolean} striped Should the rows be alternative striped
5358  * @cfg {boolean} bordered Add borders to the table
5359  * @cfg {boolean} hover Add hover highlighting
5360  * @cfg {boolean} condensed Format condensed
5361  * @cfg {boolean} responsive Format condensed
5362  * @cfg {Boolean} loadMask (true|false) default false
5363  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5364  * @cfg {Boolean} thead (true|false) generate thead, default true
5365  * @cfg {Boolean} RowSelection (true|false) default false
5366  * @cfg {Boolean} CellSelection (true|false) default false
5367  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5368  
5369  * 
5370  * @constructor
5371  * Create a new Table
5372  * @param {Object} config The config object
5373  */
5374
5375 Roo.bootstrap.Table = function(config){
5376     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5377     
5378     if (this.sm) {
5379         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5380         this.sm = this.selModel;
5381         this.sm.xmodule = this.xmodule || false;
5382     }
5383     if (this.cm && typeof(this.cm.config) == 'undefined') {
5384         this.colModel = new Roo.grid.ColumnModel(this.cm);
5385         this.cm = this.colModel;
5386         this.cm.xmodule = this.xmodule || false;
5387     }
5388     if (this.store) {
5389         this.store= Roo.factory(this.store, Roo.data);
5390         this.ds = this.store;
5391         this.ds.xmodule = this.xmodule || false;
5392          
5393     }
5394     if (this.footer && this.store) {
5395         this.footer.dataSource = this.ds;
5396         this.footer = Roo.factory(this.footer);
5397     }
5398     
5399     /** @private */
5400     this.addEvents({
5401         /**
5402          * @event cellclick
5403          * Fires when a cell is clicked
5404          * @param {Roo.bootstrap.Table} this
5405          * @param {Roo.Element} el
5406          * @param {Number} rowIndex
5407          * @param {Number} columnIndex
5408          * @param {Roo.EventObject} e
5409          */
5410         "cellclick" : true,
5411         /**
5412          * @event celldblclick
5413          * Fires when a cell is double clicked
5414          * @param {Roo.bootstrap.Table} this
5415          * @param {Roo.Element} el
5416          * @param {Number} rowIndex
5417          * @param {Number} columnIndex
5418          * @param {Roo.EventObject} e
5419          */
5420         "celldblclick" : true,
5421         /**
5422          * @event rowclick
5423          * Fires when a row is clicked
5424          * @param {Roo.bootstrap.Table} this
5425          * @param {Roo.Element} el
5426          * @param {Number} rowIndex
5427          * @param {Roo.EventObject} e
5428          */
5429         "rowclick" : true,
5430         /**
5431          * @event rowdblclick
5432          * Fires when a row is double clicked
5433          * @param {Roo.bootstrap.Table} this
5434          * @param {Roo.Element} el
5435          * @param {Number} rowIndex
5436          * @param {Roo.EventObject} e
5437          */
5438         "rowdblclick" : true,
5439         /**
5440          * @event mouseover
5441          * Fires when a mouseover occur
5442          * @param {Roo.bootstrap.Table} this
5443          * @param {Roo.Element} el
5444          * @param {Number} rowIndex
5445          * @param {Number} columnIndex
5446          * @param {Roo.EventObject} e
5447          */
5448         "mouseover" : true,
5449         /**
5450          * @event mouseout
5451          * Fires when a mouseout occur
5452          * @param {Roo.bootstrap.Table} this
5453          * @param {Roo.Element} el
5454          * @param {Number} rowIndex
5455          * @param {Number} columnIndex
5456          * @param {Roo.EventObject} e
5457          */
5458         "mouseout" : true,
5459         /**
5460          * @event rowclass
5461          * Fires when a row is rendered, so you can change add a style to it.
5462          * @param {Roo.bootstrap.Table} this
5463          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5464          */
5465         'rowclass' : true,
5466           /**
5467          * @event rowsrendered
5468          * Fires when all the  rows have been rendered
5469          * @param {Roo.bootstrap.Table} this
5470          */
5471         'rowsrendered' : true
5472         
5473     });
5474 };
5475
5476 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5477     
5478     cls: false,
5479     align: false,
5480     bgcolor: false,
5481     border: false,
5482     cellpadding: false,
5483     cellspacing: false,
5484     frame: false,
5485     rules: false,
5486     sortable: false,
5487     summary: false,
5488     width: false,
5489     striped : false,
5490     bordered: false,
5491     hover:  false,
5492     condensed : false,
5493     responsive : false,
5494     sm : false,
5495     cm : false,
5496     store : false,
5497     loadMask : false,
5498     tfoot : true,
5499     thead : true,
5500     RowSelection : false,
5501     CellSelection : false,
5502     layout : false,
5503     
5504     // Roo.Element - the tbody
5505     mainBody: false, 
5506     
5507     getAutoCreate : function(){
5508         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5509         
5510         cfg = {
5511             tag: 'table',
5512             cls : 'table',
5513             cn : []
5514         }
5515             
5516         if (this.striped) {
5517             cfg.cls += ' table-striped';
5518         }
5519         
5520         if (this.hover) {
5521             cfg.cls += ' table-hover';
5522         }
5523         if (this.bordered) {
5524             cfg.cls += ' table-bordered';
5525         }
5526         if (this.condensed) {
5527             cfg.cls += ' table-condensed';
5528         }
5529         if (this.responsive) {
5530             cfg.cls += ' table-responsive';
5531         }
5532         
5533         if (this.cls) {
5534             cfg.cls+=  ' ' +this.cls;
5535         }
5536         
5537         // this lot should be simplifed...
5538         
5539         if (this.align) {
5540             cfg.align=this.align;
5541         }
5542         if (this.bgcolor) {
5543             cfg.bgcolor=this.bgcolor;
5544         }
5545         if (this.border) {
5546             cfg.border=this.border;
5547         }
5548         if (this.cellpadding) {
5549             cfg.cellpadding=this.cellpadding;
5550         }
5551         if (this.cellspacing) {
5552             cfg.cellspacing=this.cellspacing;
5553         }
5554         if (this.frame) {
5555             cfg.frame=this.frame;
5556         }
5557         if (this.rules) {
5558             cfg.rules=this.rules;
5559         }
5560         if (this.sortable) {
5561             cfg.sortable=this.sortable;
5562         }
5563         if (this.summary) {
5564             cfg.summary=this.summary;
5565         }
5566         if (this.width) {
5567             cfg.width=this.width;
5568         }
5569         if (this.layout) {
5570             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5571         }
5572         
5573         if(this.store || this.cm){
5574             if(this.thead){
5575                 cfg.cn.push(this.renderHeader());
5576             }
5577             
5578             cfg.cn.push(this.renderBody());
5579             
5580             if(this.tfoot){
5581                 cfg.cn.push(this.renderFooter());
5582             }
5583             
5584             cfg.cls+=  ' TableGrid';
5585         }
5586         
5587         return { cn : [ cfg ] };
5588     },
5589     
5590     initEvents : function()
5591     {   
5592         if(!this.store || !this.cm){
5593             return;
5594         }
5595         
5596         //Roo.log('initEvents with ds!!!!');
5597         
5598         this.mainBody = this.el.select('tbody', true).first();
5599         
5600         
5601         var _this = this;
5602         
5603         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5604             e.on('click', _this.sort, _this);
5605         });
5606         
5607         this.el.on("click", this.onClick, this);
5608         this.el.on("dblclick", this.onDblClick, this);
5609         
5610         // why is this done????? = it breaks dialogs??
5611         //this.parent().el.setStyle('position', 'relative');
5612         
5613         
5614         if (this.footer) {
5615             this.footer.parentId = this.id;
5616             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5617         }
5618         
5619         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5620         
5621         this.store.on('load', this.onLoad, this);
5622         this.store.on('beforeload', this.onBeforeLoad, this);
5623         this.store.on('update', this.onUpdate, this);
5624         this.store.on('add', this.onAdd, this);
5625         
5626     },
5627     
5628     onMouseover : function(e, el)
5629     {
5630         var cell = Roo.get(el);
5631         
5632         if(!cell){
5633             return;
5634         }
5635         
5636         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5637             cell = cell.findParent('td', false, true);
5638         }
5639         
5640         var row = cell.findParent('tr', false, true);
5641         var cellIndex = cell.dom.cellIndex;
5642         var rowIndex = row.dom.rowIndex - 1; // start from 0
5643         
5644         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5645         
5646     },
5647     
5648     onMouseout : function(e, el)
5649     {
5650         var cell = Roo.get(el);
5651         
5652         if(!cell){
5653             return;
5654         }
5655         
5656         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5657             cell = cell.findParent('td', false, true);
5658         }
5659         
5660         var row = cell.findParent('tr', false, true);
5661         var cellIndex = cell.dom.cellIndex;
5662         var rowIndex = row.dom.rowIndex - 1; // start from 0
5663         
5664         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5665         
5666     },
5667     
5668     onClick : function(e, el)
5669     {
5670         var cell = Roo.get(el);
5671         
5672         if(!cell || (!this.CellSelection && !this.RowSelection)){
5673             return;
5674         }
5675         
5676         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5677             cell = cell.findParent('td', false, true);
5678         }
5679         
5680         if(!cell || typeof(cell) == 'undefined'){
5681             return;
5682         }
5683         
5684         var row = cell.findParent('tr', false, true);
5685         
5686         if(!row || typeof(row) == 'undefined'){
5687             return;
5688         }
5689         
5690         var cellIndex = cell.dom.cellIndex;
5691         var rowIndex = this.getRowIndex(row);
5692         
5693         if(this.CellSelection){
5694             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5695         }
5696         
5697         if(this.RowSelection){
5698             this.fireEvent('rowclick', this, row, rowIndex, e);
5699         }
5700         
5701         
5702     },
5703     
5704     onDblClick : function(e,el)
5705     {
5706         var cell = Roo.get(el);
5707         
5708         if(!cell || (!this.CellSelection && !this.RowSelection)){
5709             return;
5710         }
5711         
5712         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5713             cell = cell.findParent('td', false, true);
5714         }
5715         
5716         if(!cell || typeof(cell) == 'undefined'){
5717             return;
5718         }
5719         
5720         var row = cell.findParent('tr', false, true);
5721         
5722         if(!row || typeof(row) == 'undefined'){
5723             return;
5724         }
5725         
5726         var cellIndex = cell.dom.cellIndex;
5727         var rowIndex = this.getRowIndex(row);
5728         
5729         if(this.CellSelection){
5730             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5731         }
5732         
5733         if(this.RowSelection){
5734             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5735         }
5736     },
5737     
5738     sort : function(e,el)
5739     {
5740         var col = Roo.get(el);
5741         
5742         if(!col.hasClass('sortable')){
5743             return;
5744         }
5745         
5746         var sort = col.attr('sort');
5747         var dir = 'ASC';
5748         
5749         if(col.hasClass('glyphicon-arrow-up')){
5750             dir = 'DESC';
5751         }
5752         
5753         this.store.sortInfo = {field : sort, direction : dir};
5754         
5755         if (this.footer) {
5756             Roo.log("calling footer first");
5757             this.footer.onClick('first');
5758         } else {
5759         
5760             this.store.load({ params : { start : 0 } });
5761         }
5762     },
5763     
5764     renderHeader : function()
5765     {
5766         var header = {
5767             tag: 'thead',
5768             cn : []
5769         };
5770         
5771         var cm = this.cm;
5772         
5773         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5774             
5775             var config = cm.config[i];
5776                     
5777             var c = {
5778                 tag: 'th',
5779                 style : '',
5780                 html: cm.getColumnHeader(i)
5781             };
5782             
5783             if(typeof(config.tooltip) != 'undefined'){
5784                 c.tooltip = config.tooltip;
5785             }
5786             
5787             if(typeof(config.colspan) != 'undefined'){
5788                 c.colspan = config.colspan;
5789             }
5790             
5791             if(typeof(config.hidden) != 'undefined' && config.hidden){
5792                 c.style += ' display:none;';
5793             }
5794             
5795             if(typeof(config.dataIndex) != 'undefined'){
5796                 c.sort = config.dataIndex;
5797             }
5798             
5799             if(typeof(config.sortable) != 'undefined' && config.sortable){
5800                 c.cls = 'sortable';
5801             }
5802             
5803             if(typeof(config.align) != 'undefined' && config.align.length){
5804                 c.style += ' text-align:' + config.align + ';';
5805             }
5806             
5807             if(typeof(config.width) != 'undefined'){
5808                 c.style += ' width:' + config.width + 'px;';
5809             }
5810             
5811             if(typeof(config.cls) != 'undefined'){
5812                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5813             }
5814             
5815             header.cn.push(c)
5816         }
5817         
5818         return header;
5819     },
5820     
5821     renderBody : function()
5822     {
5823         var body = {
5824             tag: 'tbody',
5825             cn : [
5826                 {
5827                     tag: 'tr',
5828                     cn : [
5829                         {
5830                             tag : 'td',
5831                             colspan :  this.cm.getColumnCount()
5832                         }
5833                     ]
5834                 }
5835             ]
5836         };
5837         
5838         return body;
5839     },
5840     
5841     renderFooter : function()
5842     {
5843         var footer = {
5844             tag: 'tfoot',
5845             cn : [
5846                 {
5847                     tag: 'tr',
5848                     cn : [
5849                         {
5850                             tag : 'td',
5851                             colspan :  this.cm.getColumnCount()
5852                         }
5853                     ]
5854                 }
5855             ]
5856         };
5857         
5858         return footer;
5859     },
5860     
5861     
5862     
5863     onLoad : function()
5864     {
5865         Roo.log('ds onload');
5866         this.clear();
5867         
5868         var _this = this;
5869         var cm = this.cm;
5870         var ds = this.store;
5871         
5872         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5873             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5874             
5875             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5876                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5877             }
5878             
5879             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5880                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5881             }
5882         });
5883         
5884         var tbody =  this.mainBody;
5885               
5886         if(ds.getCount() > 0){
5887             ds.data.each(function(d,rowIndex){
5888                 var row =  this.renderRow(cm, ds, rowIndex);
5889                 
5890                 tbody.createChild(row);
5891                 
5892                 var _this = this;
5893                 
5894                 if(row.cellObjects.length){
5895                     Roo.each(row.cellObjects, function(r){
5896                         _this.renderCellObject(r);
5897                     })
5898                 }
5899                 
5900             }, this);
5901         }
5902         
5903         Roo.each(this.el.select('tbody td', true).elements, function(e){
5904             e.on('mouseover', _this.onMouseover, _this);
5905         });
5906         
5907         Roo.each(this.el.select('tbody td', true).elements, function(e){
5908             e.on('mouseout', _this.onMouseout, _this);
5909         });
5910         this.fireEvent('rowsrendered', this);
5911         //if(this.loadMask){
5912         //    this.maskEl.hide();
5913         //}
5914     },
5915     
5916     
5917     onUpdate : function(ds,record)
5918     {
5919         this.refreshRow(record);
5920     },
5921     
5922     onRemove : function(ds, record, index, isUpdate){
5923         if(isUpdate !== true){
5924             this.fireEvent("beforerowremoved", this, index, record);
5925         }
5926         var bt = this.mainBody.dom;
5927         
5928         var rows = this.el.select('tbody > tr', true).elements;
5929         
5930         if(typeof(rows[index]) != 'undefined'){
5931             bt.removeChild(rows[index].dom);
5932         }
5933         
5934 //        if(bt.rows[index]){
5935 //            bt.removeChild(bt.rows[index]);
5936 //        }
5937         
5938         if(isUpdate !== true){
5939             //this.stripeRows(index);
5940             //this.syncRowHeights(index, index);
5941             //this.layout();
5942             this.fireEvent("rowremoved", this, index, record);
5943         }
5944     },
5945     
5946     onAdd : function(ds, records, rowIndex)
5947     {
5948         //Roo.log('on Add called');
5949         // - note this does not handle multiple adding very well..
5950         var bt = this.mainBody.dom;
5951         for (var i =0 ; i < records.length;i++) {
5952             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5953             //Roo.log(records[i]);
5954             //Roo.log(this.store.getAt(rowIndex+i));
5955             this.insertRow(this.store, rowIndex + i, false);
5956             return;
5957         }
5958         
5959     },
5960     
5961     
5962     refreshRow : function(record){
5963         var ds = this.store, index;
5964         if(typeof record == 'number'){
5965             index = record;
5966             record = ds.getAt(index);
5967         }else{
5968             index = ds.indexOf(record);
5969         }
5970         this.insertRow(ds, index, true);
5971         this.onRemove(ds, record, index+1, true);
5972         //this.syncRowHeights(index, index);
5973         //this.layout();
5974         this.fireEvent("rowupdated", this, index, record);
5975     },
5976     
5977     insertRow : function(dm, rowIndex, isUpdate){
5978         
5979         if(!isUpdate){
5980             this.fireEvent("beforerowsinserted", this, rowIndex);
5981         }
5982             //var s = this.getScrollState();
5983         var row = this.renderRow(this.cm, this.store, rowIndex);
5984         // insert before rowIndex..
5985         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5986         
5987         var _this = this;
5988                 
5989         if(row.cellObjects.length){
5990             Roo.each(row.cellObjects, function(r){
5991                 _this.renderCellObject(r);
5992             })
5993         }
5994             
5995         if(!isUpdate){
5996             this.fireEvent("rowsinserted", this, rowIndex);
5997             //this.syncRowHeights(firstRow, lastRow);
5998             //this.stripeRows(firstRow);
5999             //this.layout();
6000         }
6001         
6002     },
6003     
6004     
6005     getRowDom : function(rowIndex)
6006     {
6007         var rows = this.el.select('tbody > tr', true).elements;
6008         
6009         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6010         
6011     },
6012     // returns the object tree for a tr..
6013   
6014     
6015     renderRow : function(cm, ds, rowIndex) 
6016     {
6017         
6018         var d = ds.getAt(rowIndex);
6019         
6020         var row = {
6021             tag : 'tr',
6022             cn : []
6023         };
6024             
6025         var cellObjects = [];
6026         
6027         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6028             var config = cm.config[i];
6029             
6030             var renderer = cm.getRenderer(i);
6031             var value = '';
6032             var id = false;
6033             
6034             if(typeof(renderer) !== 'undefined'){
6035                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6036             }
6037             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6038             // and are rendered into the cells after the row is rendered - using the id for the element.
6039             
6040             if(typeof(value) === 'object'){
6041                 id = Roo.id();
6042                 cellObjects.push({
6043                     container : id,
6044                     cfg : value 
6045                 })
6046             }
6047             
6048             var rowcfg = {
6049                 record: d,
6050                 rowIndex : rowIndex,
6051                 colIndex : i,
6052                 rowClass : ''
6053             }
6054
6055             this.fireEvent('rowclass', this, rowcfg);
6056             
6057             var td = {
6058                 tag: 'td',
6059                 cls : rowcfg.rowClass,
6060                 style: '',
6061                 html: (typeof(value) === 'object') ? '' : value
6062             };
6063             
6064             if (id) {
6065                 td.id = id;
6066             }
6067             
6068             if(typeof(config.colspan) != 'undefined'){
6069                 td.colspan = config.colspan;
6070             }
6071             
6072             if(typeof(config.hidden) != 'undefined' && config.hidden){
6073                 td.style += ' display:none;';
6074             }
6075             
6076             if(typeof(config.align) != 'undefined' && config.align.length){
6077                 td.style += ' text-align:' + config.align + ';';
6078             }
6079             
6080             if(typeof(config.width) != 'undefined'){
6081                 td.style += ' width:' +  config.width + 'px;';
6082             }
6083             
6084             if(typeof(config.cursor) != 'undefined'){
6085                 td.style += ' cursor:' +  config.cursor + ';';
6086             }
6087             
6088             if(typeof(config.cls) != 'undefined'){
6089                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6090             }
6091              
6092             row.cn.push(td);
6093            
6094         }
6095         
6096         row.cellObjects = cellObjects;
6097         
6098         return row;
6099           
6100     },
6101     
6102     
6103     
6104     onBeforeLoad : function()
6105     {
6106         //Roo.log('ds onBeforeLoad');
6107         
6108         //this.clear();
6109         
6110         //if(this.loadMask){
6111         //    this.maskEl.show();
6112         //}
6113     },
6114      /**
6115      * Remove all rows
6116      */
6117     clear : function()
6118     {
6119         this.el.select('tbody', true).first().dom.innerHTML = '';
6120     },
6121     /**
6122      * Show or hide a row.
6123      * @param {Number} rowIndex to show or hide
6124      * @param {Boolean} state hide
6125      */
6126     setRowVisibility : function(rowIndex, state)
6127     {
6128         var bt = this.mainBody.dom;
6129         
6130         var rows = this.el.select('tbody > tr', true).elements;
6131         
6132         if(typeof(rows[rowIndex]) == 'undefined'){
6133             return;
6134         }
6135         rows[rowIndex].dom.style.display = state ? '' : 'none';
6136     },
6137     
6138     
6139     getSelectionModel : function(){
6140         if(!this.selModel){
6141             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6142         }
6143         return this.selModel;
6144     },
6145     /*
6146      * Render the Roo.bootstrap object from renderder
6147      */
6148     renderCellObject : function(r)
6149     {
6150         var _this = this;
6151         
6152         var t = r.cfg.render(r.container);
6153         
6154         if(r.cfg.cn){
6155             Roo.each(r.cfg.cn, function(c){
6156                 var child = {
6157                     container: t.getChildContainer(),
6158                     cfg: c
6159                 }
6160                 _this.renderCellObject(child);
6161             })
6162         }
6163     },
6164     
6165     getRowIndex : function(row)
6166     {
6167         var rowIndex = -1;
6168         
6169         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6170             if(el != row){
6171                 return;
6172             }
6173             
6174             rowIndex = index;
6175         });
6176         
6177         return rowIndex;
6178     }
6179    
6180 });
6181
6182  
6183
6184  /*
6185  * - LGPL
6186  *
6187  * table cell
6188  * 
6189  */
6190
6191 /**
6192  * @class Roo.bootstrap.TableCell
6193  * @extends Roo.bootstrap.Component
6194  * Bootstrap TableCell class
6195  * @cfg {String} html cell contain text
6196  * @cfg {String} cls cell class
6197  * @cfg {String} tag cell tag (td|th) default td
6198  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6199  * @cfg {String} align Aligns the content in a cell
6200  * @cfg {String} axis Categorizes cells
6201  * @cfg {String} bgcolor Specifies the background color of a cell
6202  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6203  * @cfg {Number} colspan Specifies the number of columns a cell should span
6204  * @cfg {String} headers Specifies one or more header cells a cell is related to
6205  * @cfg {Number} height Sets the height of a cell
6206  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6207  * @cfg {Number} rowspan Sets the number of rows a cell should span
6208  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6209  * @cfg {String} valign Vertical aligns the content in a cell
6210  * @cfg {Number} width Specifies the width of a cell
6211  * 
6212  * @constructor
6213  * Create a new TableCell
6214  * @param {Object} config The config object
6215  */
6216
6217 Roo.bootstrap.TableCell = function(config){
6218     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6219 };
6220
6221 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6222     
6223     html: false,
6224     cls: false,
6225     tag: false,
6226     abbr: false,
6227     align: false,
6228     axis: false,
6229     bgcolor: false,
6230     charoff: false,
6231     colspan: false,
6232     headers: false,
6233     height: false,
6234     nowrap: false,
6235     rowspan: false,
6236     scope: false,
6237     valign: false,
6238     width: false,
6239     
6240     
6241     getAutoCreate : function(){
6242         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6243         
6244         cfg = {
6245             tag: 'td'
6246         }
6247         
6248         if(this.tag){
6249             cfg.tag = this.tag;
6250         }
6251         
6252         if (this.html) {
6253             cfg.html=this.html
6254         }
6255         if (this.cls) {
6256             cfg.cls=this.cls
6257         }
6258         if (this.abbr) {
6259             cfg.abbr=this.abbr
6260         }
6261         if (this.align) {
6262             cfg.align=this.align
6263         }
6264         if (this.axis) {
6265             cfg.axis=this.axis
6266         }
6267         if (this.bgcolor) {
6268             cfg.bgcolor=this.bgcolor
6269         }
6270         if (this.charoff) {
6271             cfg.charoff=this.charoff
6272         }
6273         if (this.colspan) {
6274             cfg.colspan=this.colspan
6275         }
6276         if (this.headers) {
6277             cfg.headers=this.headers
6278         }
6279         if (this.height) {
6280             cfg.height=this.height
6281         }
6282         if (this.nowrap) {
6283             cfg.nowrap=this.nowrap
6284         }
6285         if (this.rowspan) {
6286             cfg.rowspan=this.rowspan
6287         }
6288         if (this.scope) {
6289             cfg.scope=this.scope
6290         }
6291         if (this.valign) {
6292             cfg.valign=this.valign
6293         }
6294         if (this.width) {
6295             cfg.width=this.width
6296         }
6297         
6298         
6299         return cfg;
6300     }
6301    
6302 });
6303
6304  
6305
6306  /*
6307  * - LGPL
6308  *
6309  * table row
6310  * 
6311  */
6312
6313 /**
6314  * @class Roo.bootstrap.TableRow
6315  * @extends Roo.bootstrap.Component
6316  * Bootstrap TableRow class
6317  * @cfg {String} cls row class
6318  * @cfg {String} align Aligns the content in a table row
6319  * @cfg {String} bgcolor Specifies a background color for a table row
6320  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6321  * @cfg {String} valign Vertical aligns the content in a table row
6322  * 
6323  * @constructor
6324  * Create a new TableRow
6325  * @param {Object} config The config object
6326  */
6327
6328 Roo.bootstrap.TableRow = function(config){
6329     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6330 };
6331
6332 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6333     
6334     cls: false,
6335     align: false,
6336     bgcolor: false,
6337     charoff: false,
6338     valign: false,
6339     
6340     getAutoCreate : function(){
6341         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6342         
6343         cfg = {
6344             tag: 'tr'
6345         }
6346             
6347         if(this.cls){
6348             cfg.cls = this.cls;
6349         }
6350         if(this.align){
6351             cfg.align = this.align;
6352         }
6353         if(this.bgcolor){
6354             cfg.bgcolor = this.bgcolor;
6355         }
6356         if(this.charoff){
6357             cfg.charoff = this.charoff;
6358         }
6359         if(this.valign){
6360             cfg.valign = this.valign;
6361         }
6362         
6363         return cfg;
6364     }
6365    
6366 });
6367
6368  
6369
6370  /*
6371  * - LGPL
6372  *
6373  * table body
6374  * 
6375  */
6376
6377 /**
6378  * @class Roo.bootstrap.TableBody
6379  * @extends Roo.bootstrap.Component
6380  * Bootstrap TableBody class
6381  * @cfg {String} cls element class
6382  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6383  * @cfg {String} align Aligns the content inside the element
6384  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6385  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6386  * 
6387  * @constructor
6388  * Create a new TableBody
6389  * @param {Object} config The config object
6390  */
6391
6392 Roo.bootstrap.TableBody = function(config){
6393     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6394 };
6395
6396 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6397     
6398     cls: false,
6399     tag: false,
6400     align: false,
6401     charoff: false,
6402     valign: false,
6403     
6404     getAutoCreate : function(){
6405         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6406         
6407         cfg = {
6408             tag: 'tbody'
6409         }
6410             
6411         if (this.cls) {
6412             cfg.cls=this.cls
6413         }
6414         if(this.tag){
6415             cfg.tag = this.tag;
6416         }
6417         
6418         if(this.align){
6419             cfg.align = this.align;
6420         }
6421         if(this.charoff){
6422             cfg.charoff = this.charoff;
6423         }
6424         if(this.valign){
6425             cfg.valign = this.valign;
6426         }
6427         
6428         return cfg;
6429     }
6430     
6431     
6432 //    initEvents : function()
6433 //    {
6434 //        
6435 //        if(!this.store){
6436 //            return;
6437 //        }
6438 //        
6439 //        this.store = Roo.factory(this.store, Roo.data);
6440 //        this.store.on('load', this.onLoad, this);
6441 //        
6442 //        this.store.load();
6443 //        
6444 //    },
6445 //    
6446 //    onLoad: function () 
6447 //    {   
6448 //        this.fireEvent('load', this);
6449 //    }
6450 //    
6451 //   
6452 });
6453
6454  
6455
6456  /*
6457  * Based on:
6458  * Ext JS Library 1.1.1
6459  * Copyright(c) 2006-2007, Ext JS, LLC.
6460  *
6461  * Originally Released Under LGPL - original licence link has changed is not relivant.
6462  *
6463  * Fork - LGPL
6464  * <script type="text/javascript">
6465  */
6466
6467 // as we use this in bootstrap.
6468 Roo.namespace('Roo.form');
6469  /**
6470  * @class Roo.form.Action
6471  * Internal Class used to handle form actions
6472  * @constructor
6473  * @param {Roo.form.BasicForm} el The form element or its id
6474  * @param {Object} config Configuration options
6475  */
6476
6477  
6478  
6479 // define the action interface
6480 Roo.form.Action = function(form, options){
6481     this.form = form;
6482     this.options = options || {};
6483 };
6484 /**
6485  * Client Validation Failed
6486  * @const 
6487  */
6488 Roo.form.Action.CLIENT_INVALID = 'client';
6489 /**
6490  * Server Validation Failed
6491  * @const 
6492  */
6493 Roo.form.Action.SERVER_INVALID = 'server';
6494  /**
6495  * Connect to Server Failed
6496  * @const 
6497  */
6498 Roo.form.Action.CONNECT_FAILURE = 'connect';
6499 /**
6500  * Reading Data from Server Failed
6501  * @const 
6502  */
6503 Roo.form.Action.LOAD_FAILURE = 'load';
6504
6505 Roo.form.Action.prototype = {
6506     type : 'default',
6507     failureType : undefined,
6508     response : undefined,
6509     result : undefined,
6510
6511     // interface method
6512     run : function(options){
6513
6514     },
6515
6516     // interface method
6517     success : function(response){
6518
6519     },
6520
6521     // interface method
6522     handleResponse : function(response){
6523
6524     },
6525
6526     // default connection failure
6527     failure : function(response){
6528         
6529         this.response = response;
6530         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6531         this.form.afterAction(this, false);
6532     },
6533
6534     processResponse : function(response){
6535         this.response = response;
6536         if(!response.responseText){
6537             return true;
6538         }
6539         this.result = this.handleResponse(response);
6540         return this.result;
6541     },
6542
6543     // utility functions used internally
6544     getUrl : function(appendParams){
6545         var url = this.options.url || this.form.url || this.form.el.dom.action;
6546         if(appendParams){
6547             var p = this.getParams();
6548             if(p){
6549                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6550             }
6551         }
6552         return url;
6553     },
6554
6555     getMethod : function(){
6556         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6557     },
6558
6559     getParams : function(){
6560         var bp = this.form.baseParams;
6561         var p = this.options.params;
6562         if(p){
6563             if(typeof p == "object"){
6564                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6565             }else if(typeof p == 'string' && bp){
6566                 p += '&' + Roo.urlEncode(bp);
6567             }
6568         }else if(bp){
6569             p = Roo.urlEncode(bp);
6570         }
6571         return p;
6572     },
6573
6574     createCallback : function(){
6575         return {
6576             success: this.success,
6577             failure: this.failure,
6578             scope: this,
6579             timeout: (this.form.timeout*1000),
6580             upload: this.form.fileUpload ? this.success : undefined
6581         };
6582     }
6583 };
6584
6585 Roo.form.Action.Submit = function(form, options){
6586     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6587 };
6588
6589 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6590     type : 'submit',
6591
6592     haveProgress : false,
6593     uploadComplete : false,
6594     
6595     // uploadProgress indicator.
6596     uploadProgress : function()
6597     {
6598         if (!this.form.progressUrl) {
6599             return;
6600         }
6601         
6602         if (!this.haveProgress) {
6603             Roo.MessageBox.progress("Uploading", "Uploading");
6604         }
6605         if (this.uploadComplete) {
6606            Roo.MessageBox.hide();
6607            return;
6608         }
6609         
6610         this.haveProgress = true;
6611    
6612         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6613         
6614         var c = new Roo.data.Connection();
6615         c.request({
6616             url : this.form.progressUrl,
6617             params: {
6618                 id : uid
6619             },
6620             method: 'GET',
6621             success : function(req){
6622                //console.log(data);
6623                 var rdata = false;
6624                 var edata;
6625                 try  {
6626                    rdata = Roo.decode(req.responseText)
6627                 } catch (e) {
6628                     Roo.log("Invalid data from server..");
6629                     Roo.log(edata);
6630                     return;
6631                 }
6632                 if (!rdata || !rdata.success) {
6633                     Roo.log(rdata);
6634                     Roo.MessageBox.alert(Roo.encode(rdata));
6635                     return;
6636                 }
6637                 var data = rdata.data;
6638                 
6639                 if (this.uploadComplete) {
6640                    Roo.MessageBox.hide();
6641                    return;
6642                 }
6643                    
6644                 if (data){
6645                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6646                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6647                     );
6648                 }
6649                 this.uploadProgress.defer(2000,this);
6650             },
6651        
6652             failure: function(data) {
6653                 Roo.log('progress url failed ');
6654                 Roo.log(data);
6655             },
6656             scope : this
6657         });
6658            
6659     },
6660     
6661     
6662     run : function()
6663     {
6664         // run get Values on the form, so it syncs any secondary forms.
6665         this.form.getValues();
6666         
6667         var o = this.options;
6668         var method = this.getMethod();
6669         var isPost = method == 'POST';
6670         if(o.clientValidation === false || this.form.isValid()){
6671             
6672             if (this.form.progressUrl) {
6673                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6674                     (new Date() * 1) + '' + Math.random());
6675                     
6676             } 
6677             
6678             
6679             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6680                 form:this.form.el.dom,
6681                 url:this.getUrl(!isPost),
6682                 method: method,
6683                 params:isPost ? this.getParams() : null,
6684                 isUpload: this.form.fileUpload
6685             }));
6686             
6687             this.uploadProgress();
6688
6689         }else if (o.clientValidation !== false){ // client validation failed
6690             this.failureType = Roo.form.Action.CLIENT_INVALID;
6691             this.form.afterAction(this, false);
6692         }
6693     },
6694
6695     success : function(response)
6696     {
6697         this.uploadComplete= true;
6698         if (this.haveProgress) {
6699             Roo.MessageBox.hide();
6700         }
6701         
6702         
6703         var result = this.processResponse(response);
6704         if(result === true || result.success){
6705             this.form.afterAction(this, true);
6706             return;
6707         }
6708         if(result.errors){
6709             this.form.markInvalid(result.errors);
6710             this.failureType = Roo.form.Action.SERVER_INVALID;
6711         }
6712         this.form.afterAction(this, false);
6713     },
6714     failure : function(response)
6715     {
6716         this.uploadComplete= true;
6717         if (this.haveProgress) {
6718             Roo.MessageBox.hide();
6719         }
6720         
6721         this.response = response;
6722         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6723         this.form.afterAction(this, false);
6724     },
6725     
6726     handleResponse : function(response){
6727         if(this.form.errorReader){
6728             var rs = this.form.errorReader.read(response);
6729             var errors = [];
6730             if(rs.records){
6731                 for(var i = 0, len = rs.records.length; i < len; i++) {
6732                     var r = rs.records[i];
6733                     errors[i] = r.data;
6734                 }
6735             }
6736             if(errors.length < 1){
6737                 errors = null;
6738             }
6739             return {
6740                 success : rs.success,
6741                 errors : errors
6742             };
6743         }
6744         var ret = false;
6745         try {
6746             ret = Roo.decode(response.responseText);
6747         } catch (e) {
6748             ret = {
6749                 success: false,
6750                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6751                 errors : []
6752             };
6753         }
6754         return ret;
6755         
6756     }
6757 });
6758
6759
6760 Roo.form.Action.Load = function(form, options){
6761     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6762     this.reader = this.form.reader;
6763 };
6764
6765 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6766     type : 'load',
6767
6768     run : function(){
6769         
6770         Roo.Ajax.request(Roo.apply(
6771                 this.createCallback(), {
6772                     method:this.getMethod(),
6773                     url:this.getUrl(false),
6774                     params:this.getParams()
6775         }));
6776     },
6777
6778     success : function(response){
6779         
6780         var result = this.processResponse(response);
6781         if(result === true || !result.success || !result.data){
6782             this.failureType = Roo.form.Action.LOAD_FAILURE;
6783             this.form.afterAction(this, false);
6784             return;
6785         }
6786         this.form.clearInvalid();
6787         this.form.setValues(result.data);
6788         this.form.afterAction(this, true);
6789     },
6790
6791     handleResponse : function(response){
6792         if(this.form.reader){
6793             var rs = this.form.reader.read(response);
6794             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6795             return {
6796                 success : rs.success,
6797                 data : data
6798             };
6799         }
6800         return Roo.decode(response.responseText);
6801     }
6802 });
6803
6804 Roo.form.Action.ACTION_TYPES = {
6805     'load' : Roo.form.Action.Load,
6806     'submit' : Roo.form.Action.Submit
6807 };/*
6808  * - LGPL
6809  *
6810  * form
6811  * 
6812  */
6813
6814 /**
6815  * @class Roo.bootstrap.Form
6816  * @extends Roo.bootstrap.Component
6817  * Bootstrap Form class
6818  * @cfg {String} method  GET | POST (default POST)
6819  * @cfg {String} labelAlign top | left (default top)
6820  * @cfg {String} align left  | right - for navbars
6821  * @cfg {Boolean} loadMask load mask when submit (default true)
6822
6823  * 
6824  * @constructor
6825  * Create a new Form
6826  * @param {Object} config The config object
6827  */
6828
6829
6830 Roo.bootstrap.Form = function(config){
6831     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6832     this.addEvents({
6833         /**
6834          * @event clientvalidation
6835          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6836          * @param {Form} this
6837          * @param {Boolean} valid true if the form has passed client-side validation
6838          */
6839         clientvalidation: true,
6840         /**
6841          * @event beforeaction
6842          * Fires before any action is performed. Return false to cancel the action.
6843          * @param {Form} this
6844          * @param {Action} action The action to be performed
6845          */
6846         beforeaction: true,
6847         /**
6848          * @event actionfailed
6849          * Fires when an action fails.
6850          * @param {Form} this
6851          * @param {Action} action The action that failed
6852          */
6853         actionfailed : true,
6854         /**
6855          * @event actioncomplete
6856          * Fires when an action is completed.
6857          * @param {Form} this
6858          * @param {Action} action The action that completed
6859          */
6860         actioncomplete : true
6861     });
6862     
6863 };
6864
6865 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6866       
6867      /**
6868      * @cfg {String} method
6869      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6870      */
6871     method : 'POST',
6872     /**
6873      * @cfg {String} url
6874      * The URL to use for form actions if one isn't supplied in the action options.
6875      */
6876     /**
6877      * @cfg {Boolean} fileUpload
6878      * Set to true if this form is a file upload.
6879      */
6880      
6881     /**
6882      * @cfg {Object} baseParams
6883      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6884      */
6885       
6886     /**
6887      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6888      */
6889     timeout: 30,
6890     /**
6891      * @cfg {Sting} align (left|right) for navbar forms
6892      */
6893     align : 'left',
6894
6895     // private
6896     activeAction : null,
6897  
6898     /**
6899      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6900      * element by passing it or its id or mask the form itself by passing in true.
6901      * @type Mixed
6902      */
6903     waitMsgTarget : false,
6904     
6905     loadMask : true,
6906     
6907     getAutoCreate : function(){
6908         
6909         var cfg = {
6910             tag: 'form',
6911             method : this.method || 'POST',
6912             id : this.id || Roo.id(),
6913             cls : ''
6914         }
6915         if (this.parent().xtype.match(/^Nav/)) {
6916             cfg.cls = 'navbar-form navbar-' + this.align;
6917             
6918         }
6919         
6920         if (this.labelAlign == 'left' ) {
6921             cfg.cls += ' form-horizontal';
6922         }
6923         
6924         
6925         return cfg;
6926     },
6927     initEvents : function()
6928     {
6929         this.el.on('submit', this.onSubmit, this);
6930         // this was added as random key presses on the form where triggering form submit.
6931         this.el.on('keypress', function(e) {
6932             if (e.getCharCode() != 13) {
6933                 return true;
6934             }
6935             // we might need to allow it for textareas.. and some other items.
6936             // check e.getTarget().
6937             
6938             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6939                 return true;
6940             }
6941         
6942             Roo.log("keypress blocked");
6943             
6944             e.preventDefault();
6945             return false;
6946         });
6947         
6948     },
6949     // private
6950     onSubmit : function(e){
6951         e.stopEvent();
6952     },
6953     
6954      /**
6955      * Returns true if client-side validation on the form is successful.
6956      * @return Boolean
6957      */
6958     isValid : function(){
6959         var items = this.getItems();
6960         var valid = true;
6961         items.each(function(f){
6962            if(!f.validate()){
6963                valid = false;
6964                
6965            }
6966         });
6967         return valid;
6968     },
6969     /**
6970      * Returns true if any fields in this form have changed since their original load.
6971      * @return Boolean
6972      */
6973     isDirty : function(){
6974         var dirty = false;
6975         var items = this.getItems();
6976         items.each(function(f){
6977            if(f.isDirty()){
6978                dirty = true;
6979                return false;
6980            }
6981            return true;
6982         });
6983         return dirty;
6984     },
6985      /**
6986      * Performs a predefined action (submit or load) or custom actions you define on this form.
6987      * @param {String} actionName The name of the action type
6988      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6989      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6990      * accept other config options):
6991      * <pre>
6992 Property          Type             Description
6993 ----------------  ---------------  ----------------------------------------------------------------------------------
6994 url               String           The url for the action (defaults to the form's url)
6995 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6996 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6997 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6998                                    validate the form on the client (defaults to false)
6999      * </pre>
7000      * @return {BasicForm} this
7001      */
7002     doAction : function(action, options){
7003         if(typeof action == 'string'){
7004             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7005         }
7006         if(this.fireEvent('beforeaction', this, action) !== false){
7007             this.beforeAction(action);
7008             action.run.defer(100, action);
7009         }
7010         return this;
7011     },
7012     
7013     // private
7014     beforeAction : function(action){
7015         var o = action.options;
7016         
7017         if(this.loadMask){
7018             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7019         }
7020         // not really supported yet.. ??
7021         
7022         //if(this.waitMsgTarget === true){
7023         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7024         //}else if(this.waitMsgTarget){
7025         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7026         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7027         //}else {
7028         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7029        // }
7030          
7031     },
7032
7033     // private
7034     afterAction : function(action, success){
7035         this.activeAction = null;
7036         var o = action.options;
7037         
7038         //if(this.waitMsgTarget === true){
7039             this.el.unmask();
7040         //}else if(this.waitMsgTarget){
7041         //    this.waitMsgTarget.unmask();
7042         //}else{
7043         //    Roo.MessageBox.updateProgress(1);
7044         //    Roo.MessageBox.hide();
7045        // }
7046         // 
7047         if(success){
7048             if(o.reset){
7049                 this.reset();
7050             }
7051             Roo.callback(o.success, o.scope, [this, action]);
7052             this.fireEvent('actioncomplete', this, action);
7053             
7054         }else{
7055             
7056             // failure condition..
7057             // we have a scenario where updates need confirming.
7058             // eg. if a locking scenario exists..
7059             // we look for { errors : { needs_confirm : true }} in the response.
7060             if (
7061                 (typeof(action.result) != 'undefined')  &&
7062                 (typeof(action.result.errors) != 'undefined')  &&
7063                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7064            ){
7065                 var _t = this;
7066                 Roo.log("not supported yet");
7067                  /*
7068                 
7069                 Roo.MessageBox.confirm(
7070                     "Change requires confirmation",
7071                     action.result.errorMsg,
7072                     function(r) {
7073                         if (r != 'yes') {
7074                             return;
7075                         }
7076                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7077                     }
7078                     
7079                 );
7080                 */
7081                 
7082                 
7083                 return;
7084             }
7085             
7086             Roo.callback(o.failure, o.scope, [this, action]);
7087             // show an error message if no failed handler is set..
7088             if (!this.hasListener('actionfailed')) {
7089                 Roo.log("need to add dialog support");
7090                 /*
7091                 Roo.MessageBox.alert("Error",
7092                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7093                         action.result.errorMsg :
7094                         "Saving Failed, please check your entries or try again"
7095                 );
7096                 */
7097             }
7098             
7099             this.fireEvent('actionfailed', this, action);
7100         }
7101         
7102     },
7103     /**
7104      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7105      * @param {String} id The value to search for
7106      * @return Field
7107      */
7108     findField : function(id){
7109         var items = this.getItems();
7110         var field = items.get(id);
7111         if(!field){
7112              items.each(function(f){
7113                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7114                     field = f;
7115                     return false;
7116                 }
7117                 return true;
7118             });
7119         }
7120         return field || null;
7121     },
7122      /**
7123      * Mark fields in this form invalid in bulk.
7124      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7125      * @return {BasicForm} this
7126      */
7127     markInvalid : function(errors){
7128         if(errors instanceof Array){
7129             for(var i = 0, len = errors.length; i < len; i++){
7130                 var fieldError = errors[i];
7131                 var f = this.findField(fieldError.id);
7132                 if(f){
7133                     f.markInvalid(fieldError.msg);
7134                 }
7135             }
7136         }else{
7137             var field, id;
7138             for(id in errors){
7139                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7140                     field.markInvalid(errors[id]);
7141                 }
7142             }
7143         }
7144         //Roo.each(this.childForms || [], function (f) {
7145         //    f.markInvalid(errors);
7146         //});
7147         
7148         return this;
7149     },
7150
7151     /**
7152      * Set values for fields in this form in bulk.
7153      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7154      * @return {BasicForm} this
7155      */
7156     setValues : function(values){
7157         if(values instanceof Array){ // array of objects
7158             for(var i = 0, len = values.length; i < len; i++){
7159                 var v = values[i];
7160                 var f = this.findField(v.id);
7161                 if(f){
7162                     f.setValue(v.value);
7163                     if(this.trackResetOnLoad){
7164                         f.originalValue = f.getValue();
7165                     }
7166                 }
7167             }
7168         }else{ // object hash
7169             var field, id;
7170             for(id in values){
7171                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7172                     
7173                     if (field.setFromData && 
7174                         field.valueField && 
7175                         field.displayField &&
7176                         // combos' with local stores can 
7177                         // be queried via setValue()
7178                         // to set their value..
7179                         (field.store && !field.store.isLocal)
7180                         ) {
7181                         // it's a combo
7182                         var sd = { };
7183                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7184                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7185                         field.setFromData(sd);
7186                         
7187                     } else {
7188                         field.setValue(values[id]);
7189                     }
7190                     
7191                     
7192                     if(this.trackResetOnLoad){
7193                         field.originalValue = field.getValue();
7194                     }
7195                 }
7196             }
7197         }
7198          
7199         //Roo.each(this.childForms || [], function (f) {
7200         //    f.setValues(values);
7201         //});
7202                 
7203         return this;
7204     },
7205
7206     /**
7207      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7208      * they are returned as an array.
7209      * @param {Boolean} asString
7210      * @return {Object}
7211      */
7212     getValues : function(asString){
7213         //if (this.childForms) {
7214             // copy values from the child forms
7215         //    Roo.each(this.childForms, function (f) {
7216         //        this.setValues(f.getValues());
7217         //    }, this);
7218         //}
7219         
7220         
7221         
7222         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7223         if(asString === true){
7224             return fs;
7225         }
7226         return Roo.urlDecode(fs);
7227     },
7228     
7229     /**
7230      * Returns the fields in this form as an object with key/value pairs. 
7231      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7232      * @return {Object}
7233      */
7234     getFieldValues : function(with_hidden)
7235     {
7236         var items = this.getItems();
7237         var ret = {};
7238         items.each(function(f){
7239             if (!f.getName()) {
7240                 return;
7241             }
7242             var v = f.getValue();
7243             if (f.inputType =='radio') {
7244                 if (typeof(ret[f.getName()]) == 'undefined') {
7245                     ret[f.getName()] = ''; // empty..
7246                 }
7247                 
7248                 if (!f.el.dom.checked) {
7249                     return;
7250                     
7251                 }
7252                 v = f.el.dom.value;
7253                 
7254             }
7255             
7256             // not sure if this supported any more..
7257             if ((typeof(v) == 'object') && f.getRawValue) {
7258                 v = f.getRawValue() ; // dates..
7259             }
7260             // combo boxes where name != hiddenName...
7261             if (f.name != f.getName()) {
7262                 ret[f.name] = f.getRawValue();
7263             }
7264             ret[f.getName()] = v;
7265         });
7266         
7267         return ret;
7268     },
7269
7270     /**
7271      * Clears all invalid messages in this form.
7272      * @return {BasicForm} this
7273      */
7274     clearInvalid : function(){
7275         var items = this.getItems();
7276         
7277         items.each(function(f){
7278            f.clearInvalid();
7279         });
7280         
7281         
7282         
7283         return this;
7284     },
7285
7286     /**
7287      * Resets this form.
7288      * @return {BasicForm} this
7289      */
7290     reset : function(){
7291         var items = this.getItems();
7292         items.each(function(f){
7293             f.reset();
7294         });
7295         
7296         Roo.each(this.childForms || [], function (f) {
7297             f.reset();
7298         });
7299        
7300         
7301         return this;
7302     },
7303     getItems : function()
7304     {
7305         var r=new Roo.util.MixedCollection(false, function(o){
7306             return o.id || (o.id = Roo.id());
7307         });
7308         var iter = function(el) {
7309             if (el.inputEl) {
7310                 r.add(el);
7311             }
7312             if (!el.items) {
7313                 return;
7314             }
7315             Roo.each(el.items,function(e) {
7316                 iter(e);
7317             });
7318             
7319             
7320         };
7321         
7322         iter(this);
7323         return r;
7324         
7325         
7326         
7327         
7328     }
7329     
7330 });
7331
7332  
7333 /*
7334  * Based on:
7335  * Ext JS Library 1.1.1
7336  * Copyright(c) 2006-2007, Ext JS, LLC.
7337  *
7338  * Originally Released Under LGPL - original licence link has changed is not relivant.
7339  *
7340  * Fork - LGPL
7341  * <script type="text/javascript">
7342  */
7343 /**
7344  * @class Roo.form.VTypes
7345  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7346  * @singleton
7347  */
7348 Roo.form.VTypes = function(){
7349     // closure these in so they are only created once.
7350     var alpha = /^[a-zA-Z_]+$/;
7351     var alphanum = /^[a-zA-Z0-9_]+$/;
7352     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7353     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7354
7355     // All these messages and functions are configurable
7356     return {
7357         /**
7358          * The function used to validate email addresses
7359          * @param {String} value The email address
7360          */
7361         'email' : function(v){
7362             return email.test(v);
7363         },
7364         /**
7365          * The error text to display when the email validation function returns false
7366          * @type String
7367          */
7368         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7369         /**
7370          * The keystroke filter mask to be applied on email input
7371          * @type RegExp
7372          */
7373         'emailMask' : /[a-z0-9_\.\-@]/i,
7374
7375         /**
7376          * The function used to validate URLs
7377          * @param {String} value The URL
7378          */
7379         'url' : function(v){
7380             return url.test(v);
7381         },
7382         /**
7383          * The error text to display when the url validation function returns false
7384          * @type String
7385          */
7386         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7387         
7388         /**
7389          * The function used to validate alpha values
7390          * @param {String} value The value
7391          */
7392         'alpha' : function(v){
7393             return alpha.test(v);
7394         },
7395         /**
7396          * The error text to display when the alpha validation function returns false
7397          * @type String
7398          */
7399         'alphaText' : 'This field should only contain letters and _',
7400         /**
7401          * The keystroke filter mask to be applied on alpha input
7402          * @type RegExp
7403          */
7404         'alphaMask' : /[a-z_]/i,
7405
7406         /**
7407          * The function used to validate alphanumeric values
7408          * @param {String} value The value
7409          */
7410         'alphanum' : function(v){
7411             return alphanum.test(v);
7412         },
7413         /**
7414          * The error text to display when the alphanumeric validation function returns false
7415          * @type String
7416          */
7417         'alphanumText' : 'This field should only contain letters, numbers and _',
7418         /**
7419          * The keystroke filter mask to be applied on alphanumeric input
7420          * @type RegExp
7421          */
7422         'alphanumMask' : /[a-z0-9_]/i
7423     };
7424 }();/*
7425  * - LGPL
7426  *
7427  * Input
7428  * 
7429  */
7430
7431 /**
7432  * @class Roo.bootstrap.Input
7433  * @extends Roo.bootstrap.Component
7434  * Bootstrap Input class
7435  * @cfg {Boolean} disabled is it disabled
7436  * @cfg {String} fieldLabel - the label associated
7437  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7438  * @cfg {String} name name of the input
7439  * @cfg {string} fieldLabel - the label associated
7440  * @cfg {string}  inputType - input / file submit ...
7441  * @cfg {string} placeholder - placeholder to put in text.
7442  * @cfg {string}  before - input group add on before
7443  * @cfg {string} after - input group add on after
7444  * @cfg {string} size - (lg|sm) or leave empty..
7445  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7446  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7447  * @cfg {Number} md colspan out of 12 for computer-sized screens
7448  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7449  * @cfg {string} value default value of the input
7450  * @cfg {Number} labelWidth set the width of label (0-12)
7451  * @cfg {String} labelAlign (top|left)
7452  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7453  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7454
7455  * @cfg {String} align (left|center|right) Default left
7456  * 
7457  * 
7458  * 
7459  * @constructor
7460  * Create a new Input
7461  * @param {Object} config The config object
7462  */
7463
7464 Roo.bootstrap.Input = function(config){
7465     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7466    
7467         this.addEvents({
7468             /**
7469              * @event focus
7470              * Fires when this field receives input focus.
7471              * @param {Roo.form.Field} this
7472              */
7473             focus : true,
7474             /**
7475              * @event blur
7476              * Fires when this field loses input focus.
7477              * @param {Roo.form.Field} this
7478              */
7479             blur : true,
7480             /**
7481              * @event specialkey
7482              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7483              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7484              * @param {Roo.form.Field} this
7485              * @param {Roo.EventObject} e The event object
7486              */
7487             specialkey : true,
7488             /**
7489              * @event change
7490              * Fires just before the field blurs if the field value has changed.
7491              * @param {Roo.form.Field} this
7492              * @param {Mixed} newValue The new value
7493              * @param {Mixed} oldValue The original value
7494              */
7495             change : true,
7496             /**
7497              * @event invalid
7498              * Fires after the field has been marked as invalid.
7499              * @param {Roo.form.Field} this
7500              * @param {String} msg The validation message
7501              */
7502             invalid : true,
7503             /**
7504              * @event valid
7505              * Fires after the field has been validated with no errors.
7506              * @param {Roo.form.Field} this
7507              */
7508             valid : true,
7509              /**
7510              * @event keyup
7511              * Fires after the key up
7512              * @param {Roo.form.Field} this
7513              * @param {Roo.EventObject}  e The event Object
7514              */
7515             keyup : true
7516         });
7517 };
7518
7519 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7520      /**
7521      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7522       automatic validation (defaults to "keyup").
7523      */
7524     validationEvent : "keyup",
7525      /**
7526      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7527      */
7528     validateOnBlur : true,
7529     /**
7530      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7531      */
7532     validationDelay : 250,
7533      /**
7534      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7535      */
7536     focusClass : "x-form-focus",  // not needed???
7537     
7538        
7539     /**
7540      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7541      */
7542     invalidClass : "has-warning",
7543     
7544     /**
7545      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7546      */
7547     validClass : "has-success",
7548     
7549     /**
7550      * @cfg {Boolean} hasFeedback (true|false) default true
7551      */
7552     hasFeedback : true,
7553     
7554     /**
7555      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7556      */
7557     invalidFeedbackClass : "glyphicon-warning-sign",
7558     
7559     /**
7560      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7561      */
7562     validFeedbackClass : "glyphicon-ok",
7563     
7564     /**
7565      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7566      */
7567     selectOnFocus : false,
7568     
7569      /**
7570      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7571      */
7572     maskRe : null,
7573        /**
7574      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7575      */
7576     vtype : null,
7577     
7578       /**
7579      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7580      */
7581     disableKeyFilter : false,
7582     
7583        /**
7584      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7585      */
7586     disabled : false,
7587      /**
7588      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7589      */
7590     allowBlank : true,
7591     /**
7592      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7593      */
7594     blankText : "This field is required",
7595     
7596      /**
7597      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7598      */
7599     minLength : 0,
7600     /**
7601      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7602      */
7603     maxLength : Number.MAX_VALUE,
7604     /**
7605      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7606      */
7607     minLengthText : "The minimum length for this field is {0}",
7608     /**
7609      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7610      */
7611     maxLengthText : "The maximum length for this field is {0}",
7612   
7613     
7614     /**
7615      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7616      * If available, this function will be called only after the basic validators all return true, and will be passed the
7617      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7618      */
7619     validator : null,
7620     /**
7621      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7622      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7623      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7624      */
7625     regex : null,
7626     /**
7627      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7628      */
7629     regexText : "",
7630     
7631     autocomplete: false,
7632     
7633     
7634     fieldLabel : '',
7635     inputType : 'text',
7636     
7637     name : false,
7638     placeholder: false,
7639     before : false,
7640     after : false,
7641     size : false,
7642     hasFocus : false,
7643     preventMark: false,
7644     isFormField : true,
7645     value : '',
7646     labelWidth : 2,
7647     labelAlign : false,
7648     readOnly : false,
7649     align : false,
7650     formatedValue : false,
7651     
7652     parentLabelAlign : function()
7653     {
7654         var parent = this;
7655         while (parent.parent()) {
7656             parent = parent.parent();
7657             if (typeof(parent.labelAlign) !='undefined') {
7658                 return parent.labelAlign;
7659             }
7660         }
7661         return 'left';
7662         
7663     },
7664     
7665     getAutoCreate : function(){
7666         
7667         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7668         
7669         var id = Roo.id();
7670         
7671         var cfg = {};
7672         
7673         if(this.inputType != 'hidden'){
7674             cfg.cls = 'form-group' //input-group
7675         }
7676         
7677         var input =  {
7678             tag: 'input',
7679             id : id,
7680             type : this.inputType,
7681             value : this.value,
7682             cls : 'form-control',
7683             placeholder : this.placeholder || '',
7684             autocomplete : this.autocomplete || 'new-password'
7685         };
7686         
7687         
7688         if(this.align){
7689             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7690         }
7691         
7692         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7693             input.maxLength = this.maxLength;
7694         }
7695         
7696         if (this.disabled) {
7697             input.disabled=true;
7698         }
7699         
7700         if (this.readOnly) {
7701             input.readonly=true;
7702         }
7703         
7704         if (this.name) {
7705             input.name = this.name;
7706         }
7707         if (this.size) {
7708             input.cls += ' input-' + this.size;
7709         }
7710         var settings=this;
7711         ['xs','sm','md','lg'].map(function(size){
7712             if (settings[size]) {
7713                 cfg.cls += ' col-' + size + '-' + settings[size];
7714             }
7715         });
7716         
7717         var inputblock = input;
7718         
7719         var feedback = {
7720             tag: 'span',
7721             cls: 'glyphicon form-control-feedback'
7722         };
7723             
7724         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7725             
7726             inputblock = {
7727                 cls : 'has-feedback',
7728                 cn :  [
7729                     input,
7730                     feedback
7731                 ] 
7732             };  
7733         }
7734         
7735         if (this.before || this.after) {
7736             
7737             inputblock = {
7738                 cls : 'input-group',
7739                 cn :  [] 
7740             };
7741             
7742             if (this.before && typeof(this.before) == 'string') {
7743                 
7744                 inputblock.cn.push({
7745                     tag :'span',
7746                     cls : 'roo-input-before input-group-addon',
7747                     html : this.before
7748                 });
7749             }
7750             if (this.before && typeof(this.before) == 'object') {
7751                 this.before = Roo.factory(this.before);
7752                 Roo.log(this.before);
7753                 inputblock.cn.push({
7754                     tag :'span',
7755                     cls : 'roo-input-before input-group-' +
7756                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7757                 });
7758             }
7759             
7760             inputblock.cn.push(input);
7761             
7762             if (this.after && typeof(this.after) == 'string') {
7763                 inputblock.cn.push({
7764                     tag :'span',
7765                     cls : 'roo-input-after input-group-addon',
7766                     html : this.after
7767                 });
7768             }
7769             if (this.after && typeof(this.after) == 'object') {
7770                 this.after = Roo.factory(this.after);
7771                 Roo.log(this.after);
7772                 inputblock.cn.push({
7773                     tag :'span',
7774                     cls : 'roo-input-after input-group-' +
7775                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7776                 });
7777             }
7778             
7779             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7780                 inputblock.cls += ' has-feedback';
7781                 inputblock.cn.push(feedback);
7782             }
7783         };
7784         
7785         if (align ==='left' && this.fieldLabel.length) {
7786                 Roo.log("left and has label");
7787                 cfg.cn = [
7788                     
7789                     {
7790                         tag: 'label',
7791                         'for' :  id,
7792                         cls : 'control-label col-sm-' + this.labelWidth,
7793                         html : this.fieldLabel
7794                         
7795                     },
7796                     {
7797                         cls : "col-sm-" + (12 - this.labelWidth), 
7798                         cn: [
7799                             inputblock
7800                         ]
7801                     }
7802                     
7803                 ];
7804         } else if ( this.fieldLabel.length) {
7805                 Roo.log(" label");
7806                  cfg.cn = [
7807                    
7808                     {
7809                         tag: 'label',
7810                         //cls : 'input-group-addon',
7811                         html : this.fieldLabel
7812                         
7813                     },
7814                     
7815                     inputblock
7816                     
7817                 ];
7818
7819         } else {
7820             
7821                 Roo.log(" no label && no align");
7822                 cfg.cn = [
7823                     
7824                         inputblock
7825                     
7826                 ];
7827                 
7828                 
7829         };
7830         Roo.log('input-parentType: ' + this.parentType);
7831         
7832         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7833            cfg.cls += ' navbar-form';
7834            Roo.log(cfg);
7835         }
7836         
7837         return cfg;
7838         
7839     },
7840     /**
7841      * return the real input element.
7842      */
7843     inputEl: function ()
7844     {
7845         return this.el.select('input.form-control',true).first();
7846     },
7847     
7848     tooltipEl : function()
7849     {
7850         return this.inputEl();
7851     },
7852     
7853     setDisabled : function(v)
7854     {
7855         var i  = this.inputEl().dom;
7856         if (!v) {
7857             i.removeAttribute('disabled');
7858             return;
7859             
7860         }
7861         i.setAttribute('disabled','true');
7862     },
7863     initEvents : function()
7864     {
7865           
7866         this.inputEl().on("keydown" , this.fireKey,  this);
7867         this.inputEl().on("focus", this.onFocus,  this);
7868         this.inputEl().on("blur", this.onBlur,  this);
7869         
7870         this.inputEl().relayEvent('keyup', this);
7871
7872         // reference to original value for reset
7873         this.originalValue = this.getValue();
7874         //Roo.form.TextField.superclass.initEvents.call(this);
7875         if(this.validationEvent == 'keyup'){
7876             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7877             this.inputEl().on('keyup', this.filterValidation, this);
7878         }
7879         else if(this.validationEvent !== false){
7880             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7881         }
7882         
7883         if(this.selectOnFocus){
7884             this.on("focus", this.preFocus, this);
7885             
7886         }
7887         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7888             this.inputEl().on("keypress", this.filterKeys, this);
7889         }
7890        /* if(this.grow){
7891             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7892             this.el.on("click", this.autoSize,  this);
7893         }
7894         */
7895         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7896             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7897         }
7898         
7899         if (typeof(this.before) == 'object') {
7900             this.before.render(this.el.select('.roo-input-before',true).first());
7901         }
7902         if (typeof(this.after) == 'object') {
7903             this.after.render(this.el.select('.roo-input-after',true).first());
7904         }
7905         
7906         
7907     },
7908     filterValidation : function(e){
7909         if(!e.isNavKeyPress()){
7910             this.validationTask.delay(this.validationDelay);
7911         }
7912     },
7913      /**
7914      * Validates the field value
7915      * @return {Boolean} True if the value is valid, else false
7916      */
7917     validate : function(){
7918         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7919         if(this.disabled || this.validateValue(this.getRawValue())){
7920             this.markValid();
7921             return true;
7922         }
7923         
7924         this.markInvalid();
7925         return false;
7926     },
7927     
7928     
7929     /**
7930      * Validates a value according to the field's validation rules and marks the field as invalid
7931      * if the validation fails
7932      * @param {Mixed} value The value to validate
7933      * @return {Boolean} True if the value is valid, else false
7934      */
7935     validateValue : function(value){
7936         if(value.length < 1)  { // if it's blank
7937             if(this.allowBlank){
7938                 return true;
7939             }
7940             return false;
7941         }
7942         
7943         if(value.length < this.minLength){
7944             return false;
7945         }
7946         if(value.length > this.maxLength){
7947             return false;
7948         }
7949         if(this.vtype){
7950             var vt = Roo.form.VTypes;
7951             if(!vt[this.vtype](value, this)){
7952                 return false;
7953             }
7954         }
7955         if(typeof this.validator == "function"){
7956             var msg = this.validator(value);
7957             if(msg !== true){
7958                 return false;
7959             }
7960         }
7961         
7962         if(this.regex && !this.regex.test(value)){
7963             return false;
7964         }
7965         
7966         return true;
7967     },
7968
7969     
7970     
7971      // private
7972     fireKey : function(e){
7973         //Roo.log('field ' + e.getKey());
7974         if(e.isNavKeyPress()){
7975             this.fireEvent("specialkey", this, e);
7976         }
7977     },
7978     focus : function (selectText){
7979         if(this.rendered){
7980             this.inputEl().focus();
7981             if(selectText === true){
7982                 this.inputEl().dom.select();
7983             }
7984         }
7985         return this;
7986     } ,
7987     
7988     onFocus : function(){
7989         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7990            // this.el.addClass(this.focusClass);
7991         }
7992         if(!this.hasFocus){
7993             this.hasFocus = true;
7994             this.startValue = this.getValue();
7995             this.fireEvent("focus", this);
7996         }
7997     },
7998     
7999     beforeBlur : Roo.emptyFn,
8000
8001     
8002     // private
8003     onBlur : function(){
8004         this.beforeBlur();
8005         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8006             //this.el.removeClass(this.focusClass);
8007         }
8008         this.hasFocus = false;
8009         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8010             this.validate();
8011         }
8012         var v = this.getValue();
8013         if(String(v) !== String(this.startValue)){
8014             this.fireEvent('change', this, v, this.startValue);
8015         }
8016         this.fireEvent("blur", this);
8017     },
8018     
8019     /**
8020      * Resets the current field value to the originally loaded value and clears any validation messages
8021      */
8022     reset : function(){
8023         this.setValue(this.originalValue);
8024         this.validate();
8025     },
8026      /**
8027      * Returns the name of the field
8028      * @return {Mixed} name The name field
8029      */
8030     getName: function(){
8031         return this.name;
8032     },
8033      /**
8034      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8035      * @return {Mixed} value The field value
8036      */
8037     getValue : function(){
8038         
8039         var v = this.inputEl().getValue();
8040         
8041         return v;
8042     },
8043     /**
8044      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8045      * @return {Mixed} value The field value
8046      */
8047     getRawValue : function(){
8048         var v = this.inputEl().getValue();
8049         
8050         return v;
8051     },
8052     
8053     /**
8054      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8055      * @param {Mixed} value The value to set
8056      */
8057     setRawValue : function(v){
8058         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8059     },
8060     
8061     selectText : function(start, end){
8062         var v = this.getRawValue();
8063         if(v.length > 0){
8064             start = start === undefined ? 0 : start;
8065             end = end === undefined ? v.length : end;
8066             var d = this.inputEl().dom;
8067             if(d.setSelectionRange){
8068                 d.setSelectionRange(start, end);
8069             }else if(d.createTextRange){
8070                 var range = d.createTextRange();
8071                 range.moveStart("character", start);
8072                 range.moveEnd("character", v.length-end);
8073                 range.select();
8074             }
8075         }
8076     },
8077     
8078     /**
8079      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8080      * @param {Mixed} value The value to set
8081      */
8082     setValue : function(v){
8083         this.value = v;
8084         if(this.rendered){
8085             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8086             this.validate();
8087         }
8088     },
8089     
8090     /*
8091     processValue : function(value){
8092         if(this.stripCharsRe){
8093             var newValue = value.replace(this.stripCharsRe, '');
8094             if(newValue !== value){
8095                 this.setRawValue(newValue);
8096                 return newValue;
8097             }
8098         }
8099         return value;
8100     },
8101   */
8102     preFocus : function(){
8103         
8104         if(this.selectOnFocus){
8105             this.inputEl().dom.select();
8106         }
8107     },
8108     filterKeys : function(e){
8109         var k = e.getKey();
8110         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8111             return;
8112         }
8113         var c = e.getCharCode(), cc = String.fromCharCode(c);
8114         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8115             return;
8116         }
8117         if(!this.maskRe.test(cc)){
8118             e.stopEvent();
8119         }
8120     },
8121      /**
8122      * Clear any invalid styles/messages for this field
8123      */
8124     clearInvalid : function(){
8125         
8126         if(!this.el || this.preventMark){ // not rendered
8127             return;
8128         }
8129         this.el.removeClass(this.invalidClass);
8130         
8131         this.fireEvent('valid', this);
8132     },
8133     
8134      /**
8135      * Mark this field as valid
8136      */
8137     markValid : function(){
8138         if(!this.el  || this.preventMark){ // not rendered
8139             return;
8140         }
8141         
8142         this.el.removeClass([this.invalidClass, this.validClass]);
8143         
8144         if(this.disabled || this.allowBlank){
8145             return;
8146         }
8147         
8148         this.el.addClass(this.validClass);
8149         
8150         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8151             
8152             var feedback = this.el.select('.form-control-feedback', true).first();
8153             
8154             if(feedback){
8155                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8156                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8157             }
8158             
8159         }
8160         
8161         this.fireEvent('valid', this);
8162     },
8163     
8164      /**
8165      * Mark this field as invalid
8166      * @param {String} msg The validation message
8167      */
8168     markInvalid : function(msg){
8169         if(!this.el  || this.preventMark){ // not rendered
8170             return;
8171         }
8172         
8173         this.el.removeClass([this.invalidClass, this.validClass]);
8174         
8175         if(this.disabled || this.allowBlank){
8176             return;
8177         }
8178         
8179         this.el.addClass(this.invalidClass);
8180         
8181         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8182             
8183             var feedback = this.el.select('.form-control-feedback', true).first();
8184             
8185             if(feedback){
8186                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8187                 
8188                 if(this.getValue().length){
8189                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8190                 }
8191                 
8192             }
8193             
8194         }
8195         
8196         this.fireEvent('invalid', this, msg);
8197     },
8198     // private
8199     SafariOnKeyDown : function(event)
8200     {
8201         // this is a workaround for a password hang bug on chrome/ webkit.
8202         
8203         var isSelectAll = false;
8204         
8205         if(this.inputEl().dom.selectionEnd > 0){
8206             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8207         }
8208         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8209             event.preventDefault();
8210             this.setValue('');
8211             return;
8212         }
8213         
8214         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8215             
8216             event.preventDefault();
8217             // this is very hacky as keydown always get's upper case.
8218             //
8219             var cc = String.fromCharCode(event.getCharCode());
8220             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8221             
8222         }
8223     },
8224     adjustWidth : function(tag, w){
8225         tag = tag.toLowerCase();
8226         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8227             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8228                 if(tag == 'input'){
8229                     return w + 2;
8230                 }
8231                 if(tag == 'textarea'){
8232                     return w-2;
8233                 }
8234             }else if(Roo.isOpera){
8235                 if(tag == 'input'){
8236                     return w + 2;
8237                 }
8238                 if(tag == 'textarea'){
8239                     return w-2;
8240                 }
8241             }
8242         }
8243         return w;
8244     }
8245     
8246 });
8247
8248  
8249 /*
8250  * - LGPL
8251  *
8252  * Input
8253  * 
8254  */
8255
8256 /**
8257  * @class Roo.bootstrap.TextArea
8258  * @extends Roo.bootstrap.Input
8259  * Bootstrap TextArea class
8260  * @cfg {Number} cols Specifies the visible width of a text area
8261  * @cfg {Number} rows Specifies the visible number of lines in a text area
8262  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8263  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8264  * @cfg {string} html text
8265  * 
8266  * @constructor
8267  * Create a new TextArea
8268  * @param {Object} config The config object
8269  */
8270
8271 Roo.bootstrap.TextArea = function(config){
8272     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8273    
8274 };
8275
8276 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8277      
8278     cols : false,
8279     rows : 5,
8280     readOnly : false,
8281     warp : 'soft',
8282     resize : false,
8283     value: false,
8284     html: false,
8285     
8286     getAutoCreate : function(){
8287         
8288         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8289         
8290         var id = Roo.id();
8291         
8292         var cfg = {};
8293         
8294         var input =  {
8295             tag: 'textarea',
8296             id : id,
8297             warp : this.warp,
8298             rows : this.rows,
8299             value : this.value || '',
8300             html: this.html || '',
8301             cls : 'form-control',
8302             placeholder : this.placeholder || '' 
8303             
8304         };
8305         
8306         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8307             input.maxLength = this.maxLength;
8308         }
8309         
8310         if(this.resize){
8311             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8312         }
8313         
8314         if(this.cols){
8315             input.cols = this.cols;
8316         }
8317         
8318         if (this.readOnly) {
8319             input.readonly = true;
8320         }
8321         
8322         if (this.name) {
8323             input.name = this.name;
8324         }
8325         
8326         if (this.size) {
8327             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8328         }
8329         
8330         var settings=this;
8331         ['xs','sm','md','lg'].map(function(size){
8332             if (settings[size]) {
8333                 cfg.cls += ' col-' + size + '-' + settings[size];
8334             }
8335         });
8336         
8337         var inputblock = input;
8338         
8339         if(this.hasFeedback && !this.allowBlank){
8340             
8341             var feedback = {
8342                 tag: 'span',
8343                 cls: 'glyphicon form-control-feedback'
8344             };
8345
8346             inputblock = {
8347                 cls : 'has-feedback',
8348                 cn :  [
8349                     input,
8350                     feedback
8351                 ] 
8352             };  
8353         }
8354         
8355         
8356         if (this.before || this.after) {
8357             
8358             inputblock = {
8359                 cls : 'input-group',
8360                 cn :  [] 
8361             };
8362             if (this.before) {
8363                 inputblock.cn.push({
8364                     tag :'span',
8365                     cls : 'input-group-addon',
8366                     html : this.before
8367                 });
8368             }
8369             
8370             inputblock.cn.push(input);
8371             
8372             if(this.hasFeedback && !this.allowBlank){
8373                 inputblock.cls += ' has-feedback';
8374                 inputblock.cn.push(feedback);
8375             }
8376             
8377             if (this.after) {
8378                 inputblock.cn.push({
8379                     tag :'span',
8380                     cls : 'input-group-addon',
8381                     html : this.after
8382                 });
8383             }
8384             
8385         }
8386         
8387         if (align ==='left' && this.fieldLabel.length) {
8388                 Roo.log("left and has label");
8389                 cfg.cn = [
8390                     
8391                     {
8392                         tag: 'label',
8393                         'for' :  id,
8394                         cls : 'control-label col-sm-' + this.labelWidth,
8395                         html : this.fieldLabel
8396                         
8397                     },
8398                     {
8399                         cls : "col-sm-" + (12 - this.labelWidth), 
8400                         cn: [
8401                             inputblock
8402                         ]
8403                     }
8404                     
8405                 ];
8406         } else if ( this.fieldLabel.length) {
8407                 Roo.log(" label");
8408                  cfg.cn = [
8409                    
8410                     {
8411                         tag: 'label',
8412                         //cls : 'input-group-addon',
8413                         html : this.fieldLabel
8414                         
8415                     },
8416                     
8417                     inputblock
8418                     
8419                 ];
8420
8421         } else {
8422             
8423                    Roo.log(" no label && no align");
8424                 cfg.cn = [
8425                     
8426                         inputblock
8427                     
8428                 ];
8429                 
8430                 
8431         }
8432         
8433         if (this.disabled) {
8434             input.disabled=true;
8435         }
8436         
8437         return cfg;
8438         
8439     },
8440     /**
8441      * return the real textarea element.
8442      */
8443     inputEl: function ()
8444     {
8445         return this.el.select('textarea.form-control',true).first();
8446     }
8447 });
8448
8449  
8450 /*
8451  * - LGPL
8452  *
8453  * trigger field - base class for combo..
8454  * 
8455  */
8456  
8457 /**
8458  * @class Roo.bootstrap.TriggerField
8459  * @extends Roo.bootstrap.Input
8460  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8461  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8462  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8463  * for which you can provide a custom implementation.  For example:
8464  * <pre><code>
8465 var trigger = new Roo.bootstrap.TriggerField();
8466 trigger.onTriggerClick = myTriggerFn;
8467 trigger.applyTo('my-field');
8468 </code></pre>
8469  *
8470  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8471  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8472  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8473  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8474  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8475
8476  * @constructor
8477  * Create a new TriggerField.
8478  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8479  * to the base TextField)
8480  */
8481 Roo.bootstrap.TriggerField = function(config){
8482     this.mimicing = false;
8483     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8484 };
8485
8486 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8487     /**
8488      * @cfg {String} triggerClass A CSS class to apply to the trigger
8489      */
8490      /**
8491      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8492      */
8493     hideTrigger:false,
8494
8495     /**
8496      * @cfg {Boolean} removable (true|false) special filter default false
8497      */
8498     removable : false,
8499     
8500     /** @cfg {Boolean} grow @hide */
8501     /** @cfg {Number} growMin @hide */
8502     /** @cfg {Number} growMax @hide */
8503
8504     /**
8505      * @hide 
8506      * @method
8507      */
8508     autoSize: Roo.emptyFn,
8509     // private
8510     monitorTab : true,
8511     // private
8512     deferHeight : true,
8513
8514     
8515     actionMode : 'wrap',
8516     
8517     caret : false,
8518     
8519     
8520     getAutoCreate : function(){
8521        
8522         var align = this.labelAlign || this.parentLabelAlign();
8523         
8524         var id = Roo.id();
8525         
8526         var cfg = {
8527             cls: 'form-group' //input-group
8528         };
8529         
8530         
8531         var input =  {
8532             tag: 'input',
8533             id : id,
8534             type : this.inputType,
8535             cls : 'form-control',
8536             autocomplete: 'new-password',
8537             placeholder : this.placeholder || '' 
8538             
8539         };
8540         if (this.name) {
8541             input.name = this.name;
8542         }
8543         if (this.size) {
8544             input.cls += ' input-' + this.size;
8545         }
8546         
8547         if (this.disabled) {
8548             input.disabled=true;
8549         }
8550         
8551         var inputblock = input;
8552         
8553         if(this.hasFeedback && !this.allowBlank){
8554             
8555             var feedback = {
8556                 tag: 'span',
8557                 cls: 'glyphicon form-control-feedback'
8558             };
8559             
8560             if(this.removable && !this.editable && !this.tickable){
8561                 inputblock = {
8562                     cls : 'has-feedback',
8563                     cn :  [
8564                         inputblock,
8565                         {
8566                             tag: 'button',
8567                             html : 'x',
8568                             cls : 'roo-combo-removable-btn close'
8569                         },
8570                         feedback
8571                     ] 
8572                 };
8573             } else {
8574                 inputblock = {
8575                     cls : 'has-feedback',
8576                     cn :  [
8577                         inputblock,
8578                         feedback
8579                     ] 
8580                 };
8581             }
8582               
8583         } else {
8584             if(this.removable && !this.editable && !this.tickable){
8585                 inputblock = {
8586                     cls : 'roo-removable',
8587                     cn :  [
8588                         inputblock,
8589                         {
8590                             tag: 'button',
8591                             html : 'x',
8592                             cls : 'roo-combo-removable-btn close'
8593                         }
8594                     ] 
8595                 };
8596             }
8597         }
8598         
8599         if (this.before || this.after) {
8600             
8601             inputblock = {
8602                 cls : 'input-group',
8603                 cn :  [] 
8604             };
8605             if (this.before) {
8606                 inputblock.cn.push({
8607                     tag :'span',
8608                     cls : 'input-group-addon',
8609                     html : this.before
8610                 });
8611             }
8612             
8613             inputblock.cn.push(input);
8614             
8615             if(this.hasFeedback && !this.allowBlank){
8616                 inputblock.cls += ' has-feedback';
8617                 inputblock.cn.push(feedback);
8618             }
8619             
8620             if (this.after) {
8621                 inputblock.cn.push({
8622                     tag :'span',
8623                     cls : 'input-group-addon',
8624                     html : this.after
8625                 });
8626             }
8627             
8628         };
8629         
8630         var box = {
8631             tag: 'div',
8632             cn: [
8633                 {
8634                     tag: 'input',
8635                     type : 'hidden',
8636                     cls: 'form-hidden-field'
8637                 },
8638                 inputblock
8639             ]
8640             
8641         };
8642         
8643         if(this.multiple){
8644             Roo.log('multiple');
8645             
8646             box = {
8647                 tag: 'div',
8648                 cn: [
8649                     {
8650                         tag: 'input',
8651                         type : 'hidden',
8652                         cls: 'form-hidden-field'
8653                     },
8654                     {
8655                         tag: 'ul',
8656                         cls: 'select2-choices',
8657                         cn:[
8658                             {
8659                                 tag: 'li',
8660                                 cls: 'select2-search-field',
8661                                 cn: [
8662
8663                                     inputblock
8664                                 ]
8665                             }
8666                         ]
8667                     }
8668                 ]
8669             }
8670         };
8671         
8672         var combobox = {
8673             cls: 'select2-container input-group',
8674             cn: [
8675                 box
8676 //                {
8677 //                    tag: 'ul',
8678 //                    cls: 'typeahead typeahead-long dropdown-menu',
8679 //                    style: 'display:none'
8680 //                }
8681             ]
8682         };
8683         
8684         if(!this.multiple && this.showToggleBtn){
8685             
8686             var caret = {
8687                         tag: 'span',
8688                         cls: 'caret'
8689              };
8690             if (this.caret != false) {
8691                 caret = {
8692                      tag: 'i',
8693                      cls: 'fa fa-' + this.caret
8694                 };
8695                 
8696             }
8697             
8698             combobox.cn.push({
8699                 tag :'span',
8700                 cls : 'input-group-addon btn dropdown-toggle',
8701                 cn : [
8702                     caret,
8703                     {
8704                         tag: 'span',
8705                         cls: 'combobox-clear',
8706                         cn  : [
8707                             {
8708                                 tag : 'i',
8709                                 cls: 'icon-remove'
8710                             }
8711                         ]
8712                     }
8713                 ]
8714
8715             })
8716         }
8717         
8718         if(this.multiple){
8719             combobox.cls += ' select2-container-multi';
8720         }
8721         
8722         if (align ==='left' && this.fieldLabel.length) {
8723             
8724                 Roo.log("left and has label");
8725                 cfg.cn = [
8726                     
8727                     {
8728                         tag: 'label',
8729                         'for' :  id,
8730                         cls : 'control-label col-sm-' + this.labelWidth,
8731                         html : this.fieldLabel
8732                         
8733                     },
8734                     {
8735                         cls : "col-sm-" + (12 - this.labelWidth), 
8736                         cn: [
8737                             combobox
8738                         ]
8739                     }
8740                     
8741                 ];
8742         } else if ( this.fieldLabel.length) {
8743                 Roo.log(" label");
8744                  cfg.cn = [
8745                    
8746                     {
8747                         tag: 'label',
8748                         //cls : 'input-group-addon',
8749                         html : this.fieldLabel
8750                         
8751                     },
8752                     
8753                     combobox
8754                     
8755                 ];
8756
8757         } else {
8758             
8759                 Roo.log(" no label && no align");
8760                 cfg = combobox
8761                      
8762                 
8763         }
8764          
8765         var settings=this;
8766         ['xs','sm','md','lg'].map(function(size){
8767             if (settings[size]) {
8768                 cfg.cls += ' col-' + size + '-' + settings[size];
8769             }
8770         });
8771         
8772         return cfg;
8773         
8774     },
8775     
8776     
8777     
8778     // private
8779     onResize : function(w, h){
8780 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8781 //        if(typeof w == 'number'){
8782 //            var x = w - this.trigger.getWidth();
8783 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8784 //            this.trigger.setStyle('left', x+'px');
8785 //        }
8786     },
8787
8788     // private
8789     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8790
8791     // private
8792     getResizeEl : function(){
8793         return this.inputEl();
8794     },
8795
8796     // private
8797     getPositionEl : function(){
8798         return this.inputEl();
8799     },
8800
8801     // private
8802     alignErrorIcon : function(){
8803         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8804     },
8805
8806     // private
8807     initEvents : function(){
8808         
8809         this.createList();
8810         
8811         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8812         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8813         if(!this.multiple && this.showToggleBtn){
8814             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8815             if(this.hideTrigger){
8816                 this.trigger.setDisplayed(false);
8817             }
8818             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8819         }
8820         
8821         if(this.multiple){
8822             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8823         }
8824         
8825         if(this.removable && !this.editable && !this.tickable){
8826             var close = this.closeTriggerEl();
8827             
8828             if(close){
8829                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8830                 close.on('click', this.removeBtnClick, this, close);
8831             }
8832         }
8833         
8834         //this.trigger.addClassOnOver('x-form-trigger-over');
8835         //this.trigger.addClassOnClick('x-form-trigger-click');
8836         
8837         //if(!this.width){
8838         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8839         //}
8840     },
8841     
8842     closeTriggerEl : function()
8843     {
8844         var close = this.el.select('.roo-combo-removable-btn', true).first();
8845         return close ? close : false;
8846     },
8847     
8848     removeBtnClick : function(e, h, el)
8849     {
8850         e.preventDefault();
8851         
8852         this.fireEvent("remove", this);
8853     },
8854     
8855     createList : function()
8856     {
8857         this.list = Roo.get(document.body).createChild({
8858             tag: 'ul',
8859             cls: 'typeahead typeahead-long dropdown-menu',
8860             style: 'display:none'
8861         });
8862         
8863         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8864         
8865     },
8866
8867     // private
8868     initTrigger : function(){
8869        
8870     },
8871
8872     // private
8873     onDestroy : function(){
8874         if(this.trigger){
8875             this.trigger.removeAllListeners();
8876           //  this.trigger.remove();
8877         }
8878         //if(this.wrap){
8879         //    this.wrap.remove();
8880         //}
8881         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8882     },
8883
8884     // private
8885     onFocus : function(){
8886         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8887         /*
8888         if(!this.mimicing){
8889             this.wrap.addClass('x-trigger-wrap-focus');
8890             this.mimicing = true;
8891             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8892             if(this.monitorTab){
8893                 this.el.on("keydown", this.checkTab, this);
8894             }
8895         }
8896         */
8897     },
8898
8899     // private
8900     checkTab : function(e){
8901         if(e.getKey() == e.TAB){
8902             this.triggerBlur();
8903         }
8904     },
8905
8906     // private
8907     onBlur : function(){
8908         // do nothing
8909     },
8910
8911     // private
8912     mimicBlur : function(e, t){
8913         /*
8914         if(!this.wrap.contains(t) && this.validateBlur()){
8915             this.triggerBlur();
8916         }
8917         */
8918     },
8919
8920     // private
8921     triggerBlur : function(){
8922         this.mimicing = false;
8923         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8924         if(this.monitorTab){
8925             this.el.un("keydown", this.checkTab, this);
8926         }
8927         //this.wrap.removeClass('x-trigger-wrap-focus');
8928         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8929     },
8930
8931     // private
8932     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8933     validateBlur : function(e, t){
8934         return true;
8935     },
8936
8937     // private
8938     onDisable : function(){
8939         this.inputEl().dom.disabled = true;
8940         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8941         //if(this.wrap){
8942         //    this.wrap.addClass('x-item-disabled');
8943         //}
8944     },
8945
8946     // private
8947     onEnable : function(){
8948         this.inputEl().dom.disabled = false;
8949         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8950         //if(this.wrap){
8951         //    this.el.removeClass('x-item-disabled');
8952         //}
8953     },
8954
8955     // private
8956     onShow : function(){
8957         var ae = this.getActionEl();
8958         
8959         if(ae){
8960             ae.dom.style.display = '';
8961             ae.dom.style.visibility = 'visible';
8962         }
8963     },
8964
8965     // private
8966     
8967     onHide : function(){
8968         var ae = this.getActionEl();
8969         ae.dom.style.display = 'none';
8970     },
8971
8972     /**
8973      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8974      * by an implementing function.
8975      * @method
8976      * @param {EventObject} e
8977      */
8978     onTriggerClick : Roo.emptyFn
8979 });
8980  /*
8981  * Based on:
8982  * Ext JS Library 1.1.1
8983  * Copyright(c) 2006-2007, Ext JS, LLC.
8984  *
8985  * Originally Released Under LGPL - original licence link has changed is not relivant.
8986  *
8987  * Fork - LGPL
8988  * <script type="text/javascript">
8989  */
8990
8991
8992 /**
8993  * @class Roo.data.SortTypes
8994  * @singleton
8995  * Defines the default sorting (casting?) comparison functions used when sorting data.
8996  */
8997 Roo.data.SortTypes = {
8998     /**
8999      * Default sort that does nothing
9000      * @param {Mixed} s The value being converted
9001      * @return {Mixed} The comparison value
9002      */
9003     none : function(s){
9004         return s;
9005     },
9006     
9007     /**
9008      * The regular expression used to strip tags
9009      * @type {RegExp}
9010      * @property
9011      */
9012     stripTagsRE : /<\/?[^>]+>/gi,
9013     
9014     /**
9015      * Strips all HTML tags to sort on text only
9016      * @param {Mixed} s The value being converted
9017      * @return {String} The comparison value
9018      */
9019     asText : function(s){
9020         return String(s).replace(this.stripTagsRE, "");
9021     },
9022     
9023     /**
9024      * Strips all HTML tags to sort on text only - Case insensitive
9025      * @param {Mixed} s The value being converted
9026      * @return {String} The comparison value
9027      */
9028     asUCText : function(s){
9029         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9030     },
9031     
9032     /**
9033      * Case insensitive string
9034      * @param {Mixed} s The value being converted
9035      * @return {String} The comparison value
9036      */
9037     asUCString : function(s) {
9038         return String(s).toUpperCase();
9039     },
9040     
9041     /**
9042      * Date sorting
9043      * @param {Mixed} s The value being converted
9044      * @return {Number} The comparison value
9045      */
9046     asDate : function(s) {
9047         if(!s){
9048             return 0;
9049         }
9050         if(s instanceof Date){
9051             return s.getTime();
9052         }
9053         return Date.parse(String(s));
9054     },
9055     
9056     /**
9057      * Float sorting
9058      * @param {Mixed} s The value being converted
9059      * @return {Float} The comparison value
9060      */
9061     asFloat : function(s) {
9062         var val = parseFloat(String(s).replace(/,/g, ""));
9063         if(isNaN(val)) val = 0;
9064         return val;
9065     },
9066     
9067     /**
9068      * Integer sorting
9069      * @param {Mixed} s The value being converted
9070      * @return {Number} The comparison value
9071      */
9072     asInt : function(s) {
9073         var val = parseInt(String(s).replace(/,/g, ""));
9074         if(isNaN(val)) val = 0;
9075         return val;
9076     }
9077 };/*
9078  * Based on:
9079  * Ext JS Library 1.1.1
9080  * Copyright(c) 2006-2007, Ext JS, LLC.
9081  *
9082  * Originally Released Under LGPL - original licence link has changed is not relivant.
9083  *
9084  * Fork - LGPL
9085  * <script type="text/javascript">
9086  */
9087
9088 /**
9089 * @class Roo.data.Record
9090  * Instances of this class encapsulate both record <em>definition</em> information, and record
9091  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9092  * to access Records cached in an {@link Roo.data.Store} object.<br>
9093  * <p>
9094  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9095  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9096  * objects.<br>
9097  * <p>
9098  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9099  * @constructor
9100  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9101  * {@link #create}. The parameters are the same.
9102  * @param {Array} data An associative Array of data values keyed by the field name.
9103  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9104  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9105  * not specified an integer id is generated.
9106  */
9107 Roo.data.Record = function(data, id){
9108     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9109     this.data = data;
9110 };
9111
9112 /**
9113  * Generate a constructor for a specific record layout.
9114  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9115  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9116  * Each field definition object may contain the following properties: <ul>
9117  * <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,
9118  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9119  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9120  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9121  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9122  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9123  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9124  * this may be omitted.</p></li>
9125  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9126  * <ul><li>auto (Default, implies no conversion)</li>
9127  * <li>string</li>
9128  * <li>int</li>
9129  * <li>float</li>
9130  * <li>boolean</li>
9131  * <li>date</li></ul></p></li>
9132  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9133  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9134  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9135  * by the Reader into an object that will be stored in the Record. It is passed the
9136  * following parameters:<ul>
9137  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9138  * </ul></p></li>
9139  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9140  * </ul>
9141  * <br>usage:<br><pre><code>
9142 var TopicRecord = Roo.data.Record.create(
9143     {name: 'title', mapping: 'topic_title'},
9144     {name: 'author', mapping: 'username'},
9145     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9146     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9147     {name: 'lastPoster', mapping: 'user2'},
9148     {name: 'excerpt', mapping: 'post_text'}
9149 );
9150
9151 var myNewRecord = new TopicRecord({
9152     title: 'Do my job please',
9153     author: 'noobie',
9154     totalPosts: 1,
9155     lastPost: new Date(),
9156     lastPoster: 'Animal',
9157     excerpt: 'No way dude!'
9158 });
9159 myStore.add(myNewRecord);
9160 </code></pre>
9161  * @method create
9162  * @static
9163  */
9164 Roo.data.Record.create = function(o){
9165     var f = function(){
9166         f.superclass.constructor.apply(this, arguments);
9167     };
9168     Roo.extend(f, Roo.data.Record);
9169     var p = f.prototype;
9170     p.fields = new Roo.util.MixedCollection(false, function(field){
9171         return field.name;
9172     });
9173     for(var i = 0, len = o.length; i < len; i++){
9174         p.fields.add(new Roo.data.Field(o[i]));
9175     }
9176     f.getField = function(name){
9177         return p.fields.get(name);  
9178     };
9179     return f;
9180 };
9181
9182 Roo.data.Record.AUTO_ID = 1000;
9183 Roo.data.Record.EDIT = 'edit';
9184 Roo.data.Record.REJECT = 'reject';
9185 Roo.data.Record.COMMIT = 'commit';
9186
9187 Roo.data.Record.prototype = {
9188     /**
9189      * Readonly flag - true if this record has been modified.
9190      * @type Boolean
9191      */
9192     dirty : false,
9193     editing : false,
9194     error: null,
9195     modified: null,
9196
9197     // private
9198     join : function(store){
9199         this.store = store;
9200     },
9201
9202     /**
9203      * Set the named field to the specified value.
9204      * @param {String} name The name of the field to set.
9205      * @param {Object} value The value to set the field to.
9206      */
9207     set : function(name, value){
9208         if(this.data[name] == value){
9209             return;
9210         }
9211         this.dirty = true;
9212         if(!this.modified){
9213             this.modified = {};
9214         }
9215         if(typeof this.modified[name] == 'undefined'){
9216             this.modified[name] = this.data[name];
9217         }
9218         this.data[name] = value;
9219         if(!this.editing && this.store){
9220             this.store.afterEdit(this);
9221         }       
9222     },
9223
9224     /**
9225      * Get the value of the named field.
9226      * @param {String} name The name of the field to get the value of.
9227      * @return {Object} The value of the field.
9228      */
9229     get : function(name){
9230         return this.data[name]; 
9231     },
9232
9233     // private
9234     beginEdit : function(){
9235         this.editing = true;
9236         this.modified = {}; 
9237     },
9238
9239     // private
9240     cancelEdit : function(){
9241         this.editing = false;
9242         delete this.modified;
9243     },
9244
9245     // private
9246     endEdit : function(){
9247         this.editing = false;
9248         if(this.dirty && this.store){
9249             this.store.afterEdit(this);
9250         }
9251     },
9252
9253     /**
9254      * Usually called by the {@link Roo.data.Store} which owns the Record.
9255      * Rejects all changes made to the Record since either creation, or the last commit operation.
9256      * Modified fields are reverted to their original values.
9257      * <p>
9258      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9259      * of reject operations.
9260      */
9261     reject : function(){
9262         var m = this.modified;
9263         for(var n in m){
9264             if(typeof m[n] != "function"){
9265                 this.data[n] = m[n];
9266             }
9267         }
9268         this.dirty = false;
9269         delete this.modified;
9270         this.editing = false;
9271         if(this.store){
9272             this.store.afterReject(this);
9273         }
9274     },
9275
9276     /**
9277      * Usually called by the {@link Roo.data.Store} which owns the Record.
9278      * Commits all changes made to the Record since either creation, or the last commit operation.
9279      * <p>
9280      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9281      * of commit operations.
9282      */
9283     commit : function(){
9284         this.dirty = false;
9285         delete this.modified;
9286         this.editing = false;
9287         if(this.store){
9288             this.store.afterCommit(this);
9289         }
9290     },
9291
9292     // private
9293     hasError : function(){
9294         return this.error != null;
9295     },
9296
9297     // private
9298     clearError : function(){
9299         this.error = null;
9300     },
9301
9302     /**
9303      * Creates a copy of this record.
9304      * @param {String} id (optional) A new record id if you don't want to use this record's id
9305      * @return {Record}
9306      */
9307     copy : function(newId) {
9308         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9309     }
9310 };/*
9311  * Based on:
9312  * Ext JS Library 1.1.1
9313  * Copyright(c) 2006-2007, Ext JS, LLC.
9314  *
9315  * Originally Released Under LGPL - original licence link has changed is not relivant.
9316  *
9317  * Fork - LGPL
9318  * <script type="text/javascript">
9319  */
9320
9321
9322
9323 /**
9324  * @class Roo.data.Store
9325  * @extends Roo.util.Observable
9326  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9327  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9328  * <p>
9329  * 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
9330  * has no knowledge of the format of the data returned by the Proxy.<br>
9331  * <p>
9332  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9333  * instances from the data object. These records are cached and made available through accessor functions.
9334  * @constructor
9335  * Creates a new Store.
9336  * @param {Object} config A config object containing the objects needed for the Store to access data,
9337  * and read the data into Records.
9338  */
9339 Roo.data.Store = function(config){
9340     this.data = new Roo.util.MixedCollection(false);
9341     this.data.getKey = function(o){
9342         return o.id;
9343     };
9344     this.baseParams = {};
9345     // private
9346     this.paramNames = {
9347         "start" : "start",
9348         "limit" : "limit",
9349         "sort" : "sort",
9350         "dir" : "dir",
9351         "multisort" : "_multisort"
9352     };
9353
9354     if(config && config.data){
9355         this.inlineData = config.data;
9356         delete config.data;
9357     }
9358
9359     Roo.apply(this, config);
9360     
9361     if(this.reader){ // reader passed
9362         this.reader = Roo.factory(this.reader, Roo.data);
9363         this.reader.xmodule = this.xmodule || false;
9364         if(!this.recordType){
9365             this.recordType = this.reader.recordType;
9366         }
9367         if(this.reader.onMetaChange){
9368             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9369         }
9370     }
9371
9372     if(this.recordType){
9373         this.fields = this.recordType.prototype.fields;
9374     }
9375     this.modified = [];
9376
9377     this.addEvents({
9378         /**
9379          * @event datachanged
9380          * Fires when the data cache has changed, and a widget which is using this Store
9381          * as a Record cache should refresh its view.
9382          * @param {Store} this
9383          */
9384         datachanged : true,
9385         /**
9386          * @event metachange
9387          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9388          * @param {Store} this
9389          * @param {Object} meta The JSON metadata
9390          */
9391         metachange : true,
9392         /**
9393          * @event add
9394          * Fires when Records have been added to the Store
9395          * @param {Store} this
9396          * @param {Roo.data.Record[]} records The array of Records added
9397          * @param {Number} index The index at which the record(s) were added
9398          */
9399         add : true,
9400         /**
9401          * @event remove
9402          * Fires when a Record has been removed from the Store
9403          * @param {Store} this
9404          * @param {Roo.data.Record} record The Record that was removed
9405          * @param {Number} index The index at which the record was removed
9406          */
9407         remove : true,
9408         /**
9409          * @event update
9410          * Fires when a Record has been updated
9411          * @param {Store} this
9412          * @param {Roo.data.Record} record The Record that was updated
9413          * @param {String} operation The update operation being performed.  Value may be one of:
9414          * <pre><code>
9415  Roo.data.Record.EDIT
9416  Roo.data.Record.REJECT
9417  Roo.data.Record.COMMIT
9418          * </code></pre>
9419          */
9420         update : true,
9421         /**
9422          * @event clear
9423          * Fires when the data cache has been cleared.
9424          * @param {Store} this
9425          */
9426         clear : true,
9427         /**
9428          * @event beforeload
9429          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9430          * the load action will be canceled.
9431          * @param {Store} this
9432          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9433          */
9434         beforeload : true,
9435         /**
9436          * @event beforeloadadd
9437          * Fires after a new set of Records has been loaded.
9438          * @param {Store} this
9439          * @param {Roo.data.Record[]} records The Records that were loaded
9440          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9441          */
9442         beforeloadadd : true,
9443         /**
9444          * @event load
9445          * Fires after a new set of Records has been loaded, before they are added to the store.
9446          * @param {Store} this
9447          * @param {Roo.data.Record[]} records The Records that were loaded
9448          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9449          * @params {Object} return from reader
9450          */
9451         load : true,
9452         /**
9453          * @event loadexception
9454          * Fires if an exception occurs in the Proxy during loading.
9455          * Called with the signature of the Proxy's "loadexception" event.
9456          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9457          * 
9458          * @param {Proxy} 
9459          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9460          * @param {Object} load options 
9461          * @param {Object} jsonData from your request (normally this contains the Exception)
9462          */
9463         loadexception : true
9464     });
9465     
9466     if(this.proxy){
9467         this.proxy = Roo.factory(this.proxy, Roo.data);
9468         this.proxy.xmodule = this.xmodule || false;
9469         this.relayEvents(this.proxy,  ["loadexception"]);
9470     }
9471     this.sortToggle = {};
9472     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9473
9474     Roo.data.Store.superclass.constructor.call(this);
9475
9476     if(this.inlineData){
9477         this.loadData(this.inlineData);
9478         delete this.inlineData;
9479     }
9480 };
9481
9482 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9483      /**
9484     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9485     * without a remote query - used by combo/forms at present.
9486     */
9487     
9488     /**
9489     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9490     */
9491     /**
9492     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9493     */
9494     /**
9495     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9496     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9497     */
9498     /**
9499     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9500     * on any HTTP request
9501     */
9502     /**
9503     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9504     */
9505     /**
9506     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9507     */
9508     multiSort: false,
9509     /**
9510     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9511     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9512     */
9513     remoteSort : false,
9514
9515     /**
9516     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9517      * loaded or when a record is removed. (defaults to false).
9518     */
9519     pruneModifiedRecords : false,
9520
9521     // private
9522     lastOptions : null,
9523
9524     /**
9525      * Add Records to the Store and fires the add event.
9526      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9527      */
9528     add : function(records){
9529         records = [].concat(records);
9530         for(var i = 0, len = records.length; i < len; i++){
9531             records[i].join(this);
9532         }
9533         var index = this.data.length;
9534         this.data.addAll(records);
9535         this.fireEvent("add", this, records, index);
9536     },
9537
9538     /**
9539      * Remove a Record from the Store and fires the remove event.
9540      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9541      */
9542     remove : function(record){
9543         var index = this.data.indexOf(record);
9544         this.data.removeAt(index);
9545         if(this.pruneModifiedRecords){
9546             this.modified.remove(record);
9547         }
9548         this.fireEvent("remove", this, record, index);
9549     },
9550
9551     /**
9552      * Remove all Records from the Store and fires the clear event.
9553      */
9554     removeAll : function(){
9555         this.data.clear();
9556         if(this.pruneModifiedRecords){
9557             this.modified = [];
9558         }
9559         this.fireEvent("clear", this);
9560     },
9561
9562     /**
9563      * Inserts Records to the Store at the given index and fires the add event.
9564      * @param {Number} index The start index at which to insert the passed Records.
9565      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9566      */
9567     insert : function(index, records){
9568         records = [].concat(records);
9569         for(var i = 0, len = records.length; i < len; i++){
9570             this.data.insert(index, records[i]);
9571             records[i].join(this);
9572         }
9573         this.fireEvent("add", this, records, index);
9574     },
9575
9576     /**
9577      * Get the index within the cache of the passed Record.
9578      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9579      * @return {Number} The index of the passed Record. Returns -1 if not found.
9580      */
9581     indexOf : function(record){
9582         return this.data.indexOf(record);
9583     },
9584
9585     /**
9586      * Get the index within the cache of the Record with the passed id.
9587      * @param {String} id The id of the Record to find.
9588      * @return {Number} The index of the Record. Returns -1 if not found.
9589      */
9590     indexOfId : function(id){
9591         return this.data.indexOfKey(id);
9592     },
9593
9594     /**
9595      * Get the Record with the specified id.
9596      * @param {String} id The id of the Record to find.
9597      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9598      */
9599     getById : function(id){
9600         return this.data.key(id);
9601     },
9602
9603     /**
9604      * Get the Record at the specified index.
9605      * @param {Number} index The index of the Record to find.
9606      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9607      */
9608     getAt : function(index){
9609         return this.data.itemAt(index);
9610     },
9611
9612     /**
9613      * Returns a range of Records between specified indices.
9614      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9615      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9616      * @return {Roo.data.Record[]} An array of Records
9617      */
9618     getRange : function(start, end){
9619         return this.data.getRange(start, end);
9620     },
9621
9622     // private
9623     storeOptions : function(o){
9624         o = Roo.apply({}, o);
9625         delete o.callback;
9626         delete o.scope;
9627         this.lastOptions = o;
9628     },
9629
9630     /**
9631      * Loads the Record cache from the configured Proxy using the configured Reader.
9632      * <p>
9633      * If using remote paging, then the first load call must specify the <em>start</em>
9634      * and <em>limit</em> properties in the options.params property to establish the initial
9635      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9636      * <p>
9637      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9638      * and this call will return before the new data has been loaded. Perform any post-processing
9639      * in a callback function, or in a "load" event handler.</strong>
9640      * <p>
9641      * @param {Object} options An object containing properties which control loading options:<ul>
9642      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9643      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9644      * passed the following arguments:<ul>
9645      * <li>r : Roo.data.Record[]</li>
9646      * <li>options: Options object from the load call</li>
9647      * <li>success: Boolean success indicator</li></ul></li>
9648      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9649      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9650      * </ul>
9651      */
9652     load : function(options){
9653         options = options || {};
9654         if(this.fireEvent("beforeload", this, options) !== false){
9655             this.storeOptions(options);
9656             var p = Roo.apply(options.params || {}, this.baseParams);
9657             // if meta was not loaded from remote source.. try requesting it.
9658             if (!this.reader.metaFromRemote) {
9659                 p._requestMeta = 1;
9660             }
9661             if(this.sortInfo && this.remoteSort){
9662                 var pn = this.paramNames;
9663                 p[pn["sort"]] = this.sortInfo.field;
9664                 p[pn["dir"]] = this.sortInfo.direction;
9665             }
9666             if (this.multiSort) {
9667                 var pn = this.paramNames;
9668                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9669             }
9670             
9671             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9672         }
9673     },
9674
9675     /**
9676      * Reloads the Record cache from the configured Proxy using the configured Reader and
9677      * the options from the last load operation performed.
9678      * @param {Object} options (optional) An object containing properties which may override the options
9679      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9680      * the most recently used options are reused).
9681      */
9682     reload : function(options){
9683         this.load(Roo.applyIf(options||{}, this.lastOptions));
9684     },
9685
9686     // private
9687     // Called as a callback by the Reader during a load operation.
9688     loadRecords : function(o, options, success){
9689         if(!o || success === false){
9690             if(success !== false){
9691                 this.fireEvent("load", this, [], options, o);
9692             }
9693             if(options.callback){
9694                 options.callback.call(options.scope || this, [], options, false);
9695             }
9696             return;
9697         }
9698         // if data returned failure - throw an exception.
9699         if (o.success === false) {
9700             // show a message if no listener is registered.
9701             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9702                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9703             }
9704             // loadmask wil be hooked into this..
9705             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9706             return;
9707         }
9708         var r = o.records, t = o.totalRecords || r.length;
9709         
9710         this.fireEvent("beforeloadadd", this, r, options, o);
9711         
9712         if(!options || options.add !== true){
9713             if(this.pruneModifiedRecords){
9714                 this.modified = [];
9715             }
9716             for(var i = 0, len = r.length; i < len; i++){
9717                 r[i].join(this);
9718             }
9719             if(this.snapshot){
9720                 this.data = this.snapshot;
9721                 delete this.snapshot;
9722             }
9723             this.data.clear();
9724             this.data.addAll(r);
9725             this.totalLength = t;
9726             this.applySort();
9727             this.fireEvent("datachanged", this);
9728         }else{
9729             this.totalLength = Math.max(t, this.data.length+r.length);
9730             this.add(r);
9731         }
9732         this.fireEvent("load", this, r, options, o);
9733         if(options.callback){
9734             options.callback.call(options.scope || this, r, options, true);
9735         }
9736     },
9737
9738
9739     /**
9740      * Loads data from a passed data block. A Reader which understands the format of the data
9741      * must have been configured in the constructor.
9742      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9743      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9744      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9745      */
9746     loadData : function(o, append){
9747         var r = this.reader.readRecords(o);
9748         this.loadRecords(r, {add: append}, true);
9749     },
9750
9751     /**
9752      * Gets the number of cached records.
9753      * <p>
9754      * <em>If using paging, this may not be the total size of the dataset. If the data object
9755      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9756      * the data set size</em>
9757      */
9758     getCount : function(){
9759         return this.data.length || 0;
9760     },
9761
9762     /**
9763      * Gets the total number of records in the dataset as returned by the server.
9764      * <p>
9765      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9766      * the dataset size</em>
9767      */
9768     getTotalCount : function(){
9769         return this.totalLength || 0;
9770     },
9771
9772     /**
9773      * Returns the sort state of the Store as an object with two properties:
9774      * <pre><code>
9775  field {String} The name of the field by which the Records are sorted
9776  direction {String} The sort order, "ASC" or "DESC"
9777      * </code></pre>
9778      */
9779     getSortState : function(){
9780         return this.sortInfo;
9781     },
9782
9783     // private
9784     applySort : function(){
9785         if(this.sortInfo && !this.remoteSort){
9786             var s = this.sortInfo, f = s.field;
9787             var st = this.fields.get(f).sortType;
9788             var fn = function(r1, r2){
9789                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9790                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9791             };
9792             this.data.sort(s.direction, fn);
9793             if(this.snapshot && this.snapshot != this.data){
9794                 this.snapshot.sort(s.direction, fn);
9795             }
9796         }
9797     },
9798
9799     /**
9800      * Sets the default sort column and order to be used by the next load operation.
9801      * @param {String} fieldName The name of the field to sort by.
9802      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9803      */
9804     setDefaultSort : function(field, dir){
9805         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9806     },
9807
9808     /**
9809      * Sort the Records.
9810      * If remote sorting is used, the sort is performed on the server, and the cache is
9811      * reloaded. If local sorting is used, the cache is sorted internally.
9812      * @param {String} fieldName The name of the field to sort by.
9813      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9814      */
9815     sort : function(fieldName, dir){
9816         var f = this.fields.get(fieldName);
9817         if(!dir){
9818             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9819             
9820             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9821                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9822             }else{
9823                 dir = f.sortDir;
9824             }
9825         }
9826         this.sortToggle[f.name] = dir;
9827         this.sortInfo = {field: f.name, direction: dir};
9828         if(!this.remoteSort){
9829             this.applySort();
9830             this.fireEvent("datachanged", this);
9831         }else{
9832             this.load(this.lastOptions);
9833         }
9834     },
9835
9836     /**
9837      * Calls the specified function for each of the Records in the cache.
9838      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9839      * Returning <em>false</em> aborts and exits the iteration.
9840      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9841      */
9842     each : function(fn, scope){
9843         this.data.each(fn, scope);
9844     },
9845
9846     /**
9847      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9848      * (e.g., during paging).
9849      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9850      */
9851     getModifiedRecords : function(){
9852         return this.modified;
9853     },
9854
9855     // private
9856     createFilterFn : function(property, value, anyMatch){
9857         if(!value.exec){ // not a regex
9858             value = String(value);
9859             if(value.length == 0){
9860                 return false;
9861             }
9862             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9863         }
9864         return function(r){
9865             return value.test(r.data[property]);
9866         };
9867     },
9868
9869     /**
9870      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9871      * @param {String} property A field on your records
9872      * @param {Number} start The record index to start at (defaults to 0)
9873      * @param {Number} end The last record index to include (defaults to length - 1)
9874      * @return {Number} The sum
9875      */
9876     sum : function(property, start, end){
9877         var rs = this.data.items, v = 0;
9878         start = start || 0;
9879         end = (end || end === 0) ? end : rs.length-1;
9880
9881         for(var i = start; i <= end; i++){
9882             v += (rs[i].data[property] || 0);
9883         }
9884         return v;
9885     },
9886
9887     /**
9888      * Filter the records by a specified property.
9889      * @param {String} field A field on your records
9890      * @param {String/RegExp} value Either a string that the field
9891      * should start with or a RegExp to test against the field
9892      * @param {Boolean} anyMatch True to match any part not just the beginning
9893      */
9894     filter : function(property, value, anyMatch){
9895         var fn = this.createFilterFn(property, value, anyMatch);
9896         return fn ? this.filterBy(fn) : this.clearFilter();
9897     },
9898
9899     /**
9900      * Filter by a function. The specified function will be called with each
9901      * record in this data source. If the function returns true the record is included,
9902      * otherwise it is filtered.
9903      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9904      * @param {Object} scope (optional) The scope of the function (defaults to this)
9905      */
9906     filterBy : function(fn, scope){
9907         this.snapshot = this.snapshot || this.data;
9908         this.data = this.queryBy(fn, scope||this);
9909         this.fireEvent("datachanged", this);
9910     },
9911
9912     /**
9913      * Query the records by a specified property.
9914      * @param {String} field A field on your records
9915      * @param {String/RegExp} value Either a string that the field
9916      * should start with or a RegExp to test against the field
9917      * @param {Boolean} anyMatch True to match any part not just the beginning
9918      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9919      */
9920     query : function(property, value, anyMatch){
9921         var fn = this.createFilterFn(property, value, anyMatch);
9922         return fn ? this.queryBy(fn) : this.data.clone();
9923     },
9924
9925     /**
9926      * Query by a function. The specified function will be called with each
9927      * record in this data source. If the function returns true the record is included
9928      * in the results.
9929      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9930      * @param {Object} scope (optional) The scope of the function (defaults to this)
9931       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9932      **/
9933     queryBy : function(fn, scope){
9934         var data = this.snapshot || this.data;
9935         return data.filterBy(fn, scope||this);
9936     },
9937
9938     /**
9939      * Collects unique values for a particular dataIndex from this store.
9940      * @param {String} dataIndex The property to collect
9941      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9942      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9943      * @return {Array} An array of the unique values
9944      **/
9945     collect : function(dataIndex, allowNull, bypassFilter){
9946         var d = (bypassFilter === true && this.snapshot) ?
9947                 this.snapshot.items : this.data.items;
9948         var v, sv, r = [], l = {};
9949         for(var i = 0, len = d.length; i < len; i++){
9950             v = d[i].data[dataIndex];
9951             sv = String(v);
9952             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9953                 l[sv] = true;
9954                 r[r.length] = v;
9955             }
9956         }
9957         return r;
9958     },
9959
9960     /**
9961      * Revert to a view of the Record cache with no filtering applied.
9962      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9963      */
9964     clearFilter : function(suppressEvent){
9965         if(this.snapshot && this.snapshot != this.data){
9966             this.data = this.snapshot;
9967             delete this.snapshot;
9968             if(suppressEvent !== true){
9969                 this.fireEvent("datachanged", this);
9970             }
9971         }
9972     },
9973
9974     // private
9975     afterEdit : function(record){
9976         if(this.modified.indexOf(record) == -1){
9977             this.modified.push(record);
9978         }
9979         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9980     },
9981     
9982     // private
9983     afterReject : function(record){
9984         this.modified.remove(record);
9985         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9986     },
9987
9988     // private
9989     afterCommit : function(record){
9990         this.modified.remove(record);
9991         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9992     },
9993
9994     /**
9995      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9996      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9997      */
9998     commitChanges : function(){
9999         var m = this.modified.slice(0);
10000         this.modified = [];
10001         for(var i = 0, len = m.length; i < len; i++){
10002             m[i].commit();
10003         }
10004     },
10005
10006     /**
10007      * Cancel outstanding changes on all changed records.
10008      */
10009     rejectChanges : function(){
10010         var m = this.modified.slice(0);
10011         this.modified = [];
10012         for(var i = 0, len = m.length; i < len; i++){
10013             m[i].reject();
10014         }
10015     },
10016
10017     onMetaChange : function(meta, rtype, o){
10018         this.recordType = rtype;
10019         this.fields = rtype.prototype.fields;
10020         delete this.snapshot;
10021         this.sortInfo = meta.sortInfo || this.sortInfo;
10022         this.modified = [];
10023         this.fireEvent('metachange', this, this.reader.meta);
10024     },
10025     
10026     moveIndex : function(data, type)
10027     {
10028         var index = this.indexOf(data);
10029         
10030         var newIndex = index + type;
10031         
10032         this.remove(data);
10033         
10034         this.insert(newIndex, data);
10035         
10036     }
10037 });/*
10038  * Based on:
10039  * Ext JS Library 1.1.1
10040  * Copyright(c) 2006-2007, Ext JS, LLC.
10041  *
10042  * Originally Released Under LGPL - original licence link has changed is not relivant.
10043  *
10044  * Fork - LGPL
10045  * <script type="text/javascript">
10046  */
10047
10048 /**
10049  * @class Roo.data.SimpleStore
10050  * @extends Roo.data.Store
10051  * Small helper class to make creating Stores from Array data easier.
10052  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10053  * @cfg {Array} fields An array of field definition objects, or field name strings.
10054  * @cfg {Array} data The multi-dimensional array of data
10055  * @constructor
10056  * @param {Object} config
10057  */
10058 Roo.data.SimpleStore = function(config){
10059     Roo.data.SimpleStore.superclass.constructor.call(this, {
10060         isLocal : true,
10061         reader: new Roo.data.ArrayReader({
10062                 id: config.id
10063             },
10064             Roo.data.Record.create(config.fields)
10065         ),
10066         proxy : new Roo.data.MemoryProxy(config.data)
10067     });
10068     this.load();
10069 };
10070 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10071  * Based on:
10072  * Ext JS Library 1.1.1
10073  * Copyright(c) 2006-2007, Ext JS, LLC.
10074  *
10075  * Originally Released Under LGPL - original licence link has changed is not relivant.
10076  *
10077  * Fork - LGPL
10078  * <script type="text/javascript">
10079  */
10080
10081 /**
10082 /**
10083  * @extends Roo.data.Store
10084  * @class Roo.data.JsonStore
10085  * Small helper class to make creating Stores for JSON data easier. <br/>
10086 <pre><code>
10087 var store = new Roo.data.JsonStore({
10088     url: 'get-images.php',
10089     root: 'images',
10090     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10091 });
10092 </code></pre>
10093  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10094  * JsonReader and HttpProxy (unless inline data is provided).</b>
10095  * @cfg {Array} fields An array of field definition objects, or field name strings.
10096  * @constructor
10097  * @param {Object} config
10098  */
10099 Roo.data.JsonStore = function(c){
10100     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10101         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10102         reader: new Roo.data.JsonReader(c, c.fields)
10103     }));
10104 };
10105 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10106  * Based on:
10107  * Ext JS Library 1.1.1
10108  * Copyright(c) 2006-2007, Ext JS, LLC.
10109  *
10110  * Originally Released Under LGPL - original licence link has changed is not relivant.
10111  *
10112  * Fork - LGPL
10113  * <script type="text/javascript">
10114  */
10115
10116  
10117 Roo.data.Field = function(config){
10118     if(typeof config == "string"){
10119         config = {name: config};
10120     }
10121     Roo.apply(this, config);
10122     
10123     if(!this.type){
10124         this.type = "auto";
10125     }
10126     
10127     var st = Roo.data.SortTypes;
10128     // named sortTypes are supported, here we look them up
10129     if(typeof this.sortType == "string"){
10130         this.sortType = st[this.sortType];
10131     }
10132     
10133     // set default sortType for strings and dates
10134     if(!this.sortType){
10135         switch(this.type){
10136             case "string":
10137                 this.sortType = st.asUCString;
10138                 break;
10139             case "date":
10140                 this.sortType = st.asDate;
10141                 break;
10142             default:
10143                 this.sortType = st.none;
10144         }
10145     }
10146
10147     // define once
10148     var stripRe = /[\$,%]/g;
10149
10150     // prebuilt conversion function for this field, instead of
10151     // switching every time we're reading a value
10152     if(!this.convert){
10153         var cv, dateFormat = this.dateFormat;
10154         switch(this.type){
10155             case "":
10156             case "auto":
10157             case undefined:
10158                 cv = function(v){ return v; };
10159                 break;
10160             case "string":
10161                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10162                 break;
10163             case "int":
10164                 cv = function(v){
10165                     return v !== undefined && v !== null && v !== '' ?
10166                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10167                     };
10168                 break;
10169             case "float":
10170                 cv = function(v){
10171                     return v !== undefined && v !== null && v !== '' ?
10172                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10173                     };
10174                 break;
10175             case "bool":
10176             case "boolean":
10177                 cv = function(v){ return v === true || v === "true" || v == 1; };
10178                 break;
10179             case "date":
10180                 cv = function(v){
10181                     if(!v){
10182                         return '';
10183                     }
10184                     if(v instanceof Date){
10185                         return v;
10186                     }
10187                     if(dateFormat){
10188                         if(dateFormat == "timestamp"){
10189                             return new Date(v*1000);
10190                         }
10191                         return Date.parseDate(v, dateFormat);
10192                     }
10193                     var parsed = Date.parse(v);
10194                     return parsed ? new Date(parsed) : null;
10195                 };
10196              break;
10197             
10198         }
10199         this.convert = cv;
10200     }
10201 };
10202
10203 Roo.data.Field.prototype = {
10204     dateFormat: null,
10205     defaultValue: "",
10206     mapping: null,
10207     sortType : null,
10208     sortDir : "ASC"
10209 };/*
10210  * Based on:
10211  * Ext JS Library 1.1.1
10212  * Copyright(c) 2006-2007, Ext JS, LLC.
10213  *
10214  * Originally Released Under LGPL - original licence link has changed is not relivant.
10215  *
10216  * Fork - LGPL
10217  * <script type="text/javascript">
10218  */
10219  
10220 // Base class for reading structured data from a data source.  This class is intended to be
10221 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10222
10223 /**
10224  * @class Roo.data.DataReader
10225  * Base class for reading structured data from a data source.  This class is intended to be
10226  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10227  */
10228
10229 Roo.data.DataReader = function(meta, recordType){
10230     
10231     this.meta = meta;
10232     
10233     this.recordType = recordType instanceof Array ? 
10234         Roo.data.Record.create(recordType) : recordType;
10235 };
10236
10237 Roo.data.DataReader.prototype = {
10238      /**
10239      * Create an empty record
10240      * @param {Object} data (optional) - overlay some values
10241      * @return {Roo.data.Record} record created.
10242      */
10243     newRow :  function(d) {
10244         var da =  {};
10245         this.recordType.prototype.fields.each(function(c) {
10246             switch( c.type) {
10247                 case 'int' : da[c.name] = 0; break;
10248                 case 'date' : da[c.name] = new Date(); break;
10249                 case 'float' : da[c.name] = 0.0; break;
10250                 case 'boolean' : da[c.name] = false; break;
10251                 default : da[c.name] = ""; break;
10252             }
10253             
10254         });
10255         return new this.recordType(Roo.apply(da, d));
10256     }
10257     
10258 };/*
10259  * Based on:
10260  * Ext JS Library 1.1.1
10261  * Copyright(c) 2006-2007, Ext JS, LLC.
10262  *
10263  * Originally Released Under LGPL - original licence link has changed is not relivant.
10264  *
10265  * Fork - LGPL
10266  * <script type="text/javascript">
10267  */
10268
10269 /**
10270  * @class Roo.data.DataProxy
10271  * @extends Roo.data.Observable
10272  * This class is an abstract base class for implementations which provide retrieval of
10273  * unformatted data objects.<br>
10274  * <p>
10275  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10276  * (of the appropriate type which knows how to parse the data object) to provide a block of
10277  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10278  * <p>
10279  * Custom implementations must implement the load method as described in
10280  * {@link Roo.data.HttpProxy#load}.
10281  */
10282 Roo.data.DataProxy = function(){
10283     this.addEvents({
10284         /**
10285          * @event beforeload
10286          * Fires before a network request is made to retrieve a data object.
10287          * @param {Object} This DataProxy object.
10288          * @param {Object} params The params parameter to the load function.
10289          */
10290         beforeload : true,
10291         /**
10292          * @event load
10293          * Fires before the load method's callback is called.
10294          * @param {Object} This DataProxy object.
10295          * @param {Object} o The data object.
10296          * @param {Object} arg The callback argument object passed to the load function.
10297          */
10298         load : true,
10299         /**
10300          * @event loadexception
10301          * Fires if an Exception occurs during data retrieval.
10302          * @param {Object} This DataProxy object.
10303          * @param {Object} o The data object.
10304          * @param {Object} arg The callback argument object passed to the load function.
10305          * @param {Object} e The Exception.
10306          */
10307         loadexception : true
10308     });
10309     Roo.data.DataProxy.superclass.constructor.call(this);
10310 };
10311
10312 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10313
10314     /**
10315      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10316      */
10317 /*
10318  * Based on:
10319  * Ext JS Library 1.1.1
10320  * Copyright(c) 2006-2007, Ext JS, LLC.
10321  *
10322  * Originally Released Under LGPL - original licence link has changed is not relivant.
10323  *
10324  * Fork - LGPL
10325  * <script type="text/javascript">
10326  */
10327 /**
10328  * @class Roo.data.MemoryProxy
10329  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10330  * to the Reader when its load method is called.
10331  * @constructor
10332  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10333  */
10334 Roo.data.MemoryProxy = function(data){
10335     if (data.data) {
10336         data = data.data;
10337     }
10338     Roo.data.MemoryProxy.superclass.constructor.call(this);
10339     this.data = data;
10340 };
10341
10342 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10343     /**
10344      * Load data from the requested source (in this case an in-memory
10345      * data object passed to the constructor), read the data object into
10346      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10347      * process that block using the passed callback.
10348      * @param {Object} params This parameter is not used by the MemoryProxy class.
10349      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10350      * object into a block of Roo.data.Records.
10351      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10352      * The function must be passed <ul>
10353      * <li>The Record block object</li>
10354      * <li>The "arg" argument from the load function</li>
10355      * <li>A boolean success indicator</li>
10356      * </ul>
10357      * @param {Object} scope The scope in which to call the callback
10358      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10359      */
10360     load : function(params, reader, callback, scope, arg){
10361         params = params || {};
10362         var result;
10363         try {
10364             result = reader.readRecords(this.data);
10365         }catch(e){
10366             this.fireEvent("loadexception", this, arg, null, e);
10367             callback.call(scope, null, arg, false);
10368             return;
10369         }
10370         callback.call(scope, result, arg, true);
10371     },
10372     
10373     // private
10374     update : function(params, records){
10375         
10376     }
10377 });/*
10378  * Based on:
10379  * Ext JS Library 1.1.1
10380  * Copyright(c) 2006-2007, Ext JS, LLC.
10381  *
10382  * Originally Released Under LGPL - original licence link has changed is not relivant.
10383  *
10384  * Fork - LGPL
10385  * <script type="text/javascript">
10386  */
10387 /**
10388  * @class Roo.data.HttpProxy
10389  * @extends Roo.data.DataProxy
10390  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10391  * configured to reference a certain URL.<br><br>
10392  * <p>
10393  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10394  * from which the running page was served.<br><br>
10395  * <p>
10396  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10397  * <p>
10398  * Be aware that to enable the browser to parse an XML document, the server must set
10399  * the Content-Type header in the HTTP response to "text/xml".
10400  * @constructor
10401  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10402  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10403  * will be used to make the request.
10404  */
10405 Roo.data.HttpProxy = function(conn){
10406     Roo.data.HttpProxy.superclass.constructor.call(this);
10407     // is conn a conn config or a real conn?
10408     this.conn = conn;
10409     this.useAjax = !conn || !conn.events;
10410   
10411 };
10412
10413 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10414     // thse are take from connection...
10415     
10416     /**
10417      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10418      */
10419     /**
10420      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10421      * extra parameters to each request made by this object. (defaults to undefined)
10422      */
10423     /**
10424      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10425      *  to each request made by this object. (defaults to undefined)
10426      */
10427     /**
10428      * @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)
10429      */
10430     /**
10431      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10432      */
10433      /**
10434      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10435      * @type Boolean
10436      */
10437   
10438
10439     /**
10440      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10441      * @type Boolean
10442      */
10443     /**
10444      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10445      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10446      * a finer-grained basis than the DataProxy events.
10447      */
10448     getConnection : function(){
10449         return this.useAjax ? Roo.Ajax : this.conn;
10450     },
10451
10452     /**
10453      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10454      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10455      * process that block using the passed callback.
10456      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10457      * for the request to the remote server.
10458      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10459      * object into a block of Roo.data.Records.
10460      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10461      * The function must be passed <ul>
10462      * <li>The Record block object</li>
10463      * <li>The "arg" argument from the load function</li>
10464      * <li>A boolean success indicator</li>
10465      * </ul>
10466      * @param {Object} scope The scope in which to call the callback
10467      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10468      */
10469     load : function(params, reader, callback, scope, arg){
10470         if(this.fireEvent("beforeload", this, params) !== false){
10471             var  o = {
10472                 params : params || {},
10473                 request: {
10474                     callback : callback,
10475                     scope : scope,
10476                     arg : arg
10477                 },
10478                 reader: reader,
10479                 callback : this.loadResponse,
10480                 scope: this
10481             };
10482             if(this.useAjax){
10483                 Roo.applyIf(o, this.conn);
10484                 if(this.activeRequest){
10485                     Roo.Ajax.abort(this.activeRequest);
10486                 }
10487                 this.activeRequest = Roo.Ajax.request(o);
10488             }else{
10489                 this.conn.request(o);
10490             }
10491         }else{
10492             callback.call(scope||this, null, arg, false);
10493         }
10494     },
10495
10496     // private
10497     loadResponse : function(o, success, response){
10498         delete this.activeRequest;
10499         if(!success){
10500             this.fireEvent("loadexception", this, o, response);
10501             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10502             return;
10503         }
10504         var result;
10505         try {
10506             result = o.reader.read(response);
10507         }catch(e){
10508             this.fireEvent("loadexception", this, o, response, e);
10509             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10510             return;
10511         }
10512         
10513         this.fireEvent("load", this, o, o.request.arg);
10514         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10515     },
10516
10517     // private
10518     update : function(dataSet){
10519
10520     },
10521
10522     // private
10523     updateResponse : function(dataSet){
10524
10525     }
10526 });/*
10527  * Based on:
10528  * Ext JS Library 1.1.1
10529  * Copyright(c) 2006-2007, Ext JS, LLC.
10530  *
10531  * Originally Released Under LGPL - original licence link has changed is not relivant.
10532  *
10533  * Fork - LGPL
10534  * <script type="text/javascript">
10535  */
10536
10537 /**
10538  * @class Roo.data.ScriptTagProxy
10539  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10540  * other than the originating domain of the running page.<br><br>
10541  * <p>
10542  * <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
10543  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10544  * <p>
10545  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10546  * source code that is used as the source inside a &lt;script> tag.<br><br>
10547  * <p>
10548  * In order for the browser to process the returned data, the server must wrap the data object
10549  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10550  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10551  * depending on whether the callback name was passed:
10552  * <p>
10553  * <pre><code>
10554 boolean scriptTag = false;
10555 String cb = request.getParameter("callback");
10556 if (cb != null) {
10557     scriptTag = true;
10558     response.setContentType("text/javascript");
10559 } else {
10560     response.setContentType("application/x-json");
10561 }
10562 Writer out = response.getWriter();
10563 if (scriptTag) {
10564     out.write(cb + "(");
10565 }
10566 out.print(dataBlock.toJsonString());
10567 if (scriptTag) {
10568     out.write(");");
10569 }
10570 </pre></code>
10571  *
10572  * @constructor
10573  * @param {Object} config A configuration object.
10574  */
10575 Roo.data.ScriptTagProxy = function(config){
10576     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10577     Roo.apply(this, config);
10578     this.head = document.getElementsByTagName("head")[0];
10579 };
10580
10581 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10582
10583 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10584     /**
10585      * @cfg {String} url The URL from which to request the data object.
10586      */
10587     /**
10588      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10589      */
10590     timeout : 30000,
10591     /**
10592      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10593      * the server the name of the callback function set up by the load call to process the returned data object.
10594      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10595      * javascript output which calls this named function passing the data object as its only parameter.
10596      */
10597     callbackParam : "callback",
10598     /**
10599      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10600      * name to the request.
10601      */
10602     nocache : true,
10603
10604     /**
10605      * Load data from the configured URL, read the data object into
10606      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10607      * process that block using the passed callback.
10608      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10609      * for the request to the remote server.
10610      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10611      * object into a block of Roo.data.Records.
10612      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10613      * The function must be passed <ul>
10614      * <li>The Record block object</li>
10615      * <li>The "arg" argument from the load function</li>
10616      * <li>A boolean success indicator</li>
10617      * </ul>
10618      * @param {Object} scope The scope in which to call the callback
10619      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10620      */
10621     load : function(params, reader, callback, scope, arg){
10622         if(this.fireEvent("beforeload", this, params) !== false){
10623
10624             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10625
10626             var url = this.url;
10627             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10628             if(this.nocache){
10629                 url += "&_dc=" + (new Date().getTime());
10630             }
10631             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10632             var trans = {
10633                 id : transId,
10634                 cb : "stcCallback"+transId,
10635                 scriptId : "stcScript"+transId,
10636                 params : params,
10637                 arg : arg,
10638                 url : url,
10639                 callback : callback,
10640                 scope : scope,
10641                 reader : reader
10642             };
10643             var conn = this;
10644
10645             window[trans.cb] = function(o){
10646                 conn.handleResponse(o, trans);
10647             };
10648
10649             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10650
10651             if(this.autoAbort !== false){
10652                 this.abort();
10653             }
10654
10655             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10656
10657             var script = document.createElement("script");
10658             script.setAttribute("src", url);
10659             script.setAttribute("type", "text/javascript");
10660             script.setAttribute("id", trans.scriptId);
10661             this.head.appendChild(script);
10662
10663             this.trans = trans;
10664         }else{
10665             callback.call(scope||this, null, arg, false);
10666         }
10667     },
10668
10669     // private
10670     isLoading : function(){
10671         return this.trans ? true : false;
10672     },
10673
10674     /**
10675      * Abort the current server request.
10676      */
10677     abort : function(){
10678         if(this.isLoading()){
10679             this.destroyTrans(this.trans);
10680         }
10681     },
10682
10683     // private
10684     destroyTrans : function(trans, isLoaded){
10685         this.head.removeChild(document.getElementById(trans.scriptId));
10686         clearTimeout(trans.timeoutId);
10687         if(isLoaded){
10688             window[trans.cb] = undefined;
10689             try{
10690                 delete window[trans.cb];
10691             }catch(e){}
10692         }else{
10693             // if hasn't been loaded, wait for load to remove it to prevent script error
10694             window[trans.cb] = function(){
10695                 window[trans.cb] = undefined;
10696                 try{
10697                     delete window[trans.cb];
10698                 }catch(e){}
10699             };
10700         }
10701     },
10702
10703     // private
10704     handleResponse : function(o, trans){
10705         this.trans = false;
10706         this.destroyTrans(trans, true);
10707         var result;
10708         try {
10709             result = trans.reader.readRecords(o);
10710         }catch(e){
10711             this.fireEvent("loadexception", this, o, trans.arg, e);
10712             trans.callback.call(trans.scope||window, null, trans.arg, false);
10713             return;
10714         }
10715         this.fireEvent("load", this, o, trans.arg);
10716         trans.callback.call(trans.scope||window, result, trans.arg, true);
10717     },
10718
10719     // private
10720     handleFailure : function(trans){
10721         this.trans = false;
10722         this.destroyTrans(trans, false);
10723         this.fireEvent("loadexception", this, null, trans.arg);
10724         trans.callback.call(trans.scope||window, null, trans.arg, false);
10725     }
10726 });/*
10727  * Based on:
10728  * Ext JS Library 1.1.1
10729  * Copyright(c) 2006-2007, Ext JS, LLC.
10730  *
10731  * Originally Released Under LGPL - original licence link has changed is not relivant.
10732  *
10733  * Fork - LGPL
10734  * <script type="text/javascript">
10735  */
10736
10737 /**
10738  * @class Roo.data.JsonReader
10739  * @extends Roo.data.DataReader
10740  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10741  * based on mappings in a provided Roo.data.Record constructor.
10742  * 
10743  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10744  * in the reply previously. 
10745  * 
10746  * <p>
10747  * Example code:
10748  * <pre><code>
10749 var RecordDef = Roo.data.Record.create([
10750     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10751     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10752 ]);
10753 var myReader = new Roo.data.JsonReader({
10754     totalProperty: "results",    // The property which contains the total dataset size (optional)
10755     root: "rows",                // The property which contains an Array of row objects
10756     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10757 }, RecordDef);
10758 </code></pre>
10759  * <p>
10760  * This would consume a JSON file like this:
10761  * <pre><code>
10762 { 'results': 2, 'rows': [
10763     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10764     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10765 }
10766 </code></pre>
10767  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10768  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10769  * paged from the remote server.
10770  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10771  * @cfg {String} root name of the property which contains the Array of row objects.
10772  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10773  * @cfg {Array} fields Array of field definition objects
10774  * @constructor
10775  * Create a new JsonReader
10776  * @param {Object} meta Metadata configuration options
10777  * @param {Object} recordType Either an Array of field definition objects,
10778  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10779  */
10780 Roo.data.JsonReader = function(meta, recordType){
10781     
10782     meta = meta || {};
10783     // set some defaults:
10784     Roo.applyIf(meta, {
10785         totalProperty: 'total',
10786         successProperty : 'success',
10787         root : 'data',
10788         id : 'id'
10789     });
10790     
10791     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10792 };
10793 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10794     
10795     /**
10796      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10797      * Used by Store query builder to append _requestMeta to params.
10798      * 
10799      */
10800     metaFromRemote : false,
10801     /**
10802      * This method is only used by a DataProxy which has retrieved data from a remote server.
10803      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10804      * @return {Object} data A data block which is used by an Roo.data.Store object as
10805      * a cache of Roo.data.Records.
10806      */
10807     read : function(response){
10808         var json = response.responseText;
10809        
10810         var o = /* eval:var:o */ eval("("+json+")");
10811         if(!o) {
10812             throw {message: "JsonReader.read: Json object not found"};
10813         }
10814         
10815         if(o.metaData){
10816             
10817             delete this.ef;
10818             this.metaFromRemote = true;
10819             this.meta = o.metaData;
10820             this.recordType = Roo.data.Record.create(o.metaData.fields);
10821             this.onMetaChange(this.meta, this.recordType, o);
10822         }
10823         return this.readRecords(o);
10824     },
10825
10826     // private function a store will implement
10827     onMetaChange : function(meta, recordType, o){
10828
10829     },
10830
10831     /**
10832          * @ignore
10833          */
10834     simpleAccess: function(obj, subsc) {
10835         return obj[subsc];
10836     },
10837
10838         /**
10839          * @ignore
10840          */
10841     getJsonAccessor: function(){
10842         var re = /[\[\.]/;
10843         return function(expr) {
10844             try {
10845                 return(re.test(expr))
10846                     ? new Function("obj", "return obj." + expr)
10847                     : function(obj){
10848                         return obj[expr];
10849                     };
10850             } catch(e){}
10851             return Roo.emptyFn;
10852         };
10853     }(),
10854
10855     /**
10856      * Create a data block containing Roo.data.Records from an XML document.
10857      * @param {Object} o An object which contains an Array of row objects in the property specified
10858      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10859      * which contains the total size of the dataset.
10860      * @return {Object} data A data block which is used by an Roo.data.Store object as
10861      * a cache of Roo.data.Records.
10862      */
10863     readRecords : function(o){
10864         /**
10865          * After any data loads, the raw JSON data is available for further custom processing.
10866          * @type Object
10867          */
10868         this.o = o;
10869         var s = this.meta, Record = this.recordType,
10870             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10871
10872 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10873         if (!this.ef) {
10874             if(s.totalProperty) {
10875                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10876                 }
10877                 if(s.successProperty) {
10878                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10879                 }
10880                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10881                 if (s.id) {
10882                         var g = this.getJsonAccessor(s.id);
10883                         this.getId = function(rec) {
10884                                 var r = g(rec);  
10885                                 return (r === undefined || r === "") ? null : r;
10886                         };
10887                 } else {
10888                         this.getId = function(){return null;};
10889                 }
10890             this.ef = [];
10891             for(var jj = 0; jj < fl; jj++){
10892                 f = fi[jj];
10893                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10894                 this.ef[jj] = this.getJsonAccessor(map);
10895             }
10896         }
10897
10898         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10899         if(s.totalProperty){
10900             var vt = parseInt(this.getTotal(o), 10);
10901             if(!isNaN(vt)){
10902                 totalRecords = vt;
10903             }
10904         }
10905         if(s.successProperty){
10906             var vs = this.getSuccess(o);
10907             if(vs === false || vs === 'false'){
10908                 success = false;
10909             }
10910         }
10911         var records = [];
10912         for(var i = 0; i < c; i++){
10913                 var n = root[i];
10914             var values = {};
10915             var id = this.getId(n);
10916             for(var j = 0; j < fl; j++){
10917                 f = fi[j];
10918             var v = this.ef[j](n);
10919             if (!f.convert) {
10920                 Roo.log('missing convert for ' + f.name);
10921                 Roo.log(f);
10922                 continue;
10923             }
10924             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10925             }
10926             var record = new Record(values, id);
10927             record.json = n;
10928             records[i] = record;
10929         }
10930         return {
10931             raw : o,
10932             success : success,
10933             records : records,
10934             totalRecords : totalRecords
10935         };
10936     }
10937 });/*
10938  * Based on:
10939  * Ext JS Library 1.1.1
10940  * Copyright(c) 2006-2007, Ext JS, LLC.
10941  *
10942  * Originally Released Under LGPL - original licence link has changed is not relivant.
10943  *
10944  * Fork - LGPL
10945  * <script type="text/javascript">
10946  */
10947
10948 /**
10949  * @class Roo.data.ArrayReader
10950  * @extends Roo.data.DataReader
10951  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10952  * Each element of that Array represents a row of data fields. The
10953  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10954  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10955  * <p>
10956  * Example code:.
10957  * <pre><code>
10958 var RecordDef = Roo.data.Record.create([
10959     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10960     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10961 ]);
10962 var myReader = new Roo.data.ArrayReader({
10963     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10964 }, RecordDef);
10965 </code></pre>
10966  * <p>
10967  * This would consume an Array like this:
10968  * <pre><code>
10969 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10970   </code></pre>
10971  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10972  * @constructor
10973  * Create a new JsonReader
10974  * @param {Object} meta Metadata configuration options.
10975  * @param {Object} recordType Either an Array of field definition objects
10976  * as specified to {@link Roo.data.Record#create},
10977  * or an {@link Roo.data.Record} object
10978  * created using {@link Roo.data.Record#create}.
10979  */
10980 Roo.data.ArrayReader = function(meta, recordType){
10981     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10982 };
10983
10984 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10985     /**
10986      * Create a data block containing Roo.data.Records from an XML document.
10987      * @param {Object} o An Array of row objects which represents the dataset.
10988      * @return {Object} data A data block which is used by an Roo.data.Store object as
10989      * a cache of Roo.data.Records.
10990      */
10991     readRecords : function(o){
10992         var sid = this.meta ? this.meta.id : null;
10993         var recordType = this.recordType, fields = recordType.prototype.fields;
10994         var records = [];
10995         var root = o;
10996             for(var i = 0; i < root.length; i++){
10997                     var n = root[i];
10998                 var values = {};
10999                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11000                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11001                 var f = fields.items[j];
11002                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11003                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11004                 v = f.convert(v);
11005                 values[f.name] = v;
11006             }
11007                 var record = new recordType(values, id);
11008                 record.json = n;
11009                 records[records.length] = record;
11010             }
11011             return {
11012                 records : records,
11013                 totalRecords : records.length
11014             };
11015     }
11016 });/*
11017  * - LGPL
11018  * * 
11019  */
11020
11021 /**
11022  * @class Roo.bootstrap.ComboBox
11023  * @extends Roo.bootstrap.TriggerField
11024  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11025  * @cfg {Boolean} append (true|false) default false
11026  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11027  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11028  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11029  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11030  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11031  * @constructor
11032  * Create a new ComboBox.
11033  * @param {Object} config Configuration options
11034  */
11035 Roo.bootstrap.ComboBox = function(config){
11036     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11037     this.addEvents({
11038         /**
11039          * @event expand
11040          * Fires when the dropdown list is expanded
11041              * @param {Roo.bootstrap.ComboBox} combo This combo box
11042              */
11043         'expand' : true,
11044         /**
11045          * @event collapse
11046          * Fires when the dropdown list is collapsed
11047              * @param {Roo.bootstrap.ComboBox} combo This combo box
11048              */
11049         'collapse' : true,
11050         /**
11051          * @event beforeselect
11052          * Fires before a list item is selected. Return false to cancel the selection.
11053              * @param {Roo.bootstrap.ComboBox} combo This combo box
11054              * @param {Roo.data.Record} record The data record returned from the underlying store
11055              * @param {Number} index The index of the selected item in the dropdown list
11056              */
11057         'beforeselect' : true,
11058         /**
11059          * @event select
11060          * Fires when a list item is selected
11061              * @param {Roo.bootstrap.ComboBox} combo This combo box
11062              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11063              * @param {Number} index The index of the selected item in the dropdown list
11064              */
11065         'select' : true,
11066         /**
11067          * @event beforequery
11068          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11069          * The event object passed has these properties:
11070              * @param {Roo.bootstrap.ComboBox} combo This combo box
11071              * @param {String} query The query
11072              * @param {Boolean} forceAll true to force "all" query
11073              * @param {Boolean} cancel true to cancel the query
11074              * @param {Object} e The query event object
11075              */
11076         'beforequery': true,
11077          /**
11078          * @event add
11079          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11080              * @param {Roo.bootstrap.ComboBox} combo This combo box
11081              */
11082         'add' : true,
11083         /**
11084          * @event edit
11085          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11086              * @param {Roo.bootstrap.ComboBox} combo This combo box
11087              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11088              */
11089         'edit' : true,
11090         /**
11091          * @event remove
11092          * Fires when the remove value from the combobox array
11093              * @param {Roo.bootstrap.ComboBox} combo This combo box
11094              */
11095         'remove' : true,
11096         /**
11097          * @event specialfilter
11098          * Fires when specialfilter
11099             * @param {Roo.bootstrap.ComboBox} combo This combo box
11100             */
11101         'specialfilter' : true
11102         
11103     });
11104     
11105     this.item = [];
11106     this.tickItems = [];
11107     
11108     this.selectedIndex = -1;
11109     if(this.mode == 'local'){
11110         if(config.queryDelay === undefined){
11111             this.queryDelay = 10;
11112         }
11113         if(config.minChars === undefined){
11114             this.minChars = 0;
11115         }
11116     }
11117 };
11118
11119 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11120      
11121     /**
11122      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11123      * rendering into an Roo.Editor, defaults to false)
11124      */
11125     /**
11126      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11127      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11128      */
11129     /**
11130      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11131      */
11132     /**
11133      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11134      * the dropdown list (defaults to undefined, with no header element)
11135      */
11136
11137      /**
11138      * @cfg {String/Roo.Template} tpl The template to use to render the output
11139      */
11140      
11141      /**
11142      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11143      */
11144     listWidth: undefined,
11145     /**
11146      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11147      * mode = 'remote' or 'text' if mode = 'local')
11148      */
11149     displayField: undefined,
11150     
11151     /**
11152      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11153      * mode = 'remote' or 'value' if mode = 'local'). 
11154      * Note: use of a valueField requires the user make a selection
11155      * in order for a value to be mapped.
11156      */
11157     valueField: undefined,
11158     
11159     
11160     /**
11161      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11162      * field's data value (defaults to the underlying DOM element's name)
11163      */
11164     hiddenName: undefined,
11165     /**
11166      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11167      */
11168     listClass: '',
11169     /**
11170      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11171      */
11172     selectedClass: 'active',
11173     
11174     /**
11175      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11176      */
11177     shadow:'sides',
11178     /**
11179      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11180      * anchor positions (defaults to 'tl-bl')
11181      */
11182     listAlign: 'tl-bl?',
11183     /**
11184      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11185      */
11186     maxHeight: 300,
11187     /**
11188      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11189      * query specified by the allQuery config option (defaults to 'query')
11190      */
11191     triggerAction: 'query',
11192     /**
11193      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11194      * (defaults to 4, does not apply if editable = false)
11195      */
11196     minChars : 4,
11197     /**
11198      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11199      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11200      */
11201     typeAhead: false,
11202     /**
11203      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11204      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11205      */
11206     queryDelay: 500,
11207     /**
11208      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11209      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11210      */
11211     pageSize: 0,
11212     /**
11213      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11214      * when editable = true (defaults to false)
11215      */
11216     selectOnFocus:false,
11217     /**
11218      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11219      */
11220     queryParam: 'query',
11221     /**
11222      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11223      * when mode = 'remote' (defaults to 'Loading...')
11224      */
11225     loadingText: 'Loading...',
11226     /**
11227      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11228      */
11229     resizable: false,
11230     /**
11231      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11232      */
11233     handleHeight : 8,
11234     /**
11235      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11236      * traditional select (defaults to true)
11237      */
11238     editable: true,
11239     /**
11240      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11241      */
11242     allQuery: '',
11243     /**
11244      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11245      */
11246     mode: 'remote',
11247     /**
11248      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11249      * listWidth has a higher value)
11250      */
11251     minListWidth : 70,
11252     /**
11253      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11254      * allow the user to set arbitrary text into the field (defaults to false)
11255      */
11256     forceSelection:false,
11257     /**
11258      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11259      * if typeAhead = true (defaults to 250)
11260      */
11261     typeAheadDelay : 250,
11262     /**
11263      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11264      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11265      */
11266     valueNotFoundText : undefined,
11267     /**
11268      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11269      */
11270     blockFocus : false,
11271     
11272     /**
11273      * @cfg {Boolean} disableClear Disable showing of clear button.
11274      */
11275     disableClear : false,
11276     /**
11277      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11278      */
11279     alwaysQuery : false,
11280     
11281     /**
11282      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11283      */
11284     multiple : false,
11285     
11286     /**
11287      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11288      */
11289     invalidClass : "has-warning",
11290     
11291     /**
11292      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11293      */
11294     validClass : "has-success",
11295     
11296     /**
11297      * @cfg {Boolean} specialFilter (true|false) special filter default false
11298      */
11299     specialFilter : false,
11300     
11301     //private
11302     addicon : false,
11303     editicon: false,
11304     
11305     page: 0,
11306     hasQuery: false,
11307     append: false,
11308     loadNext: false,
11309     autoFocus : true,
11310     tickable : false,
11311     btnPosition : 'right',
11312     triggerList : true,
11313     showToggleBtn : true,
11314     // element that contains real text value.. (when hidden is used..)
11315     
11316     getAutoCreate : function()
11317     {
11318         var cfg = false;
11319         
11320         /*
11321          *  Normal ComboBox
11322          */
11323         if(!this.tickable){
11324             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11325             return cfg;
11326         }
11327         
11328         /*
11329          *  ComboBox with tickable selections
11330          */
11331              
11332         var align = this.labelAlign || this.parentLabelAlign();
11333         
11334         cfg = {
11335             cls : 'form-group roo-combobox-tickable' //input-group
11336         };
11337         
11338         var buttons = {
11339             tag : 'div',
11340             cls : 'tickable-buttons',
11341             cn : [
11342                 {
11343                     tag : 'button',
11344                     type : 'button',
11345                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11346                     html : 'Edit'
11347                 },
11348                 {
11349                     tag : 'button',
11350                     type : 'button',
11351                     name : 'ok',
11352                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11353                     html : 'Done'
11354                 },
11355                 {
11356                     tag : 'button',
11357                     type : 'button',
11358                     name : 'cancel',
11359                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11360                     html : 'Cancel'
11361                 }
11362             ]
11363         };
11364         
11365         if(this.editable){
11366             buttons.cn.unshift({
11367                 tag: 'input',
11368                 cls: 'select2-search-field-input'
11369             });
11370         }
11371         
11372         var _this = this;
11373         
11374         Roo.each(buttons.cn, function(c){
11375             if (_this.size) {
11376                 c.cls += ' btn-' + _this.size;
11377             }
11378
11379             if (_this.disabled) {
11380                 c.disabled = true;
11381             }
11382         });
11383         
11384         var box = {
11385             tag: 'div',
11386             cn: [
11387                 {
11388                     tag: 'input',
11389                     type : 'hidden',
11390                     cls: 'form-hidden-field'
11391                 },
11392                 {
11393                     tag: 'ul',
11394                     cls: 'select2-choices',
11395                     cn:[
11396                         {
11397                             tag: 'li',
11398                             cls: 'select2-search-field',
11399                             cn: [
11400
11401                                 buttons
11402                             ]
11403                         }
11404                     ]
11405                 }
11406             ]
11407         }
11408         
11409         var combobox = {
11410             cls: 'select2-container input-group select2-container-multi',
11411             cn: [
11412                 box
11413 //                {
11414 //                    tag: 'ul',
11415 //                    cls: 'typeahead typeahead-long dropdown-menu',
11416 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11417 //                }
11418             ]
11419         };
11420         
11421         if(this.hasFeedback && !this.allowBlank){
11422             
11423             var feedback = {
11424                 tag: 'span',
11425                 cls: 'glyphicon form-control-feedback'
11426             };
11427
11428             combobox.cn.push(feedback);
11429         }
11430         
11431         if (align ==='left' && this.fieldLabel.length) {
11432             
11433                 Roo.log("left and has label");
11434                 cfg.cn = [
11435                     
11436                     {
11437                         tag: 'label',
11438                         'for' :  id,
11439                         cls : 'control-label col-sm-' + this.labelWidth,
11440                         html : this.fieldLabel
11441                         
11442                     },
11443                     {
11444                         cls : "col-sm-" + (12 - this.labelWidth), 
11445                         cn: [
11446                             combobox
11447                         ]
11448                     }
11449                     
11450                 ];
11451         } else if ( this.fieldLabel.length) {
11452                 Roo.log(" label");
11453                  cfg.cn = [
11454                    
11455                     {
11456                         tag: 'label',
11457                         //cls : 'input-group-addon',
11458                         html : this.fieldLabel
11459                         
11460                     },
11461                     
11462                     combobox
11463                     
11464                 ];
11465
11466         } else {
11467             
11468                 Roo.log(" no label && no align");
11469                 cfg = combobox
11470                      
11471                 
11472         }
11473          
11474         var settings=this;
11475         ['xs','sm','md','lg'].map(function(size){
11476             if (settings[size]) {
11477                 cfg.cls += ' col-' + size + '-' + settings[size];
11478             }
11479         });
11480         
11481         return cfg;
11482         
11483     },
11484     
11485     // private
11486     initEvents: function()
11487     {
11488         
11489         if (!this.store) {
11490             throw "can not find store for combo";
11491         }
11492         this.store = Roo.factory(this.store, Roo.data);
11493         
11494         if(this.tickable){
11495             this.initTickableEvents();
11496             return;
11497         }
11498         
11499         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11500         
11501         if(this.hiddenName){
11502             
11503             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11504             
11505             this.hiddenField.dom.value =
11506                 this.hiddenValue !== undefined ? this.hiddenValue :
11507                 this.value !== undefined ? this.value : '';
11508
11509             // prevent input submission
11510             this.el.dom.removeAttribute('name');
11511             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11512              
11513              
11514         }
11515         //if(Roo.isGecko){
11516         //    this.el.dom.setAttribute('autocomplete', 'off');
11517         //}
11518         
11519         var cls = 'x-combo-list';
11520         
11521         //this.list = new Roo.Layer({
11522         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11523         //});
11524         
11525         var _this = this;
11526         
11527         (function(){
11528             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11529             _this.list.setWidth(lw);
11530         }).defer(100);
11531         
11532         this.list.on('mouseover', this.onViewOver, this);
11533         this.list.on('mousemove', this.onViewMove, this);
11534         
11535         this.list.on('scroll', this.onViewScroll, this);
11536         
11537         /*
11538         this.list.swallowEvent('mousewheel');
11539         this.assetHeight = 0;
11540
11541         if(this.title){
11542             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11543             this.assetHeight += this.header.getHeight();
11544         }
11545
11546         this.innerList = this.list.createChild({cls:cls+'-inner'});
11547         this.innerList.on('mouseover', this.onViewOver, this);
11548         this.innerList.on('mousemove', this.onViewMove, this);
11549         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11550         
11551         if(this.allowBlank && !this.pageSize && !this.disableClear){
11552             this.footer = this.list.createChild({cls:cls+'-ft'});
11553             this.pageTb = new Roo.Toolbar(this.footer);
11554            
11555         }
11556         if(this.pageSize){
11557             this.footer = this.list.createChild({cls:cls+'-ft'});
11558             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11559                     {pageSize: this.pageSize});
11560             
11561         }
11562         
11563         if (this.pageTb && this.allowBlank && !this.disableClear) {
11564             var _this = this;
11565             this.pageTb.add(new Roo.Toolbar.Fill(), {
11566                 cls: 'x-btn-icon x-btn-clear',
11567                 text: '&#160;',
11568                 handler: function()
11569                 {
11570                     _this.collapse();
11571                     _this.clearValue();
11572                     _this.onSelect(false, -1);
11573                 }
11574             });
11575         }
11576         if (this.footer) {
11577             this.assetHeight += this.footer.getHeight();
11578         }
11579         */
11580             
11581         if(!this.tpl){
11582             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11583         }
11584
11585         this.view = new Roo.View(this.list, this.tpl, {
11586             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11587         });
11588         //this.view.wrapEl.setDisplayed(false);
11589         this.view.on('click', this.onViewClick, this);
11590         
11591         
11592         
11593         this.store.on('beforeload', this.onBeforeLoad, this);
11594         this.store.on('load', this.onLoad, this);
11595         this.store.on('loadexception', this.onLoadException, this);
11596         /*
11597         if(this.resizable){
11598             this.resizer = new Roo.Resizable(this.list,  {
11599                pinned:true, handles:'se'
11600             });
11601             this.resizer.on('resize', function(r, w, h){
11602                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11603                 this.listWidth = w;
11604                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11605                 this.restrictHeight();
11606             }, this);
11607             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11608         }
11609         */
11610         if(!this.editable){
11611             this.editable = true;
11612             this.setEditable(false);
11613         }
11614         
11615         /*
11616         
11617         if (typeof(this.events.add.listeners) != 'undefined') {
11618             
11619             this.addicon = this.wrap.createChild(
11620                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11621        
11622             this.addicon.on('click', function(e) {
11623                 this.fireEvent('add', this);
11624             }, this);
11625         }
11626         if (typeof(this.events.edit.listeners) != 'undefined') {
11627             
11628             this.editicon = this.wrap.createChild(
11629                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11630             if (this.addicon) {
11631                 this.editicon.setStyle('margin-left', '40px');
11632             }
11633             this.editicon.on('click', function(e) {
11634                 
11635                 // we fire even  if inothing is selected..
11636                 this.fireEvent('edit', this, this.lastData );
11637                 
11638             }, this);
11639         }
11640         */
11641         
11642         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11643             "up" : function(e){
11644                 this.inKeyMode = true;
11645                 this.selectPrev();
11646             },
11647
11648             "down" : function(e){
11649                 if(!this.isExpanded()){
11650                     this.onTriggerClick();
11651                 }else{
11652                     this.inKeyMode = true;
11653                     this.selectNext();
11654                 }
11655             },
11656
11657             "enter" : function(e){
11658 //                this.onViewClick();
11659                 //return true;
11660                 this.collapse();
11661                 
11662                 if(this.fireEvent("specialkey", this, e)){
11663                     this.onViewClick(false);
11664                 }
11665                 
11666                 return true;
11667             },
11668
11669             "esc" : function(e){
11670                 this.collapse();
11671             },
11672
11673             "tab" : function(e){
11674                 this.collapse();
11675                 
11676                 if(this.fireEvent("specialkey", this, e)){
11677                     this.onViewClick(false);
11678                 }
11679                 
11680                 return true;
11681             },
11682
11683             scope : this,
11684
11685             doRelay : function(foo, bar, hname){
11686                 if(hname == 'down' || this.scope.isExpanded()){
11687                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11688                 }
11689                 return true;
11690             },
11691
11692             forceKeyDown: true
11693         });
11694         
11695         
11696         this.queryDelay = Math.max(this.queryDelay || 10,
11697                 this.mode == 'local' ? 10 : 250);
11698         
11699         
11700         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11701         
11702         if(this.typeAhead){
11703             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11704         }
11705         if(this.editable !== false){
11706             this.inputEl().on("keyup", this.onKeyUp, this);
11707         }
11708         if(this.forceSelection){
11709             this.inputEl().on('blur', this.doForce, this);
11710         }
11711         
11712         if(this.multiple){
11713             this.choices = this.el.select('ul.select2-choices', true).first();
11714             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11715         }
11716     },
11717     
11718     initTickableEvents: function()
11719     {   
11720         this.createList();
11721         
11722         if(this.hiddenName){
11723             
11724             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11725             
11726             this.hiddenField.dom.value =
11727                 this.hiddenValue !== undefined ? this.hiddenValue :
11728                 this.value !== undefined ? this.value : '';
11729
11730             // prevent input submission
11731             this.el.dom.removeAttribute('name');
11732             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11733              
11734              
11735         }
11736         
11737 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11738         
11739         this.choices = this.el.select('ul.select2-choices', true).first();
11740         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11741         if(this.triggerList){
11742             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11743         }
11744          
11745         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11746         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11747         
11748         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11749         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11750         
11751         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11752         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11753         
11754         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11755         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11756         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11757         
11758         this.okBtn.hide();
11759         this.cancelBtn.hide();
11760         
11761         var _this = this;
11762         
11763         (function(){
11764             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11765             _this.list.setWidth(lw);
11766         }).defer(100);
11767         
11768         this.list.on('mouseover', this.onViewOver, this);
11769         this.list.on('mousemove', this.onViewMove, this);
11770         
11771         this.list.on('scroll', this.onViewScroll, this);
11772         
11773         if(!this.tpl){
11774             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>';
11775         }
11776
11777         this.view = new Roo.View(this.list, this.tpl, {
11778             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11779         });
11780         
11781         //this.view.wrapEl.setDisplayed(false);
11782         this.view.on('click', this.onViewClick, this);
11783         
11784         
11785         
11786         this.store.on('beforeload', this.onBeforeLoad, this);
11787         this.store.on('load', this.onLoad, this);
11788         this.store.on('loadexception', this.onLoadException, this);
11789         
11790         if(this.editable){
11791             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11792                 "up" : function(e){
11793                     this.inKeyMode = true;
11794                     this.selectPrev();
11795                 },
11796
11797                 "down" : function(e){
11798                     this.inKeyMode = true;
11799                     this.selectNext();
11800                 },
11801
11802                 "enter" : function(e){
11803                     if(this.fireEvent("specialkey", this, e)){
11804                         this.onViewClick(false);
11805                     }
11806                     
11807                     return true;
11808                 },
11809
11810                 "esc" : function(e){
11811                     this.onTickableFooterButtonClick(e, false, false);
11812                 },
11813
11814                 "tab" : function(e){
11815                     this.fireEvent("specialkey", this, e);
11816                     
11817                     this.onTickableFooterButtonClick(e, false, false);
11818                     
11819                     return true;
11820                 },
11821
11822                 scope : this,
11823
11824                 doRelay : function(e, fn, key){
11825                     if(this.scope.isExpanded()){
11826                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11827                     }
11828                     return true;
11829                 },
11830
11831                 forceKeyDown: true
11832             });
11833         }
11834         
11835         this.queryDelay = Math.max(this.queryDelay || 10,
11836                 this.mode == 'local' ? 10 : 250);
11837         
11838         
11839         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11840         
11841         if(this.typeAhead){
11842             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11843         }
11844         
11845         if(this.editable !== false){
11846             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11847         }
11848         
11849     },
11850
11851     onDestroy : function(){
11852         if(this.view){
11853             this.view.setStore(null);
11854             this.view.el.removeAllListeners();
11855             this.view.el.remove();
11856             this.view.purgeListeners();
11857         }
11858         if(this.list){
11859             this.list.dom.innerHTML  = '';
11860         }
11861         
11862         if(this.store){
11863             this.store.un('beforeload', this.onBeforeLoad, this);
11864             this.store.un('load', this.onLoad, this);
11865             this.store.un('loadexception', this.onLoadException, this);
11866         }
11867         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11868     },
11869
11870     // private
11871     fireKey : function(e){
11872         if(e.isNavKeyPress() && !this.list.isVisible()){
11873             this.fireEvent("specialkey", this, e);
11874         }
11875     },
11876
11877     // private
11878     onResize: function(w, h){
11879 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11880 //        
11881 //        if(typeof w != 'number'){
11882 //            // we do not handle it!?!?
11883 //            return;
11884 //        }
11885 //        var tw = this.trigger.getWidth();
11886 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11887 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11888 //        var x = w - tw;
11889 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11890 //            
11891 //        //this.trigger.setStyle('left', x+'px');
11892 //        
11893 //        if(this.list && this.listWidth === undefined){
11894 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11895 //            this.list.setWidth(lw);
11896 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11897 //        }
11898         
11899     
11900         
11901     },
11902
11903     /**
11904      * Allow or prevent the user from directly editing the field text.  If false is passed,
11905      * the user will only be able to select from the items defined in the dropdown list.  This method
11906      * is the runtime equivalent of setting the 'editable' config option at config time.
11907      * @param {Boolean} value True to allow the user to directly edit the field text
11908      */
11909     setEditable : function(value){
11910         if(value == this.editable){
11911             return;
11912         }
11913         this.editable = value;
11914         if(!value){
11915             this.inputEl().dom.setAttribute('readOnly', true);
11916             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11917             this.inputEl().addClass('x-combo-noedit');
11918         }else{
11919             this.inputEl().dom.setAttribute('readOnly', false);
11920             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11921             this.inputEl().removeClass('x-combo-noedit');
11922         }
11923     },
11924
11925     // private
11926     
11927     onBeforeLoad : function(combo,opts){
11928         if(!this.hasFocus){
11929             return;
11930         }
11931          if (!opts.add) {
11932             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11933          }
11934         this.restrictHeight();
11935         this.selectedIndex = -1;
11936     },
11937
11938     // private
11939     onLoad : function(){
11940         
11941         this.hasQuery = false;
11942         
11943         if(!this.hasFocus){
11944             return;
11945         }
11946         
11947         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11948             this.loading.hide();
11949         }
11950              
11951         if(this.store.getCount() > 0){
11952             this.expand();
11953             this.restrictHeight();
11954             if(this.lastQuery == this.allQuery){
11955                 if(this.editable && !this.tickable){
11956                     this.inputEl().dom.select();
11957                 }
11958                 
11959                 if(
11960                     !this.selectByValue(this.value, true) &&
11961                     this.autoFocus && 
11962                     (
11963                         !this.store.lastOptions ||
11964                         typeof(this.store.lastOptions.add) == 'undefined' || 
11965                         this.store.lastOptions.add != true
11966                     )
11967                 ){
11968                     this.select(0, true);
11969                 }
11970             }else{
11971                 if(this.autoFocus){
11972                     this.selectNext();
11973                 }
11974                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11975                     this.taTask.delay(this.typeAheadDelay);
11976                 }
11977             }
11978         }else{
11979             this.onEmptyResults();
11980         }
11981         
11982         //this.el.focus();
11983     },
11984     // private
11985     onLoadException : function()
11986     {
11987         this.hasQuery = false;
11988         
11989         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11990             this.loading.hide();
11991         }
11992         
11993         if(this.tickable && this.editable){
11994             return;
11995         }
11996         
11997         this.collapse();
11998         
11999         Roo.log(this.store.reader.jsonData);
12000         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12001             // fixme
12002             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12003         }
12004         
12005         
12006     },
12007     // private
12008     onTypeAhead : function(){
12009         if(this.store.getCount() > 0){
12010             var r = this.store.getAt(0);
12011             var newValue = r.data[this.displayField];
12012             var len = newValue.length;
12013             var selStart = this.getRawValue().length;
12014             
12015             if(selStart != len){
12016                 this.setRawValue(newValue);
12017                 this.selectText(selStart, newValue.length);
12018             }
12019         }
12020     },
12021
12022     // private
12023     onSelect : function(record, index){
12024         
12025         if(this.fireEvent('beforeselect', this, record, index) !== false){
12026         
12027             this.setFromData(index > -1 ? record.data : false);
12028             
12029             this.collapse();
12030             this.fireEvent('select', this, record, index);
12031         }
12032     },
12033
12034     /**
12035      * Returns the currently selected field value or empty string if no value is set.
12036      * @return {String} value The selected value
12037      */
12038     getValue : function(){
12039         
12040         if(this.multiple){
12041             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12042         }
12043         
12044         if(this.valueField){
12045             return typeof this.value != 'undefined' ? this.value : '';
12046         }else{
12047             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12048         }
12049     },
12050
12051     /**
12052      * Clears any text/value currently set in the field
12053      */
12054     clearValue : function(){
12055         if(this.hiddenField){
12056             this.hiddenField.dom.value = '';
12057         }
12058         this.value = '';
12059         this.setRawValue('');
12060         this.lastSelectionText = '';
12061         this.lastData = false;
12062         
12063         var close = this.closeTriggerEl();
12064         
12065         if(close){
12066             close.hide();
12067         }
12068         
12069     },
12070
12071     /**
12072      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12073      * will be displayed in the field.  If the value does not match the data value of an existing item,
12074      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12075      * Otherwise the field will be blank (although the value will still be set).
12076      * @param {String} value The value to match
12077      */
12078     setValue : function(v){
12079         if(this.multiple){
12080             this.syncValue();
12081             return;
12082         }
12083         
12084         var text = v;
12085         if(this.valueField){
12086             var r = this.findRecord(this.valueField, v);
12087             if(r){
12088                 text = r.data[this.displayField];
12089             }else if(this.valueNotFoundText !== undefined){
12090                 text = this.valueNotFoundText;
12091             }
12092         }
12093         this.lastSelectionText = text;
12094         if(this.hiddenField){
12095             this.hiddenField.dom.value = v;
12096         }
12097         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12098         this.value = v;
12099         
12100         var close = this.closeTriggerEl();
12101         
12102         if(close){
12103             (v.length || v * 1 > 0) ? close.show() : close.hide();
12104         }
12105     },
12106     /**
12107      * @property {Object} the last set data for the element
12108      */
12109     
12110     lastData : false,
12111     /**
12112      * Sets the value of the field based on a object which is related to the record format for the store.
12113      * @param {Object} value the value to set as. or false on reset?
12114      */
12115     setFromData : function(o){
12116         
12117         if(this.multiple){
12118             this.addItem(o);
12119             return;
12120         }
12121             
12122         var dv = ''; // display value
12123         var vv = ''; // value value..
12124         this.lastData = o;
12125         if (this.displayField) {
12126             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12127         } else {
12128             // this is an error condition!!!
12129             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12130         }
12131         
12132         if(this.valueField){
12133             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12134         }
12135         
12136         var close = this.closeTriggerEl();
12137         
12138         if(close){
12139             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12140         }
12141         
12142         if(this.hiddenField){
12143             this.hiddenField.dom.value = vv;
12144             
12145             this.lastSelectionText = dv;
12146             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12147             this.value = vv;
12148             return;
12149         }
12150         // no hidden field.. - we store the value in 'value', but still display
12151         // display field!!!!
12152         this.lastSelectionText = dv;
12153         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12154         this.value = vv;
12155         
12156         
12157         
12158     },
12159     // private
12160     reset : function(){
12161         // overridden so that last data is reset..
12162         
12163         if(this.multiple){
12164             this.clearItem();
12165             return;
12166         }
12167         
12168         this.setValue(this.originalValue);
12169         this.clearInvalid();
12170         this.lastData = false;
12171         if (this.view) {
12172             this.view.clearSelections();
12173         }
12174     },
12175     // private
12176     findRecord : function(prop, value){
12177         var record;
12178         if(this.store.getCount() > 0){
12179             this.store.each(function(r){
12180                 if(r.data[prop] == value){
12181                     record = r;
12182                     return false;
12183                 }
12184                 return true;
12185             });
12186         }
12187         return record;
12188     },
12189     
12190     getName: function()
12191     {
12192         // returns hidden if it's set..
12193         if (!this.rendered) {return ''};
12194         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12195         
12196     },
12197     // private
12198     onViewMove : function(e, t){
12199         this.inKeyMode = false;
12200     },
12201
12202     // private
12203     onViewOver : function(e, t){
12204         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12205             return;
12206         }
12207         var item = this.view.findItemFromChild(t);
12208         
12209         if(item){
12210             var index = this.view.indexOf(item);
12211             this.select(index, false);
12212         }
12213     },
12214
12215     // private
12216     onViewClick : function(view, doFocus, el, e)
12217     {
12218         var index = this.view.getSelectedIndexes()[0];
12219         
12220         var r = this.store.getAt(index);
12221         
12222         if(this.tickable){
12223             
12224             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12225                 return;
12226             }
12227             
12228             var rm = false;
12229             var _this = this;
12230             
12231             Roo.each(this.tickItems, function(v,k){
12232                 
12233                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12234                     _this.tickItems.splice(k, 1);
12235                     
12236                     if(typeof(e) == 'undefined' && view == false){
12237                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12238                     }
12239                     
12240                     rm = true;
12241                     return;
12242                 }
12243             });
12244             
12245             if(rm){
12246                 return;
12247             }
12248             
12249             this.tickItems.push(r.data);
12250             
12251             if(typeof(e) == 'undefined' && view == false){
12252                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12253             }
12254                     
12255             return;
12256         }
12257         
12258         if(r){
12259             this.onSelect(r, index);
12260         }
12261         if(doFocus !== false && !this.blockFocus){
12262             this.inputEl().focus();
12263         }
12264     },
12265
12266     // private
12267     restrictHeight : function(){
12268         //this.innerList.dom.style.height = '';
12269         //var inner = this.innerList.dom;
12270         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12271         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12272         //this.list.beginUpdate();
12273         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12274         this.list.alignTo(this.inputEl(), this.listAlign);
12275         this.list.alignTo(this.inputEl(), this.listAlign);
12276         //this.list.endUpdate();
12277     },
12278
12279     // private
12280     onEmptyResults : function(){
12281         
12282         if(this.tickable && this.editable){
12283             this.restrictHeight();
12284             return;
12285         }
12286         
12287         this.collapse();
12288     },
12289
12290     /**
12291      * Returns true if the dropdown list is expanded, else false.
12292      */
12293     isExpanded : function(){
12294         return this.list.isVisible();
12295     },
12296
12297     /**
12298      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12299      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12300      * @param {String} value The data value of the item to select
12301      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12302      * selected item if it is not currently in view (defaults to true)
12303      * @return {Boolean} True if the value matched an item in the list, else false
12304      */
12305     selectByValue : function(v, scrollIntoView){
12306         if(v !== undefined && v !== null){
12307             var r = this.findRecord(this.valueField || this.displayField, v);
12308             if(r){
12309                 this.select(this.store.indexOf(r), scrollIntoView);
12310                 return true;
12311             }
12312         }
12313         return false;
12314     },
12315
12316     /**
12317      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12318      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12319      * @param {Number} index The zero-based index of the list item to select
12320      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12321      * selected item if it is not currently in view (defaults to true)
12322      */
12323     select : function(index, scrollIntoView){
12324         this.selectedIndex = index;
12325         this.view.select(index);
12326         if(scrollIntoView !== false){
12327             var el = this.view.getNode(index);
12328             /*
12329              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12330              */
12331             if(el){
12332                 this.list.scrollChildIntoView(el, false);
12333             }
12334         }
12335     },
12336
12337     // private
12338     selectNext : function(){
12339         var ct = this.store.getCount();
12340         if(ct > 0){
12341             if(this.selectedIndex == -1){
12342                 this.select(0);
12343             }else if(this.selectedIndex < ct-1){
12344                 this.select(this.selectedIndex+1);
12345             }
12346         }
12347     },
12348
12349     // private
12350     selectPrev : function(){
12351         var ct = this.store.getCount();
12352         if(ct > 0){
12353             if(this.selectedIndex == -1){
12354                 this.select(0);
12355             }else if(this.selectedIndex != 0){
12356                 this.select(this.selectedIndex-1);
12357             }
12358         }
12359     },
12360
12361     // private
12362     onKeyUp : function(e){
12363         if(this.editable !== false && !e.isSpecialKey()){
12364             this.lastKey = e.getKey();
12365             this.dqTask.delay(this.queryDelay);
12366         }
12367     },
12368
12369     // private
12370     validateBlur : function(){
12371         return !this.list || !this.list.isVisible();   
12372     },
12373
12374     // private
12375     initQuery : function(){
12376         
12377         var v = this.getRawValue();
12378         
12379         if(this.tickable && this.editable){
12380             v = this.tickableInputEl().getValue();
12381         }
12382         
12383         this.doQuery(v);
12384     },
12385
12386     // private
12387     doForce : function(){
12388         if(this.inputEl().dom.value.length > 0){
12389             this.inputEl().dom.value =
12390                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12391              
12392         }
12393     },
12394
12395     /**
12396      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12397      * query allowing the query action to be canceled if needed.
12398      * @param {String} query The SQL query to execute
12399      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12400      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12401      * saved in the current store (defaults to false)
12402      */
12403     doQuery : function(q, forceAll){
12404         
12405         if(q === undefined || q === null){
12406             q = '';
12407         }
12408         var qe = {
12409             query: q,
12410             forceAll: forceAll,
12411             combo: this,
12412             cancel:false
12413         };
12414         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12415             return false;
12416         }
12417         q = qe.query;
12418         
12419         forceAll = qe.forceAll;
12420         if(forceAll === true || (q.length >= this.minChars)){
12421             
12422             this.hasQuery = true;
12423             
12424             if(this.lastQuery != q || this.alwaysQuery){
12425                 this.lastQuery = q;
12426                 if(this.mode == 'local'){
12427                     this.selectedIndex = -1;
12428                     if(forceAll){
12429                         this.store.clearFilter();
12430                     }else{
12431                         
12432                         if(this.specialFilter){
12433                             this.fireEvent('specialfilter', this);
12434                             this.onLoad();
12435                             return;
12436                         }
12437                         
12438                         this.store.filter(this.displayField, q);
12439                     }
12440                     
12441                     this.store.fireEvent("datachanged", this.store);
12442                     
12443                     this.onLoad();
12444                     
12445                     
12446                 }else{
12447                     
12448                     this.store.baseParams[this.queryParam] = q;
12449                     
12450                     var options = {params : this.getParams(q)};
12451                     
12452                     if(this.loadNext){
12453                         options.add = true;
12454                         options.params.start = this.page * this.pageSize;
12455                     }
12456                     
12457                     this.store.load(options);
12458                     
12459                     /*
12460                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12461                      *  we should expand the list on onLoad
12462                      *  so command out it
12463                      */
12464 //                    this.expand();
12465                 }
12466             }else{
12467                 this.selectedIndex = -1;
12468                 this.onLoad();   
12469             }
12470         }
12471         
12472         this.loadNext = false;
12473     },
12474     
12475     // private
12476     getParams : function(q){
12477         var p = {};
12478         //p[this.queryParam] = q;
12479         
12480         if(this.pageSize){
12481             p.start = 0;
12482             p.limit = this.pageSize;
12483         }
12484         return p;
12485     },
12486
12487     /**
12488      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12489      */
12490     collapse : function(){
12491         if(!this.isExpanded()){
12492             return;
12493         }
12494         
12495         this.list.hide();
12496         
12497         if(this.tickable){
12498             this.hasFocus = false;
12499             this.okBtn.hide();
12500             this.cancelBtn.hide();
12501             this.trigger.show();
12502             
12503             if(this.editable){
12504                 this.tickableInputEl().dom.value = '';
12505                 this.tickableInputEl().blur();
12506             }
12507             
12508         }
12509         
12510         Roo.get(document).un('mousedown', this.collapseIf, this);
12511         Roo.get(document).un('mousewheel', this.collapseIf, this);
12512         if (!this.editable) {
12513             Roo.get(document).un('keydown', this.listKeyPress, this);
12514         }
12515         this.fireEvent('collapse', this);
12516     },
12517
12518     // private
12519     collapseIf : function(e){
12520         var in_combo  = e.within(this.el);
12521         var in_list =  e.within(this.list);
12522         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12523         
12524         if (in_combo || in_list || is_list) {
12525             //e.stopPropagation();
12526             return;
12527         }
12528         
12529         if(this.tickable){
12530             this.onTickableFooterButtonClick(e, false, false);
12531         }
12532
12533         this.collapse();
12534         
12535     },
12536
12537     /**
12538      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12539      */
12540     expand : function(){
12541        
12542         if(this.isExpanded() || !this.hasFocus){
12543             return;
12544         }
12545         
12546         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12547         this.list.setWidth(lw);
12548         
12549         
12550          Roo.log('expand');
12551         
12552         this.list.show();
12553         
12554         this.restrictHeight();
12555         
12556         if(this.tickable){
12557             
12558             this.tickItems = Roo.apply([], this.item);
12559             
12560             this.okBtn.show();
12561             this.cancelBtn.show();
12562             this.trigger.hide();
12563             
12564             if(this.editable){
12565                 this.tickableInputEl().focus();
12566             }
12567             
12568         }
12569         
12570         Roo.get(document).on('mousedown', this.collapseIf, this);
12571         Roo.get(document).on('mousewheel', this.collapseIf, this);
12572         if (!this.editable) {
12573             Roo.get(document).on('keydown', this.listKeyPress, this);
12574         }
12575         
12576         this.fireEvent('expand', this);
12577     },
12578
12579     // private
12580     // Implements the default empty TriggerField.onTriggerClick function
12581     onTriggerClick : function(e)
12582     {
12583         Roo.log('trigger click');
12584         
12585         if(this.disabled || !this.triggerList){
12586             return;
12587         }
12588         
12589         this.page = 0;
12590         this.loadNext = false;
12591         
12592         if(this.isExpanded()){
12593             this.collapse();
12594             if (!this.blockFocus) {
12595                 this.inputEl().focus();
12596             }
12597             
12598         }else {
12599             this.hasFocus = true;
12600             if(this.triggerAction == 'all') {
12601                 this.doQuery(this.allQuery, true);
12602             } else {
12603                 this.doQuery(this.getRawValue());
12604             }
12605             if (!this.blockFocus) {
12606                 this.inputEl().focus();
12607             }
12608         }
12609     },
12610     
12611     onTickableTriggerClick : function(e)
12612     {
12613         if(this.disabled){
12614             return;
12615         }
12616         
12617         this.page = 0;
12618         this.loadNext = false;
12619         this.hasFocus = true;
12620         
12621         if(this.triggerAction == 'all') {
12622             this.doQuery(this.allQuery, true);
12623         } else {
12624             this.doQuery(this.getRawValue());
12625         }
12626     },
12627     
12628     onSearchFieldClick : function(e)
12629     {
12630         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12631             this.onTickableFooterButtonClick(e, false, false);
12632             return;
12633         }
12634         
12635         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12636             return;
12637         }
12638         
12639         this.page = 0;
12640         this.loadNext = false;
12641         this.hasFocus = true;
12642         
12643         if(this.triggerAction == 'all') {
12644             this.doQuery(this.allQuery, true);
12645         } else {
12646             this.doQuery(this.getRawValue());
12647         }
12648     },
12649     
12650     listKeyPress : function(e)
12651     {
12652         //Roo.log('listkeypress');
12653         // scroll to first matching element based on key pres..
12654         if (e.isSpecialKey()) {
12655             return false;
12656         }
12657         var k = String.fromCharCode(e.getKey()).toUpperCase();
12658         //Roo.log(k);
12659         var match  = false;
12660         var csel = this.view.getSelectedNodes();
12661         var cselitem = false;
12662         if (csel.length) {
12663             var ix = this.view.indexOf(csel[0]);
12664             cselitem  = this.store.getAt(ix);
12665             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12666                 cselitem = false;
12667             }
12668             
12669         }
12670         
12671         this.store.each(function(v) { 
12672             if (cselitem) {
12673                 // start at existing selection.
12674                 if (cselitem.id == v.id) {
12675                     cselitem = false;
12676                 }
12677                 return true;
12678             }
12679                 
12680             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12681                 match = this.store.indexOf(v);
12682                 return false;
12683             }
12684             return true;
12685         }, this);
12686         
12687         if (match === false) {
12688             return true; // no more action?
12689         }
12690         // scroll to?
12691         this.view.select(match);
12692         var sn = Roo.get(this.view.getSelectedNodes()[0])
12693         sn.scrollIntoView(sn.dom.parentNode, false);
12694     },
12695     
12696     onViewScroll : function(e, t){
12697         
12698         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){
12699             return;
12700         }
12701         
12702         this.hasQuery = true;
12703         
12704         this.loading = this.list.select('.loading', true).first();
12705         
12706         if(this.loading === null){
12707             this.list.createChild({
12708                 tag: 'div',
12709                 cls: 'loading select2-more-results select2-active',
12710                 html: 'Loading more results...'
12711             })
12712             
12713             this.loading = this.list.select('.loading', true).first();
12714             
12715             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12716             
12717             this.loading.hide();
12718         }
12719         
12720         this.loading.show();
12721         
12722         var _combo = this;
12723         
12724         this.page++;
12725         this.loadNext = true;
12726         
12727         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12728         
12729         return;
12730     },
12731     
12732     addItem : function(o)
12733     {   
12734         var dv = ''; // display value
12735         
12736         if (this.displayField) {
12737             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12738         } else {
12739             // this is an error condition!!!
12740             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12741         }
12742         
12743         if(!dv.length){
12744             return;
12745         }
12746         
12747         var choice = this.choices.createChild({
12748             tag: 'li',
12749             cls: 'select2-search-choice',
12750             cn: [
12751                 {
12752                     tag: 'div',
12753                     html: dv
12754                 },
12755                 {
12756                     tag: 'a',
12757                     href: '#',
12758                     cls: 'select2-search-choice-close',
12759                     tabindex: '-1'
12760                 }
12761             ]
12762             
12763         }, this.searchField);
12764         
12765         var close = choice.select('a.select2-search-choice-close', true).first()
12766         
12767         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12768         
12769         this.item.push(o);
12770         
12771         this.lastData = o;
12772         
12773         this.syncValue();
12774         
12775         this.inputEl().dom.value = '';
12776         
12777         this.validate();
12778     },
12779     
12780     onRemoveItem : function(e, _self, o)
12781     {
12782         e.preventDefault();
12783         
12784         this.lastItem = Roo.apply([], this.item);
12785         
12786         var index = this.item.indexOf(o.data) * 1;
12787         
12788         if( index < 0){
12789             Roo.log('not this item?!');
12790             return;
12791         }
12792         
12793         this.item.splice(index, 1);
12794         o.item.remove();
12795         
12796         this.syncValue();
12797         
12798         this.fireEvent('remove', this, e);
12799         
12800         this.validate();
12801         
12802     },
12803     
12804     syncValue : function()
12805     {
12806         if(!this.item.length){
12807             this.clearValue();
12808             return;
12809         }
12810             
12811         var value = [];
12812         var _this = this;
12813         Roo.each(this.item, function(i){
12814             if(_this.valueField){
12815                 value.push(i[_this.valueField]);
12816                 return;
12817             }
12818
12819             value.push(i);
12820         });
12821
12822         this.value = value.join(',');
12823
12824         if(this.hiddenField){
12825             this.hiddenField.dom.value = this.value;
12826         }
12827         
12828         this.store.fireEvent("datachanged", this.store);
12829     },
12830     
12831     clearItem : function()
12832     {
12833         if(!this.multiple){
12834             return;
12835         }
12836         
12837         this.item = [];
12838         
12839         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12840            c.remove();
12841         });
12842         
12843         this.syncValue();
12844         
12845         this.validate();
12846     },
12847     
12848     inputEl: function ()
12849     {
12850         if(this.tickable){
12851             return this.searchField;
12852         }
12853         return this.el.select('input.form-control',true).first();
12854     },
12855     
12856     
12857     onTickableFooterButtonClick : function(e, btn, el)
12858     {
12859         e.preventDefault();
12860         
12861         this.lastItem = Roo.apply([], this.item);
12862         
12863         if(btn && btn.name == 'cancel'){
12864             this.tickItems = Roo.apply([], this.item);
12865             this.collapse();
12866             return;
12867         }
12868         
12869         this.clearItem();
12870         
12871         var _this = this;
12872         
12873         Roo.each(this.tickItems, function(o){
12874             _this.addItem(o);
12875         });
12876         
12877         this.collapse();
12878         
12879     },
12880     
12881     validate : function()
12882     {
12883         var v = this.getRawValue();
12884         
12885         if(this.multiple){
12886             v = this.getValue();
12887         }
12888         
12889         if(this.disabled || this.allowBlank || v.length){
12890             this.markValid();
12891             return true;
12892         }
12893         
12894         this.markInvalid();
12895         return false;
12896     },
12897     
12898     tickableInputEl : function()
12899     {
12900         if(!this.tickable || !this.editable){
12901             return this.inputEl();
12902         }
12903         
12904         return this.inputEl().select('.select2-search-field-input', true).first();
12905     }
12906     
12907     
12908
12909     /** 
12910     * @cfg {Boolean} grow 
12911     * @hide 
12912     */
12913     /** 
12914     * @cfg {Number} growMin 
12915     * @hide 
12916     */
12917     /** 
12918     * @cfg {Number} growMax 
12919     * @hide 
12920     */
12921     /**
12922      * @hide
12923      * @method autoSize
12924      */
12925 });
12926 /*
12927  * Based on:
12928  * Ext JS Library 1.1.1
12929  * Copyright(c) 2006-2007, Ext JS, LLC.
12930  *
12931  * Originally Released Under LGPL - original licence link has changed is not relivant.
12932  *
12933  * Fork - LGPL
12934  * <script type="text/javascript">
12935  */
12936
12937 /**
12938  * @class Roo.View
12939  * @extends Roo.util.Observable
12940  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12941  * This class also supports single and multi selection modes. <br>
12942  * Create a data model bound view:
12943  <pre><code>
12944  var store = new Roo.data.Store(...);
12945
12946  var view = new Roo.View({
12947     el : "my-element",
12948     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12949  
12950     singleSelect: true,
12951     selectedClass: "ydataview-selected",
12952     store: store
12953  });
12954
12955  // listen for node click?
12956  view.on("click", function(vw, index, node, e){
12957  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12958  });
12959
12960  // load XML data
12961  dataModel.load("foobar.xml");
12962  </code></pre>
12963  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12964  * <br><br>
12965  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12966  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12967  * 
12968  * Note: old style constructor is still suported (container, template, config)
12969  * 
12970  * @constructor
12971  * Create a new View
12972  * @param {Object} config The config object
12973  * 
12974  */
12975 Roo.View = function(config, depreciated_tpl, depreciated_config){
12976     
12977     this.parent = false;
12978     
12979     if (typeof(depreciated_tpl) == 'undefined') {
12980         // new way.. - universal constructor.
12981         Roo.apply(this, config);
12982         this.el  = Roo.get(this.el);
12983     } else {
12984         // old format..
12985         this.el  = Roo.get(config);
12986         this.tpl = depreciated_tpl;
12987         Roo.apply(this, depreciated_config);
12988     }
12989     this.wrapEl  = this.el.wrap().wrap();
12990     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12991     
12992     
12993     if(typeof(this.tpl) == "string"){
12994         this.tpl = new Roo.Template(this.tpl);
12995     } else {
12996         // support xtype ctors..
12997         this.tpl = new Roo.factory(this.tpl, Roo);
12998     }
12999     
13000     
13001     this.tpl.compile();
13002     
13003     /** @private */
13004     this.addEvents({
13005         /**
13006          * @event beforeclick
13007          * Fires before a click is processed. Returns false to cancel the default action.
13008          * @param {Roo.View} this
13009          * @param {Number} index The index of the target node
13010          * @param {HTMLElement} node The target node
13011          * @param {Roo.EventObject} e The raw event object
13012          */
13013             "beforeclick" : true,
13014         /**
13015          * @event click
13016          * Fires when a template node is clicked.
13017          * @param {Roo.View} this
13018          * @param {Number} index The index of the target node
13019          * @param {HTMLElement} node The target node
13020          * @param {Roo.EventObject} e The raw event object
13021          */
13022             "click" : true,
13023         /**
13024          * @event dblclick
13025          * Fires when a template node is double clicked.
13026          * @param {Roo.View} this
13027          * @param {Number} index The index of the target node
13028          * @param {HTMLElement} node The target node
13029          * @param {Roo.EventObject} e The raw event object
13030          */
13031             "dblclick" : true,
13032         /**
13033          * @event contextmenu
13034          * Fires when a template node is right clicked.
13035          * @param {Roo.View} this
13036          * @param {Number} index The index of the target node
13037          * @param {HTMLElement} node The target node
13038          * @param {Roo.EventObject} e The raw event object
13039          */
13040             "contextmenu" : true,
13041         /**
13042          * @event selectionchange
13043          * Fires when the selected nodes change.
13044          * @param {Roo.View} this
13045          * @param {Array} selections Array of the selected nodes
13046          */
13047             "selectionchange" : true,
13048     
13049         /**
13050          * @event beforeselect
13051          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13052          * @param {Roo.View} this
13053          * @param {HTMLElement} node The node to be selected
13054          * @param {Array} selections Array of currently selected nodes
13055          */
13056             "beforeselect" : true,
13057         /**
13058          * @event preparedata
13059          * Fires on every row to render, to allow you to change the data.
13060          * @param {Roo.View} this
13061          * @param {Object} data to be rendered (change this)
13062          */
13063           "preparedata" : true
13064           
13065           
13066         });
13067
13068
13069
13070     this.el.on({
13071         "click": this.onClick,
13072         "dblclick": this.onDblClick,
13073         "contextmenu": this.onContextMenu,
13074         scope:this
13075     });
13076
13077     this.selections = [];
13078     this.nodes = [];
13079     this.cmp = new Roo.CompositeElementLite([]);
13080     if(this.store){
13081         this.store = Roo.factory(this.store, Roo.data);
13082         this.setStore(this.store, true);
13083     }
13084     
13085     if ( this.footer && this.footer.xtype) {
13086            
13087          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13088         
13089         this.footer.dataSource = this.store
13090         this.footer.container = fctr;
13091         this.footer = Roo.factory(this.footer, Roo);
13092         fctr.insertFirst(this.el);
13093         
13094         // this is a bit insane - as the paging toolbar seems to detach the el..
13095 //        dom.parentNode.parentNode.parentNode
13096          // they get detached?
13097     }
13098     
13099     
13100     Roo.View.superclass.constructor.call(this);
13101     
13102     
13103 };
13104
13105 Roo.extend(Roo.View, Roo.util.Observable, {
13106     
13107      /**
13108      * @cfg {Roo.data.Store} store Data store to load data from.
13109      */
13110     store : false,
13111     
13112     /**
13113      * @cfg {String|Roo.Element} el The container element.
13114      */
13115     el : '',
13116     
13117     /**
13118      * @cfg {String|Roo.Template} tpl The template used by this View 
13119      */
13120     tpl : false,
13121     /**
13122      * @cfg {String} dataName the named area of the template to use as the data area
13123      *                          Works with domtemplates roo-name="name"
13124      */
13125     dataName: false,
13126     /**
13127      * @cfg {String} selectedClass The css class to add to selected nodes
13128      */
13129     selectedClass : "x-view-selected",
13130      /**
13131      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13132      */
13133     emptyText : "",
13134     
13135     /**
13136      * @cfg {String} text to display on mask (default Loading)
13137      */
13138     mask : false,
13139     /**
13140      * @cfg {Boolean} multiSelect Allow multiple selection
13141      */
13142     multiSelect : false,
13143     /**
13144      * @cfg {Boolean} singleSelect Allow single selection
13145      */
13146     singleSelect:  false,
13147     
13148     /**
13149      * @cfg {Boolean} toggleSelect - selecting 
13150      */
13151     toggleSelect : false,
13152     
13153     /**
13154      * @cfg {Boolean} tickable - selecting 
13155      */
13156     tickable : false,
13157     
13158     /**
13159      * Returns the element this view is bound to.
13160      * @return {Roo.Element}
13161      */
13162     getEl : function(){
13163         return this.wrapEl;
13164     },
13165     
13166     
13167
13168     /**
13169      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13170      */
13171     refresh : function(){
13172         //Roo.log('refresh');
13173         var t = this.tpl;
13174         
13175         // if we are using something like 'domtemplate', then
13176         // the what gets used is:
13177         // t.applySubtemplate(NAME, data, wrapping data..)
13178         // the outer template then get' applied with
13179         //     the store 'extra data'
13180         // and the body get's added to the
13181         //      roo-name="data" node?
13182         //      <span class='roo-tpl-{name}'></span> ?????
13183         
13184         
13185         
13186         this.clearSelections();
13187         this.el.update("");
13188         var html = [];
13189         var records = this.store.getRange();
13190         if(records.length < 1) {
13191             
13192             // is this valid??  = should it render a template??
13193             
13194             this.el.update(this.emptyText);
13195             return;
13196         }
13197         var el = this.el;
13198         if (this.dataName) {
13199             this.el.update(t.apply(this.store.meta)); //????
13200             el = this.el.child('.roo-tpl-' + this.dataName);
13201         }
13202         
13203         for(var i = 0, len = records.length; i < len; i++){
13204             var data = this.prepareData(records[i].data, i, records[i]);
13205             this.fireEvent("preparedata", this, data, i, records[i]);
13206             
13207             var d = Roo.apply({}, data);
13208             
13209             if(this.tickable){
13210                 Roo.apply(d, {'roo-id' : Roo.id()});
13211                 
13212                 var _this = this;
13213             
13214                 Roo.each(this.parent.item, function(item){
13215                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13216                         return;
13217                     }
13218                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13219                 });
13220             }
13221             
13222             html[html.length] = Roo.util.Format.trim(
13223                 this.dataName ?
13224                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13225                     t.apply(d)
13226             );
13227         }
13228         
13229         
13230         
13231         el.update(html.join(""));
13232         this.nodes = el.dom.childNodes;
13233         this.updateIndexes(0);
13234     },
13235     
13236
13237     /**
13238      * Function to override to reformat the data that is sent to
13239      * the template for each node.
13240      * DEPRICATED - use the preparedata event handler.
13241      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13242      * a JSON object for an UpdateManager bound view).
13243      */
13244     prepareData : function(data, index, record)
13245     {
13246         this.fireEvent("preparedata", this, data, index, record);
13247         return data;
13248     },
13249
13250     onUpdate : function(ds, record){
13251         // Roo.log('on update');   
13252         this.clearSelections();
13253         var index = this.store.indexOf(record);
13254         var n = this.nodes[index];
13255         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13256         n.parentNode.removeChild(n);
13257         this.updateIndexes(index, index);
13258     },
13259
13260     
13261     
13262 // --------- FIXME     
13263     onAdd : function(ds, records, index)
13264     {
13265         //Roo.log(['on Add', ds, records, index] );        
13266         this.clearSelections();
13267         if(this.nodes.length == 0){
13268             this.refresh();
13269             return;
13270         }
13271         var n = this.nodes[index];
13272         for(var i = 0, len = records.length; i < len; i++){
13273             var d = this.prepareData(records[i].data, i, records[i]);
13274             if(n){
13275                 this.tpl.insertBefore(n, d);
13276             }else{
13277                 
13278                 this.tpl.append(this.el, d);
13279             }
13280         }
13281         this.updateIndexes(index);
13282     },
13283
13284     onRemove : function(ds, record, index){
13285        // Roo.log('onRemove');
13286         this.clearSelections();
13287         var el = this.dataName  ?
13288             this.el.child('.roo-tpl-' + this.dataName) :
13289             this.el; 
13290         
13291         el.dom.removeChild(this.nodes[index]);
13292         this.updateIndexes(index);
13293     },
13294
13295     /**
13296      * Refresh an individual node.
13297      * @param {Number} index
13298      */
13299     refreshNode : function(index){
13300         this.onUpdate(this.store, this.store.getAt(index));
13301     },
13302
13303     updateIndexes : function(startIndex, endIndex){
13304         var ns = this.nodes;
13305         startIndex = startIndex || 0;
13306         endIndex = endIndex || ns.length - 1;
13307         for(var i = startIndex; i <= endIndex; i++){
13308             ns[i].nodeIndex = i;
13309         }
13310     },
13311
13312     /**
13313      * Changes the data store this view uses and refresh the view.
13314      * @param {Store} store
13315      */
13316     setStore : function(store, initial){
13317         if(!initial && this.store){
13318             this.store.un("datachanged", this.refresh);
13319             this.store.un("add", this.onAdd);
13320             this.store.un("remove", this.onRemove);
13321             this.store.un("update", this.onUpdate);
13322             this.store.un("clear", this.refresh);
13323             this.store.un("beforeload", this.onBeforeLoad);
13324             this.store.un("load", this.onLoad);
13325             this.store.un("loadexception", this.onLoad);
13326         }
13327         if(store){
13328           
13329             store.on("datachanged", this.refresh, this);
13330             store.on("add", this.onAdd, this);
13331             store.on("remove", this.onRemove, this);
13332             store.on("update", this.onUpdate, this);
13333             store.on("clear", this.refresh, this);
13334             store.on("beforeload", this.onBeforeLoad, this);
13335             store.on("load", this.onLoad, this);
13336             store.on("loadexception", this.onLoad, this);
13337         }
13338         
13339         if(store){
13340             this.refresh();
13341         }
13342     },
13343     /**
13344      * onbeforeLoad - masks the loading area.
13345      *
13346      */
13347     onBeforeLoad : function(store,opts)
13348     {
13349          //Roo.log('onBeforeLoad');   
13350         if (!opts.add) {
13351             this.el.update("");
13352         }
13353         this.el.mask(this.mask ? this.mask : "Loading" ); 
13354     },
13355     onLoad : function ()
13356     {
13357         this.el.unmask();
13358     },
13359     
13360
13361     /**
13362      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13363      * @param {HTMLElement} node
13364      * @return {HTMLElement} The template node
13365      */
13366     findItemFromChild : function(node){
13367         var el = this.dataName  ?
13368             this.el.child('.roo-tpl-' + this.dataName,true) :
13369             this.el.dom; 
13370         
13371         if(!node || node.parentNode == el){
13372                     return node;
13373             }
13374             var p = node.parentNode;
13375             while(p && p != el){
13376             if(p.parentNode == el){
13377                 return p;
13378             }
13379             p = p.parentNode;
13380         }
13381             return null;
13382     },
13383
13384     /** @ignore */
13385     onClick : function(e){
13386         var item = this.findItemFromChild(e.getTarget());
13387         if(item){
13388             var index = this.indexOf(item);
13389             if(this.onItemClick(item, index, e) !== false){
13390                 this.fireEvent("click", this, index, item, e);
13391             }
13392         }else{
13393             this.clearSelections();
13394         }
13395     },
13396
13397     /** @ignore */
13398     onContextMenu : function(e){
13399         var item = this.findItemFromChild(e.getTarget());
13400         if(item){
13401             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13402         }
13403     },
13404
13405     /** @ignore */
13406     onDblClick : function(e){
13407         var item = this.findItemFromChild(e.getTarget());
13408         if(item){
13409             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13410         }
13411     },
13412
13413     onItemClick : function(item, index, e)
13414     {
13415         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13416             return false;
13417         }
13418         if (this.toggleSelect) {
13419             var m = this.isSelected(item) ? 'unselect' : 'select';
13420             //Roo.log(m);
13421             var _t = this;
13422             _t[m](item, true, false);
13423             return true;
13424         }
13425         if(this.multiSelect || this.singleSelect){
13426             if(this.multiSelect && e.shiftKey && this.lastSelection){
13427                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13428             }else{
13429                 this.select(item, this.multiSelect && e.ctrlKey);
13430                 this.lastSelection = item;
13431             }
13432             
13433             if(!this.tickable){
13434                 e.preventDefault();
13435             }
13436             
13437         }
13438         return true;
13439     },
13440
13441     /**
13442      * Get the number of selected nodes.
13443      * @return {Number}
13444      */
13445     getSelectionCount : function(){
13446         return this.selections.length;
13447     },
13448
13449     /**
13450      * Get the currently selected nodes.
13451      * @return {Array} An array of HTMLElements
13452      */
13453     getSelectedNodes : function(){
13454         return this.selections;
13455     },
13456
13457     /**
13458      * Get the indexes of the selected nodes.
13459      * @return {Array}
13460      */
13461     getSelectedIndexes : function(){
13462         var indexes = [], s = this.selections;
13463         for(var i = 0, len = s.length; i < len; i++){
13464             indexes.push(s[i].nodeIndex);
13465         }
13466         return indexes;
13467     },
13468
13469     /**
13470      * Clear all selections
13471      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13472      */
13473     clearSelections : function(suppressEvent){
13474         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13475             this.cmp.elements = this.selections;
13476             this.cmp.removeClass(this.selectedClass);
13477             this.selections = [];
13478             if(!suppressEvent){
13479                 this.fireEvent("selectionchange", this, this.selections);
13480             }
13481         }
13482     },
13483
13484     /**
13485      * Returns true if the passed node is selected
13486      * @param {HTMLElement/Number} node The node or node index
13487      * @return {Boolean}
13488      */
13489     isSelected : function(node){
13490         var s = this.selections;
13491         if(s.length < 1){
13492             return false;
13493         }
13494         node = this.getNode(node);
13495         return s.indexOf(node) !== -1;
13496     },
13497
13498     /**
13499      * Selects nodes.
13500      * @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
13501      * @param {Boolean} keepExisting (optional) true to keep existing selections
13502      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13503      */
13504     select : function(nodeInfo, keepExisting, suppressEvent){
13505         if(nodeInfo instanceof Array){
13506             if(!keepExisting){
13507                 this.clearSelections(true);
13508             }
13509             for(var i = 0, len = nodeInfo.length; i < len; i++){
13510                 this.select(nodeInfo[i], true, true);
13511             }
13512             return;
13513         } 
13514         var node = this.getNode(nodeInfo);
13515         if(!node || this.isSelected(node)){
13516             return; // already selected.
13517         }
13518         if(!keepExisting){
13519             this.clearSelections(true);
13520         }
13521         
13522         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13523             Roo.fly(node).addClass(this.selectedClass);
13524             this.selections.push(node);
13525             if(!suppressEvent){
13526                 this.fireEvent("selectionchange", this, this.selections);
13527             }
13528         }
13529         
13530         
13531     },
13532       /**
13533      * Unselects nodes.
13534      * @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
13535      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13536      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13537      */
13538     unselect : function(nodeInfo, keepExisting, suppressEvent)
13539     {
13540         if(nodeInfo instanceof Array){
13541             Roo.each(this.selections, function(s) {
13542                 this.unselect(s, nodeInfo);
13543             }, this);
13544             return;
13545         }
13546         var node = this.getNode(nodeInfo);
13547         if(!node || !this.isSelected(node)){
13548             //Roo.log("not selected");
13549             return; // not selected.
13550         }
13551         // fireevent???
13552         var ns = [];
13553         Roo.each(this.selections, function(s) {
13554             if (s == node ) {
13555                 Roo.fly(node).removeClass(this.selectedClass);
13556
13557                 return;
13558             }
13559             ns.push(s);
13560         },this);
13561         
13562         this.selections= ns;
13563         this.fireEvent("selectionchange", this, this.selections);
13564     },
13565
13566     /**
13567      * Gets a template node.
13568      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13569      * @return {HTMLElement} The node or null if it wasn't found
13570      */
13571     getNode : function(nodeInfo){
13572         if(typeof nodeInfo == "string"){
13573             return document.getElementById(nodeInfo);
13574         }else if(typeof nodeInfo == "number"){
13575             return this.nodes[nodeInfo];
13576         }
13577         return nodeInfo;
13578     },
13579
13580     /**
13581      * Gets a range template nodes.
13582      * @param {Number} startIndex
13583      * @param {Number} endIndex
13584      * @return {Array} An array of nodes
13585      */
13586     getNodes : function(start, end){
13587         var ns = this.nodes;
13588         start = start || 0;
13589         end = typeof end == "undefined" ? ns.length - 1 : end;
13590         var nodes = [];
13591         if(start <= end){
13592             for(var i = start; i <= end; i++){
13593                 nodes.push(ns[i]);
13594             }
13595         } else{
13596             for(var i = start; i >= end; i--){
13597                 nodes.push(ns[i]);
13598             }
13599         }
13600         return nodes;
13601     },
13602
13603     /**
13604      * Finds the index of the passed node
13605      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13606      * @return {Number} The index of the node or -1
13607      */
13608     indexOf : function(node){
13609         node = this.getNode(node);
13610         if(typeof node.nodeIndex == "number"){
13611             return node.nodeIndex;
13612         }
13613         var ns = this.nodes;
13614         for(var i = 0, len = ns.length; i < len; i++){
13615             if(ns[i] == node){
13616                 return i;
13617             }
13618         }
13619         return -1;
13620     }
13621 });
13622 /*
13623  * - LGPL
13624  *
13625  * based on jquery fullcalendar
13626  * 
13627  */
13628
13629 Roo.bootstrap = Roo.bootstrap || {};
13630 /**
13631  * @class Roo.bootstrap.Calendar
13632  * @extends Roo.bootstrap.Component
13633  * Bootstrap Calendar class
13634  * @cfg {Boolean} loadMask (true|false) default false
13635  * @cfg {Object} header generate the user specific header of the calendar, default false
13636
13637  * @constructor
13638  * Create a new Container
13639  * @param {Object} config The config object
13640  */
13641
13642
13643
13644 Roo.bootstrap.Calendar = function(config){
13645     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13646      this.addEvents({
13647         /**
13648              * @event select
13649              * Fires when a date is selected
13650              * @param {DatePicker} this
13651              * @param {Date} date The selected date
13652              */
13653         'select': true,
13654         /**
13655              * @event monthchange
13656              * Fires when the displayed month changes 
13657              * @param {DatePicker} this
13658              * @param {Date} date The selected month
13659              */
13660         'monthchange': true,
13661         /**
13662              * @event evententer
13663              * Fires when mouse over an event
13664              * @param {Calendar} this
13665              * @param {event} Event
13666              */
13667         'evententer': true,
13668         /**
13669              * @event eventleave
13670              * Fires when the mouse leaves an
13671              * @param {Calendar} this
13672              * @param {event}
13673              */
13674         'eventleave': true,
13675         /**
13676              * @event eventclick
13677              * Fires when the mouse click an
13678              * @param {Calendar} this
13679              * @param {event}
13680              */
13681         'eventclick': true
13682         
13683     });
13684
13685 };
13686
13687 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13688     
13689      /**
13690      * @cfg {Number} startDay
13691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13692      */
13693     startDay : 0,
13694     
13695     loadMask : false,
13696     
13697     header : false,
13698       
13699     getAutoCreate : function(){
13700         
13701         
13702         var fc_button = function(name, corner, style, content ) {
13703             return Roo.apply({},{
13704                 tag : 'span',
13705                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13706                          (corner.length ?
13707                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13708                             ''
13709                         ),
13710                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13711                 unselectable: 'on'
13712             });
13713         };
13714         
13715         var header = {};
13716         
13717         if(!this.header){
13718             header = {
13719                 tag : 'table',
13720                 cls : 'fc-header',
13721                 style : 'width:100%',
13722                 cn : [
13723                     {
13724                         tag: 'tr',
13725                         cn : [
13726                             {
13727                                 tag : 'td',
13728                                 cls : 'fc-header-left',
13729                                 cn : [
13730                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13731                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13732                                     { tag: 'span', cls: 'fc-header-space' },
13733                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13734
13735
13736                                 ]
13737                             },
13738
13739                             {
13740                                 tag : 'td',
13741                                 cls : 'fc-header-center',
13742                                 cn : [
13743                                     {
13744                                         tag: 'span',
13745                                         cls: 'fc-header-title',
13746                                         cn : {
13747                                             tag: 'H2',
13748                                             html : 'month / year'
13749                                         }
13750                                     }
13751
13752                                 ]
13753                             },
13754                             {
13755                                 tag : 'td',
13756                                 cls : 'fc-header-right',
13757                                 cn : [
13758                               /*      fc_button('month', 'left', '', 'month' ),
13759                                     fc_button('week', '', '', 'week' ),
13760                                     fc_button('day', 'right', '', 'day' )
13761                                 */    
13762
13763                                 ]
13764                             }
13765
13766                         ]
13767                     }
13768                 ]
13769             };
13770         }
13771         
13772         header = this.header;
13773         
13774        
13775         var cal_heads = function() {
13776             var ret = [];
13777             // fixme - handle this.
13778             
13779             for (var i =0; i < Date.dayNames.length; i++) {
13780                 var d = Date.dayNames[i];
13781                 ret.push({
13782                     tag: 'th',
13783                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13784                     html : d.substring(0,3)
13785                 });
13786                 
13787             }
13788             ret[0].cls += ' fc-first';
13789             ret[6].cls += ' fc-last';
13790             return ret;
13791         };
13792         var cal_cell = function(n) {
13793             return  {
13794                 tag: 'td',
13795                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13796                 cn : [
13797                     {
13798                         cn : [
13799                             {
13800                                 cls: 'fc-day-number',
13801                                 html: 'D'
13802                             },
13803                             {
13804                                 cls: 'fc-day-content',
13805                              
13806                                 cn : [
13807                                      {
13808                                         style: 'position: relative;' // height: 17px;
13809                                     }
13810                                 ]
13811                             }
13812                             
13813                             
13814                         ]
13815                     }
13816                 ]
13817                 
13818             }
13819         };
13820         var cal_rows = function() {
13821             
13822             var ret = [];
13823             for (var r = 0; r < 6; r++) {
13824                 var row= {
13825                     tag : 'tr',
13826                     cls : 'fc-week',
13827                     cn : []
13828                 };
13829                 
13830                 for (var i =0; i < Date.dayNames.length; i++) {
13831                     var d = Date.dayNames[i];
13832                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13833
13834                 }
13835                 row.cn[0].cls+=' fc-first';
13836                 row.cn[0].cn[0].style = 'min-height:90px';
13837                 row.cn[6].cls+=' fc-last';
13838                 ret.push(row);
13839                 
13840             }
13841             ret[0].cls += ' fc-first';
13842             ret[4].cls += ' fc-prev-last';
13843             ret[5].cls += ' fc-last';
13844             return ret;
13845             
13846         };
13847         
13848         var cal_table = {
13849             tag: 'table',
13850             cls: 'fc-border-separate',
13851             style : 'width:100%',
13852             cellspacing  : 0,
13853             cn : [
13854                 { 
13855                     tag: 'thead',
13856                     cn : [
13857                         { 
13858                             tag: 'tr',
13859                             cls : 'fc-first fc-last',
13860                             cn : cal_heads()
13861                         }
13862                     ]
13863                 },
13864                 { 
13865                     tag: 'tbody',
13866                     cn : cal_rows()
13867                 }
13868                   
13869             ]
13870         };
13871          
13872          var cfg = {
13873             cls : 'fc fc-ltr',
13874             cn : [
13875                 header,
13876                 {
13877                     cls : 'fc-content',
13878                     style : "position: relative;",
13879                     cn : [
13880                         {
13881                             cls : 'fc-view fc-view-month fc-grid',
13882                             style : 'position: relative',
13883                             unselectable : 'on',
13884                             cn : [
13885                                 {
13886                                     cls : 'fc-event-container',
13887                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13888                                 },
13889                                 cal_table
13890                             ]
13891                         }
13892                     ]
13893     
13894                 }
13895            ] 
13896             
13897         };
13898         
13899          
13900         
13901         return cfg;
13902     },
13903     
13904     
13905     initEvents : function()
13906     {
13907         if(!this.store){
13908             throw "can not find store for calendar";
13909         }
13910         
13911         var mark = {
13912             tag: "div",
13913             cls:"x-dlg-mask",
13914             style: "text-align:center",
13915             cn: [
13916                 {
13917                     tag: "div",
13918                     style: "background-color:white;width:50%;margin:250 auto",
13919                     cn: [
13920                         {
13921                             tag: "img",
13922                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13923                         },
13924                         {
13925                             tag: "span",
13926                             html: "Loading"
13927                         }
13928                         
13929                     ]
13930                 }
13931             ]
13932         }
13933         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13934         
13935         var size = this.el.select('.fc-content', true).first().getSize();
13936         this.maskEl.setSize(size.width, size.height);
13937         this.maskEl.enableDisplayMode("block");
13938         if(!this.loadMask){
13939             this.maskEl.hide();
13940         }
13941         
13942         this.store = Roo.factory(this.store, Roo.data);
13943         this.store.on('load', this.onLoad, this);
13944         this.store.on('beforeload', this.onBeforeLoad, this);
13945         
13946         this.resize();
13947         
13948         this.cells = this.el.select('.fc-day',true);
13949         //Roo.log(this.cells);
13950         this.textNodes = this.el.query('.fc-day-number');
13951         this.cells.addClassOnOver('fc-state-hover');
13952         
13953         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13954         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13955         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13956         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13957         
13958         this.on('monthchange', this.onMonthChange, this);
13959         
13960         this.update(new Date().clearTime());
13961     },
13962     
13963     resize : function() {
13964         var sz  = this.el.getSize();
13965         
13966         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13967         this.el.select('.fc-day-content div',true).setHeight(34);
13968     },
13969     
13970     
13971     // private
13972     showPrevMonth : function(e){
13973         this.update(this.activeDate.add("mo", -1));
13974     },
13975     showToday : function(e){
13976         this.update(new Date().clearTime());
13977     },
13978     // private
13979     showNextMonth : function(e){
13980         this.update(this.activeDate.add("mo", 1));
13981     },
13982
13983     // private
13984     showPrevYear : function(){
13985         this.update(this.activeDate.add("y", -1));
13986     },
13987
13988     // private
13989     showNextYear : function(){
13990         this.update(this.activeDate.add("y", 1));
13991     },
13992
13993     
13994    // private
13995     update : function(date)
13996     {
13997         var vd = this.activeDate;
13998         this.activeDate = date;
13999 //        if(vd && this.el){
14000 //            var t = date.getTime();
14001 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14002 //                Roo.log('using add remove');
14003 //                
14004 //                this.fireEvent('monthchange', this, date);
14005 //                
14006 //                this.cells.removeClass("fc-state-highlight");
14007 //                this.cells.each(function(c){
14008 //                   if(c.dateValue == t){
14009 //                       c.addClass("fc-state-highlight");
14010 //                       setTimeout(function(){
14011 //                            try{c.dom.firstChild.focus();}catch(e){}
14012 //                       }, 50);
14013 //                       return false;
14014 //                   }
14015 //                   return true;
14016 //                });
14017 //                return;
14018 //            }
14019 //        }
14020         
14021         var days = date.getDaysInMonth();
14022         
14023         var firstOfMonth = date.getFirstDateOfMonth();
14024         var startingPos = firstOfMonth.getDay()-this.startDay;
14025         
14026         if(startingPos < this.startDay){
14027             startingPos += 7;
14028         }
14029         
14030         var pm = date.add(Date.MONTH, -1);
14031         var prevStart = pm.getDaysInMonth()-startingPos;
14032 //        
14033         this.cells = this.el.select('.fc-day',true);
14034         this.textNodes = this.el.query('.fc-day-number');
14035         this.cells.addClassOnOver('fc-state-hover');
14036         
14037         var cells = this.cells.elements;
14038         var textEls = this.textNodes;
14039         
14040         Roo.each(cells, function(cell){
14041             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14042         });
14043         
14044         days += startingPos;
14045
14046         // convert everything to numbers so it's fast
14047         var day = 86400000;
14048         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14049         //Roo.log(d);
14050         //Roo.log(pm);
14051         //Roo.log(prevStart);
14052         
14053         var today = new Date().clearTime().getTime();
14054         var sel = date.clearTime().getTime();
14055         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14056         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14057         var ddMatch = this.disabledDatesRE;
14058         var ddText = this.disabledDatesText;
14059         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14060         var ddaysText = this.disabledDaysText;
14061         var format = this.format;
14062         
14063         var setCellClass = function(cal, cell){
14064             cell.row = 0;
14065             cell.events = [];
14066             cell.more = [];
14067             //Roo.log('set Cell Class');
14068             cell.title = "";
14069             var t = d.getTime();
14070             
14071             //Roo.log(d);
14072             
14073             cell.dateValue = t;
14074             if(t == today){
14075                 cell.className += " fc-today";
14076                 cell.className += " fc-state-highlight";
14077                 cell.title = cal.todayText;
14078             }
14079             if(t == sel){
14080                 // disable highlight in other month..
14081                 //cell.className += " fc-state-highlight";
14082                 
14083             }
14084             // disabling
14085             if(t < min) {
14086                 cell.className = " fc-state-disabled";
14087                 cell.title = cal.minText;
14088                 return;
14089             }
14090             if(t > max) {
14091                 cell.className = " fc-state-disabled";
14092                 cell.title = cal.maxText;
14093                 return;
14094             }
14095             if(ddays){
14096                 if(ddays.indexOf(d.getDay()) != -1){
14097                     cell.title = ddaysText;
14098                     cell.className = " fc-state-disabled";
14099                 }
14100             }
14101             if(ddMatch && format){
14102                 var fvalue = d.dateFormat(format);
14103                 if(ddMatch.test(fvalue)){
14104                     cell.title = ddText.replace("%0", fvalue);
14105                     cell.className = " fc-state-disabled";
14106                 }
14107             }
14108             
14109             if (!cell.initialClassName) {
14110                 cell.initialClassName = cell.dom.className;
14111             }
14112             
14113             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14114         };
14115
14116         var i = 0;
14117         
14118         for(; i < startingPos; i++) {
14119             textEls[i].innerHTML = (++prevStart);
14120             d.setDate(d.getDate()+1);
14121             
14122             cells[i].className = "fc-past fc-other-month";
14123             setCellClass(this, cells[i]);
14124         }
14125         
14126         var intDay = 0;
14127         
14128         for(; i < days; i++){
14129             intDay = i - startingPos + 1;
14130             textEls[i].innerHTML = (intDay);
14131             d.setDate(d.getDate()+1);
14132             
14133             cells[i].className = ''; // "x-date-active";
14134             setCellClass(this, cells[i]);
14135         }
14136         var extraDays = 0;
14137         
14138         for(; i < 42; i++) {
14139             textEls[i].innerHTML = (++extraDays);
14140             d.setDate(d.getDate()+1);
14141             
14142             cells[i].className = "fc-future fc-other-month";
14143             setCellClass(this, cells[i]);
14144         }
14145         
14146         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14147         
14148         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14149         
14150         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14151         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14152         
14153         if(totalRows != 6){
14154             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14155             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14156         }
14157         
14158         this.fireEvent('monthchange', this, date);
14159         
14160         
14161         /*
14162         if(!this.internalRender){
14163             var main = this.el.dom.firstChild;
14164             var w = main.offsetWidth;
14165             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14166             Roo.fly(main).setWidth(w);
14167             this.internalRender = true;
14168             // opera does not respect the auto grow header center column
14169             // then, after it gets a width opera refuses to recalculate
14170             // without a second pass
14171             if(Roo.isOpera && !this.secondPass){
14172                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14173                 this.secondPass = true;
14174                 this.update.defer(10, this, [date]);
14175             }
14176         }
14177         */
14178         
14179     },
14180     
14181     findCell : function(dt) {
14182         dt = dt.clearTime().getTime();
14183         var ret = false;
14184         this.cells.each(function(c){
14185             //Roo.log("check " +c.dateValue + '?=' + dt);
14186             if(c.dateValue == dt){
14187                 ret = c;
14188                 return false;
14189             }
14190             return true;
14191         });
14192         
14193         return ret;
14194     },
14195     
14196     findCells : function(ev) {
14197         var s = ev.start.clone().clearTime().getTime();
14198        // Roo.log(s);
14199         var e= ev.end.clone().clearTime().getTime();
14200        // Roo.log(e);
14201         var ret = [];
14202         this.cells.each(function(c){
14203              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14204             
14205             if(c.dateValue > e){
14206                 return ;
14207             }
14208             if(c.dateValue < s){
14209                 return ;
14210             }
14211             ret.push(c);
14212         });
14213         
14214         return ret;    
14215     },
14216     
14217 //    findBestRow: function(cells)
14218 //    {
14219 //        var ret = 0;
14220 //        
14221 //        for (var i =0 ; i < cells.length;i++) {
14222 //            ret  = Math.max(cells[i].rows || 0,ret);
14223 //        }
14224 //        return ret;
14225 //        
14226 //    },
14227     
14228     
14229     addItem : function(ev)
14230     {
14231         // look for vertical location slot in
14232         var cells = this.findCells(ev);
14233         
14234 //        ev.row = this.findBestRow(cells);
14235         
14236         // work out the location.
14237         
14238         var crow = false;
14239         var rows = [];
14240         for(var i =0; i < cells.length; i++) {
14241             
14242             cells[i].row = cells[0].row;
14243             
14244             if(i == 0){
14245                 cells[i].row = cells[i].row + 1;
14246             }
14247             
14248             if (!crow) {
14249                 crow = {
14250                     start : cells[i],
14251                     end :  cells[i]
14252                 };
14253                 continue;
14254             }
14255             if (crow.start.getY() == cells[i].getY()) {
14256                 // on same row.
14257                 crow.end = cells[i];
14258                 continue;
14259             }
14260             // different row.
14261             rows.push(crow);
14262             crow = {
14263                 start: cells[i],
14264                 end : cells[i]
14265             };
14266             
14267         }
14268         
14269         rows.push(crow);
14270         ev.els = [];
14271         ev.rows = rows;
14272         ev.cells = cells;
14273         
14274         cells[0].events.push(ev);
14275         
14276         this.calevents.push(ev);
14277     },
14278     
14279     clearEvents: function() {
14280         
14281         if(!this.calevents){
14282             return;
14283         }
14284         
14285         Roo.each(this.cells.elements, function(c){
14286             c.row = 0;
14287             c.events = [];
14288             c.more = [];
14289         });
14290         
14291         Roo.each(this.calevents, function(e) {
14292             Roo.each(e.els, function(el) {
14293                 el.un('mouseenter' ,this.onEventEnter, this);
14294                 el.un('mouseleave' ,this.onEventLeave, this);
14295                 el.remove();
14296             },this);
14297         },this);
14298         
14299         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14300             e.remove();
14301         });
14302         
14303     },
14304     
14305     renderEvents: function()
14306     {   
14307         var _this = this;
14308         
14309         this.cells.each(function(c) {
14310             
14311             if(c.row < 5){
14312                 return;
14313             }
14314             
14315             var ev = c.events;
14316             
14317             var r = 4;
14318             if(c.row != c.events.length){
14319                 r = 4 - (4 - (c.row - c.events.length));
14320             }
14321             
14322             c.events = ev.slice(0, r);
14323             c.more = ev.slice(r);
14324             
14325             if(c.more.length && c.more.length == 1){
14326                 c.events.push(c.more.pop());
14327             }
14328             
14329             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14330             
14331         });
14332             
14333         this.cells.each(function(c) {
14334             
14335             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14336             
14337             
14338             for (var e = 0; e < c.events.length; e++){
14339                 var ev = c.events[e];
14340                 var rows = ev.rows;
14341                 
14342                 for(var i = 0; i < rows.length; i++) {
14343                 
14344                     // how many rows should it span..
14345
14346                     var  cfg = {
14347                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14348                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14349
14350                         unselectable : "on",
14351                         cn : [
14352                             {
14353                                 cls: 'fc-event-inner',
14354                                 cn : [
14355     //                                {
14356     //                                  tag:'span',
14357     //                                  cls: 'fc-event-time',
14358     //                                  html : cells.length > 1 ? '' : ev.time
14359     //                                },
14360                                     {
14361                                       tag:'span',
14362                                       cls: 'fc-event-title',
14363                                       html : String.format('{0}', ev.title)
14364                                     }
14365
14366
14367                                 ]
14368                             },
14369                             {
14370                                 cls: 'ui-resizable-handle ui-resizable-e',
14371                                 html : '&nbsp;&nbsp;&nbsp'
14372                             }
14373
14374                         ]
14375                     };
14376
14377                     if (i == 0) {
14378                         cfg.cls += ' fc-event-start';
14379                     }
14380                     if ((i+1) == rows.length) {
14381                         cfg.cls += ' fc-event-end';
14382                     }
14383
14384                     var ctr = _this.el.select('.fc-event-container',true).first();
14385                     var cg = ctr.createChild(cfg);
14386
14387                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14388                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14389
14390                     var r = (c.more.length) ? 1 : 0;
14391                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14392                     cg.setWidth(ebox.right - sbox.x -2);
14393
14394                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14395                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14396                     cg.on('click', _this.onEventClick, _this, ev);
14397
14398                     ev.els.push(cg);
14399                     
14400                 }
14401                 
14402             }
14403             
14404             
14405             if(c.more.length){
14406                 var  cfg = {
14407                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14408                     style : 'position: absolute',
14409                     unselectable : "on",
14410                     cn : [
14411                         {
14412                             cls: 'fc-event-inner',
14413                             cn : [
14414                                 {
14415                                   tag:'span',
14416                                   cls: 'fc-event-title',
14417                                   html : 'More'
14418                                 }
14419
14420
14421                             ]
14422                         },
14423                         {
14424                             cls: 'ui-resizable-handle ui-resizable-e',
14425                             html : '&nbsp;&nbsp;&nbsp'
14426                         }
14427
14428                     ]
14429                 };
14430
14431                 var ctr = _this.el.select('.fc-event-container',true).first();
14432                 var cg = ctr.createChild(cfg);
14433
14434                 var sbox = c.select('.fc-day-content',true).first().getBox();
14435                 var ebox = c.select('.fc-day-content',true).first().getBox();
14436                 //Roo.log(cg);
14437                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14438                 cg.setWidth(ebox.right - sbox.x -2);
14439
14440                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14441                 
14442             }
14443             
14444         });
14445         
14446         
14447         
14448     },
14449     
14450     onEventEnter: function (e, el,event,d) {
14451         this.fireEvent('evententer', this, el, event);
14452     },
14453     
14454     onEventLeave: function (e, el,event,d) {
14455         this.fireEvent('eventleave', this, el, event);
14456     },
14457     
14458     onEventClick: function (e, el,event,d) {
14459         this.fireEvent('eventclick', this, el, event);
14460     },
14461     
14462     onMonthChange: function () {
14463         this.store.load();
14464     },
14465     
14466     onMoreEventClick: function(e, el, more)
14467     {
14468         var _this = this;
14469         
14470         this.calpopover.placement = 'right';
14471         this.calpopover.setTitle('More');
14472         
14473         this.calpopover.setContent('');
14474         
14475         var ctr = this.calpopover.el.select('.popover-content', true).first();
14476         
14477         Roo.each(more, function(m){
14478             var cfg = {
14479                 cls : 'fc-event-hori fc-event-draggable',
14480                 html : m.title
14481             }
14482             var cg = ctr.createChild(cfg);
14483             
14484             cg.on('click', _this.onEventClick, _this, m);
14485         });
14486         
14487         this.calpopover.show(el);
14488         
14489         
14490     },
14491     
14492     onLoad: function () 
14493     {   
14494         this.calevents = [];
14495         var cal = this;
14496         
14497         if(this.store.getCount() > 0){
14498             this.store.data.each(function(d){
14499                cal.addItem({
14500                     id : d.data.id,
14501                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14502                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14503                     time : d.data.start_time,
14504                     title : d.data.title,
14505                     description : d.data.description,
14506                     venue : d.data.venue
14507                 });
14508             });
14509         }
14510         
14511         this.renderEvents();
14512         
14513         if(this.calevents.length && this.loadMask){
14514             this.maskEl.hide();
14515         }
14516     },
14517     
14518     onBeforeLoad: function()
14519     {
14520         this.clearEvents();
14521         if(this.loadMask){
14522             this.maskEl.show();
14523         }
14524     }
14525 });
14526
14527  
14528  /*
14529  * - LGPL
14530  *
14531  * element
14532  * 
14533  */
14534
14535 /**
14536  * @class Roo.bootstrap.Popover
14537  * @extends Roo.bootstrap.Component
14538  * Bootstrap Popover class
14539  * @cfg {String} html contents of the popover   (or false to use children..)
14540  * @cfg {String} title of popover (or false to hide)
14541  * @cfg {String} placement how it is placed
14542  * @cfg {String} trigger click || hover (or false to trigger manually)
14543  * @cfg {String} over what (parent or false to trigger manually.)
14544  * @cfg {Number} delay - delay before showing
14545  
14546  * @constructor
14547  * Create a new Popover
14548  * @param {Object} config The config object
14549  */
14550
14551 Roo.bootstrap.Popover = function(config){
14552     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14553 };
14554
14555 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14556     
14557     title: 'Fill in a title',
14558     html: false,
14559     
14560     placement : 'right',
14561     trigger : 'hover', // hover
14562     
14563     delay : 0,
14564     
14565     over: 'parent',
14566     
14567     can_build_overlaid : false,
14568     
14569     getChildContainer : function()
14570     {
14571         return this.el.select('.popover-content',true).first();
14572     },
14573     
14574     getAutoCreate : function(){
14575          Roo.log('make popover?');
14576         var cfg = {
14577            cls : 'popover roo-dynamic',
14578            style: 'display:block',
14579            cn : [
14580                 {
14581                     cls : 'arrow'
14582                 },
14583                 {
14584                     cls : 'popover-inner',
14585                     cn : [
14586                         {
14587                             tag: 'h3',
14588                             cls: 'popover-title',
14589                             html : this.title
14590                         },
14591                         {
14592                             cls : 'popover-content',
14593                             html : this.html
14594                         }
14595                     ]
14596                     
14597                 }
14598            ]
14599         };
14600         
14601         return cfg;
14602     },
14603     setTitle: function(str)
14604     {
14605         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14606     },
14607     setContent: function(str)
14608     {
14609         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14610     },
14611     // as it get's added to the bottom of the page.
14612     onRender : function(ct, position)
14613     {
14614         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14615         if(!this.el){
14616             var cfg = Roo.apply({},  this.getAutoCreate());
14617             cfg.id = Roo.id();
14618             
14619             if (this.cls) {
14620                 cfg.cls += ' ' + this.cls;
14621             }
14622             if (this.style) {
14623                 cfg.style = this.style;
14624             }
14625             Roo.log("adding to ")
14626             this.el = Roo.get(document.body).createChild(cfg, position);
14627             Roo.log(this.el);
14628         }
14629         this.initEvents();
14630     },
14631     
14632     initEvents : function()
14633     {
14634         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14635         this.el.enableDisplayMode('block');
14636         this.el.hide();
14637         if (this.over === false) {
14638             return; 
14639         }
14640         if (this.triggers === false) {
14641             return;
14642         }
14643         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14644         var triggers = this.trigger ? this.trigger.split(' ') : [];
14645         Roo.each(triggers, function(trigger) {
14646         
14647             if (trigger == 'click') {
14648                 on_el.on('click', this.toggle, this);
14649             } else if (trigger != 'manual') {
14650                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14651                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14652       
14653                 on_el.on(eventIn  ,this.enter, this);
14654                 on_el.on(eventOut, this.leave, this);
14655             }
14656         }, this);
14657         
14658     },
14659     
14660     
14661     // private
14662     timeout : null,
14663     hoverState : null,
14664     
14665     toggle : function () {
14666         this.hoverState == 'in' ? this.leave() : this.enter();
14667     },
14668     
14669     enter : function () {
14670        
14671     
14672         clearTimeout(this.timeout);
14673     
14674         this.hoverState = 'in';
14675     
14676         if (!this.delay || !this.delay.show) {
14677             this.show();
14678             return;
14679         }
14680         var _t = this;
14681         this.timeout = setTimeout(function () {
14682             if (_t.hoverState == 'in') {
14683                 _t.show();
14684             }
14685         }, this.delay.show)
14686     },
14687     leave : function() {
14688         clearTimeout(this.timeout);
14689     
14690         this.hoverState = 'out';
14691     
14692         if (!this.delay || !this.delay.hide) {
14693             this.hide();
14694             return;
14695         }
14696         var _t = this;
14697         this.timeout = setTimeout(function () {
14698             if (_t.hoverState == 'out') {
14699                 _t.hide();
14700             }
14701         }, this.delay.hide)
14702     },
14703     
14704     show : function (on_el)
14705     {
14706         if (!on_el) {
14707             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14708         }
14709         // set content.
14710         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14711         if (this.html !== false) {
14712             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14713         }
14714         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14715         if (!this.title.length) {
14716             this.el.select('.popover-title',true).hide();
14717         }
14718         
14719         var placement = typeof this.placement == 'function' ?
14720             this.placement.call(this, this.el, on_el) :
14721             this.placement;
14722             
14723         var autoToken = /\s?auto?\s?/i;
14724         var autoPlace = autoToken.test(placement);
14725         if (autoPlace) {
14726             placement = placement.replace(autoToken, '') || 'top';
14727         }
14728         
14729         //this.el.detach()
14730         //this.el.setXY([0,0]);
14731         this.el.show();
14732         this.el.dom.style.display='block';
14733         this.el.addClass(placement);
14734         
14735         //this.el.appendTo(on_el);
14736         
14737         var p = this.getPosition();
14738         var box = this.el.getBox();
14739         
14740         if (autoPlace) {
14741             // fixme..
14742         }
14743         var align = Roo.bootstrap.Popover.alignment[placement];
14744         this.el.alignTo(on_el, align[0],align[1]);
14745         //var arrow = this.el.select('.arrow',true).first();
14746         //arrow.set(align[2], 
14747         
14748         this.el.addClass('in');
14749         this.hoverState = null;
14750         
14751         if (this.el.hasClass('fade')) {
14752             // fade it?
14753         }
14754         
14755     },
14756     hide : function()
14757     {
14758         this.el.setXY([0,0]);
14759         this.el.removeClass('in');
14760         this.el.hide();
14761         
14762     }
14763     
14764 });
14765
14766 Roo.bootstrap.Popover.alignment = {
14767     'left' : ['r-l', [-10,0], 'right'],
14768     'right' : ['l-r', [10,0], 'left'],
14769     'bottom' : ['t-b', [0,10], 'top'],
14770     'top' : [ 'b-t', [0,-10], 'bottom']
14771 };
14772
14773  /*
14774  * - LGPL
14775  *
14776  * Progress
14777  * 
14778  */
14779
14780 /**
14781  * @class Roo.bootstrap.Progress
14782  * @extends Roo.bootstrap.Component
14783  * Bootstrap Progress class
14784  * @cfg {Boolean} striped striped of the progress bar
14785  * @cfg {Boolean} active animated of the progress bar
14786  * 
14787  * 
14788  * @constructor
14789  * Create a new Progress
14790  * @param {Object} config The config object
14791  */
14792
14793 Roo.bootstrap.Progress = function(config){
14794     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14795 };
14796
14797 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14798     
14799     striped : false,
14800     active: false,
14801     
14802     getAutoCreate : function(){
14803         var cfg = {
14804             tag: 'div',
14805             cls: 'progress'
14806         };
14807         
14808         
14809         if(this.striped){
14810             cfg.cls += ' progress-striped';
14811         }
14812       
14813         if(this.active){
14814             cfg.cls += ' active';
14815         }
14816         
14817         
14818         return cfg;
14819     }
14820    
14821 });
14822
14823  
14824
14825  /*
14826  * - LGPL
14827  *
14828  * ProgressBar
14829  * 
14830  */
14831
14832 /**
14833  * @class Roo.bootstrap.ProgressBar
14834  * @extends Roo.bootstrap.Component
14835  * Bootstrap ProgressBar class
14836  * @cfg {Number} aria_valuenow aria-value now
14837  * @cfg {Number} aria_valuemin aria-value min
14838  * @cfg {Number} aria_valuemax aria-value max
14839  * @cfg {String} label label for the progress bar
14840  * @cfg {String} panel (success | info | warning | danger )
14841  * @cfg {String} role role of the progress bar
14842  * @cfg {String} sr_only text
14843  * 
14844  * 
14845  * @constructor
14846  * Create a new ProgressBar
14847  * @param {Object} config The config object
14848  */
14849
14850 Roo.bootstrap.ProgressBar = function(config){
14851     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14852 };
14853
14854 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14855     
14856     aria_valuenow : 0,
14857     aria_valuemin : 0,
14858     aria_valuemax : 100,
14859     label : false,
14860     panel : false,
14861     role : false,
14862     sr_only: false,
14863     
14864     getAutoCreate : function()
14865     {
14866         
14867         var cfg = {
14868             tag: 'div',
14869             cls: 'progress-bar',
14870             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14871         };
14872         
14873         if(this.sr_only){
14874             cfg.cn = {
14875                 tag: 'span',
14876                 cls: 'sr-only',
14877                 html: this.sr_only
14878             }
14879         }
14880         
14881         if(this.role){
14882             cfg.role = this.role;
14883         }
14884         
14885         if(this.aria_valuenow){
14886             cfg['aria-valuenow'] = this.aria_valuenow;
14887         }
14888         
14889         if(this.aria_valuemin){
14890             cfg['aria-valuemin'] = this.aria_valuemin;
14891         }
14892         
14893         if(this.aria_valuemax){
14894             cfg['aria-valuemax'] = this.aria_valuemax;
14895         }
14896         
14897         if(this.label && !this.sr_only){
14898             cfg.html = this.label;
14899         }
14900         
14901         if(this.panel){
14902             cfg.cls += ' progress-bar-' + this.panel;
14903         }
14904         
14905         return cfg;
14906     },
14907     
14908     update : function(aria_valuenow)
14909     {
14910         this.aria_valuenow = aria_valuenow;
14911         
14912         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14913     }
14914    
14915 });
14916
14917  
14918
14919  /*
14920  * - LGPL
14921  *
14922  * column
14923  * 
14924  */
14925
14926 /**
14927  * @class Roo.bootstrap.TabGroup
14928  * @extends Roo.bootstrap.Column
14929  * Bootstrap Column class
14930  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14931  * @cfg {Boolean} carousel true to make the group behave like a carousel
14932  * @cfg {Number} bullets show the panel pointer.. default 0
14933  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14934  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14935  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14936  * 
14937  * @constructor
14938  * Create a new TabGroup
14939  * @param {Object} config The config object
14940  */
14941
14942 Roo.bootstrap.TabGroup = function(config){
14943     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14944     if (!this.navId) {
14945         this.navId = Roo.id();
14946     }
14947     this.tabs = [];
14948     Roo.bootstrap.TabGroup.register(this);
14949     
14950 };
14951
14952 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14953     
14954     carousel : false,
14955     transition : false,
14956     bullets : 0,
14957     timer : 0,
14958     autoslide : false,
14959     slideFn : false,
14960     slideOnTouch : false,
14961     
14962     getAutoCreate : function()
14963     {
14964         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14965         
14966         cfg.cls += ' tab-content';
14967         
14968         Roo.log('get auto create...............');
14969         
14970         if (this.carousel) {
14971             cfg.cls += ' carousel slide';
14972             
14973             cfg.cn = [{
14974                cls : 'carousel-inner'
14975             }];
14976         
14977             if(this.bullets > 0 && !Roo.isTouch){
14978                 
14979                 var bullets = {
14980                     cls : 'carousel-bullets',
14981                     cn : []
14982                 };
14983                 
14984                 if(this.bullets_cls){
14985                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14986                 }
14987                 
14988                 for (var i = 0; i < this.bullets; i++){
14989                     bullets.cn.push({
14990                         cls : 'bullet bullet-' + i
14991                     });
14992                 }
14993                 
14994                 bullets.cn.push({
14995                     cls : 'clear'
14996                 });
14997                 
14998                 cfg.cn[0].cn = bullets;
14999             }
15000         }
15001         
15002         return cfg;
15003     },
15004     
15005     initEvents:  function()
15006     {
15007         Roo.log('-------- init events on tab group ---------');
15008         
15009         if(this.bullets > 0 && !Roo.isTouch){
15010             this.initBullet();
15011         }
15012         
15013         Roo.log(this);
15014         
15015         if(Roo.isTouch && this.slideOnTouch){
15016             this.el.on("touchstart", this.onTouchStart, this);
15017         }
15018         
15019         if(this.autoslide){
15020             var _this = this;
15021             
15022             this.slideFn = window.setInterval(function() {
15023                 _this.showPanelNext();
15024             }, this.timer);
15025         }
15026         
15027     },
15028     
15029     onTouchStart : function(e, el, o)
15030     {
15031         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15032             return;
15033         }
15034         
15035         this.showPanelNext();
15036     },
15037     
15038     getChildContainer : function()
15039     {
15040         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15041     },
15042     
15043     /**
15044     * register a Navigation item
15045     * @param {Roo.bootstrap.NavItem} the navitem to add
15046     */
15047     register : function(item)
15048     {
15049         this.tabs.push( item);
15050         item.navId = this.navId; // not really needed..
15051     
15052     },
15053     
15054     getActivePanel : function()
15055     {
15056         var r = false;
15057         Roo.each(this.tabs, function(t) {
15058             if (t.active) {
15059                 r = t;
15060                 return false;
15061             }
15062             return null;
15063         });
15064         return r;
15065         
15066     },
15067     getPanelByName : function(n)
15068     {
15069         var r = false;
15070         Roo.each(this.tabs, function(t) {
15071             if (t.tabId == n) {
15072                 r = t;
15073                 return false;
15074             }
15075             return null;
15076         });
15077         return r;
15078     },
15079     indexOfPanel : function(p)
15080     {
15081         var r = false;
15082         Roo.each(this.tabs, function(t,i) {
15083             if (t.tabId == p.tabId) {
15084                 r = i;
15085                 return false;
15086             }
15087             return null;
15088         });
15089         return r;
15090     },
15091     /**
15092      * show a specific panel
15093      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15094      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15095      */
15096     showPanel : function (pan)
15097     {
15098         if(this.transition){
15099             Roo.log("waiting for the transitionend");
15100             return;
15101         }
15102         
15103         if (typeof(pan) == 'number') {
15104             pan = this.tabs[pan];
15105         }
15106         if (typeof(pan) == 'string') {
15107             pan = this.getPanelByName(pan);
15108         }
15109         if (pan.tabId == this.getActivePanel().tabId) {
15110             return true;
15111         }
15112         var cur = this.getActivePanel();
15113         
15114         if (false === cur.fireEvent('beforedeactivate')) {
15115             return false;
15116         }
15117         
15118         if(this.bullets > 0 && !Roo.isTouch){
15119             this.setActiveBullet(this.indexOfPanel(pan));
15120         }
15121         
15122         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15123             
15124             this.transition = true;
15125             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15126             var lr = dir == 'next' ? 'left' : 'right';
15127             pan.el.addClass(dir); // or prev
15128             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15129             cur.el.addClass(lr); // or right
15130             pan.el.addClass(lr);
15131             
15132             var _this = this;
15133             cur.el.on('transitionend', function() {
15134                 Roo.log("trans end?");
15135                 
15136                 pan.el.removeClass([lr,dir]);
15137                 pan.setActive(true);
15138                 
15139                 cur.el.removeClass([lr]);
15140                 cur.setActive(false);
15141                 
15142                 _this.transition = false;
15143                 
15144             }, this, { single:  true } );
15145             
15146             return true;
15147         }
15148         
15149         cur.setActive(false);
15150         pan.setActive(true);
15151         
15152         return true;
15153         
15154     },
15155     showPanelNext : function()
15156     {
15157         var i = this.indexOfPanel(this.getActivePanel());
15158         
15159         if (i >= this.tabs.length - 1 && !this.autoslide) {
15160             return;
15161         }
15162         
15163         if (i >= this.tabs.length - 1 && this.autoslide) {
15164             i = -1;
15165         }
15166         
15167         this.showPanel(this.tabs[i+1]);
15168     },
15169     
15170     showPanelPrev : function()
15171     {
15172         var i = this.indexOfPanel(this.getActivePanel());
15173         
15174         if (i  < 1 && !this.autoslide) {
15175             return;
15176         }
15177         
15178         if (i < 1 && this.autoslide) {
15179             i = this.tabs.length;
15180         }
15181         
15182         this.showPanel(this.tabs[i-1]);
15183     },
15184     
15185     initBullet : function()
15186     {
15187         if(Roo.isTouch){
15188             return;
15189         }
15190         
15191         var _this = this;
15192         
15193         for (var i = 0; i < this.bullets; i++){
15194             var bullet = this.el.select('.bullet-' + i, true).first();
15195
15196             if(!bullet){
15197                 continue;
15198             }
15199
15200             bullet.on('click', (function(e, el, o, ii, t){
15201
15202                 e.preventDefault();
15203
15204                 _this.showPanel(ii);
15205
15206                 if(_this.autoslide && _this.slideFn){
15207                     clearInterval(_this.slideFn);
15208                     _this.slideFn = window.setInterval(function() {
15209                         _this.showPanelNext();
15210                     }, _this.timer);
15211                 }
15212
15213             }).createDelegate(this, [i, bullet], true));
15214         }
15215     },
15216     
15217     setActiveBullet : function(i)
15218     {
15219         if(Roo.isTouch){
15220             return;
15221         }
15222         
15223         Roo.each(this.el.select('.bullet', true).elements, function(el){
15224             el.removeClass('selected');
15225         });
15226
15227         var bullet = this.el.select('.bullet-' + i, true).first();
15228         
15229         if(!bullet){
15230             return;
15231         }
15232         
15233         bullet.addClass('selected');
15234     }
15235     
15236     
15237   
15238 });
15239
15240  
15241
15242  
15243  
15244 Roo.apply(Roo.bootstrap.TabGroup, {
15245     
15246     groups: {},
15247      /**
15248     * register a Navigation Group
15249     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15250     */
15251     register : function(navgrp)
15252     {
15253         this.groups[navgrp.navId] = navgrp;
15254         
15255     },
15256     /**
15257     * fetch a Navigation Group based on the navigation ID
15258     * if one does not exist , it will get created.
15259     * @param {string} the navgroup to add
15260     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15261     */
15262     get: function(navId) {
15263         if (typeof(this.groups[navId]) == 'undefined') {
15264             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15265         }
15266         return this.groups[navId] ;
15267     }
15268     
15269     
15270     
15271 });
15272
15273  /*
15274  * - LGPL
15275  *
15276  * TabPanel
15277  * 
15278  */
15279
15280 /**
15281  * @class Roo.bootstrap.TabPanel
15282  * @extends Roo.bootstrap.Component
15283  * Bootstrap TabPanel class
15284  * @cfg {Boolean} active panel active
15285  * @cfg {String} html panel content
15286  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15287  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15288  * 
15289  * 
15290  * @constructor
15291  * Create a new TabPanel
15292  * @param {Object} config The config object
15293  */
15294
15295 Roo.bootstrap.TabPanel = function(config){
15296     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15297     this.addEvents({
15298         /**
15299              * @event changed
15300              * Fires when the active status changes
15301              * @param {Roo.bootstrap.TabPanel} this
15302              * @param {Boolean} state the new state
15303             
15304          */
15305         'changed': true,
15306         /**
15307              * @event beforedeactivate
15308              * Fires before a tab is de-activated - can be used to do validation on a form.
15309              * @param {Roo.bootstrap.TabPanel} this
15310              * @return {Boolean} false if there is an error
15311             
15312          */
15313         'beforedeactivate': true
15314      });
15315     
15316     this.tabId = this.tabId || Roo.id();
15317   
15318 };
15319
15320 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15321     
15322     active: false,
15323     html: false,
15324     tabId: false,
15325     navId : false,
15326     
15327     getAutoCreate : function(){
15328         var cfg = {
15329             tag: 'div',
15330             // item is needed for carousel - not sure if it has any effect otherwise
15331             cls: 'tab-pane item',
15332             html: this.html || ''
15333         };
15334         
15335         if(this.active){
15336             cfg.cls += ' active';
15337         }
15338         
15339         if(this.tabId){
15340             cfg.tabId = this.tabId;
15341         }
15342         
15343         
15344         return cfg;
15345     },
15346     
15347     initEvents:  function()
15348     {
15349         Roo.log('-------- init events on tab panel ---------');
15350         
15351         var p = this.parent();
15352         this.navId = this.navId || p.navId;
15353         
15354         if (typeof(this.navId) != 'undefined') {
15355             // not really needed.. but just in case.. parent should be a NavGroup.
15356             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15357             Roo.log(['register', tg, this]);
15358             tg.register(this);
15359             
15360             var i = tg.tabs.length - 1;
15361             
15362             if(this.active && tg.bullets > 0 && i < tg.bullets){
15363                 tg.setActiveBullet(i);
15364             }
15365         }
15366         
15367     },
15368     
15369     
15370     onRender : function(ct, position)
15371     {
15372        // Roo.log("Call onRender: " + this.xtype);
15373         
15374         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15375         
15376         
15377         
15378         
15379         
15380     },
15381     
15382     setActive: function(state)
15383     {
15384         Roo.log("panel - set active " + this.tabId + "=" + state);
15385         
15386         this.active = state;
15387         if (!state) {
15388             this.el.removeClass('active');
15389             
15390         } else  if (!this.el.hasClass('active')) {
15391             this.el.addClass('active');
15392         }
15393         
15394         this.fireEvent('changed', this, state);
15395     }
15396     
15397     
15398 });
15399  
15400
15401  
15402
15403  /*
15404  * - LGPL
15405  *
15406  * DateField
15407  * 
15408  */
15409
15410 /**
15411  * @class Roo.bootstrap.DateField
15412  * @extends Roo.bootstrap.Input
15413  * Bootstrap DateField class
15414  * @cfg {Number} weekStart default 0
15415  * @cfg {String} viewMode default empty, (months|years)
15416  * @cfg {String} minViewMode default empty, (months|years)
15417  * @cfg {Number} startDate default -Infinity
15418  * @cfg {Number} endDate default Infinity
15419  * @cfg {Boolean} todayHighlight default false
15420  * @cfg {Boolean} todayBtn default false
15421  * @cfg {Boolean} calendarWeeks default false
15422  * @cfg {Object} daysOfWeekDisabled default empty
15423  * @cfg {Boolean} singleMode default false (true | false)
15424  * 
15425  * @cfg {Boolean} keyboardNavigation default true
15426  * @cfg {String} language default en
15427  * 
15428  * @constructor
15429  * Create a new DateField
15430  * @param {Object} config The config object
15431  */
15432
15433 Roo.bootstrap.DateField = function(config){
15434     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15435      this.addEvents({
15436             /**
15437              * @event show
15438              * Fires when this field show.
15439              * @param {Roo.bootstrap.DateField} this
15440              * @param {Mixed} date The date value
15441              */
15442             show : true,
15443             /**
15444              * @event show
15445              * Fires when this field hide.
15446              * @param {Roo.bootstrap.DateField} this
15447              * @param {Mixed} date The date value
15448              */
15449             hide : true,
15450             /**
15451              * @event select
15452              * Fires when select a date.
15453              * @param {Roo.bootstrap.DateField} this
15454              * @param {Mixed} date The date value
15455              */
15456             select : true
15457         });
15458 };
15459
15460 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15461     
15462     /**
15463      * @cfg {String} format
15464      * The default date format string which can be overriden for localization support.  The format must be
15465      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15466      */
15467     format : "m/d/y",
15468     /**
15469      * @cfg {String} altFormats
15470      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15471      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15472      */
15473     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15474     
15475     weekStart : 0,
15476     
15477     viewMode : '',
15478     
15479     minViewMode : '',
15480     
15481     todayHighlight : false,
15482     
15483     todayBtn: false,
15484     
15485     language: 'en',
15486     
15487     keyboardNavigation: true,
15488     
15489     calendarWeeks: false,
15490     
15491     startDate: -Infinity,
15492     
15493     endDate: Infinity,
15494     
15495     daysOfWeekDisabled: [],
15496     
15497     _events: [],
15498     
15499     singleMode : false,
15500     
15501     UTCDate: function()
15502     {
15503         return new Date(Date.UTC.apply(Date, arguments));
15504     },
15505     
15506     UTCToday: function()
15507     {
15508         var today = new Date();
15509         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15510     },
15511     
15512     getDate: function() {
15513             var d = this.getUTCDate();
15514             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15515     },
15516     
15517     getUTCDate: function() {
15518             return this.date;
15519     },
15520     
15521     setDate: function(d) {
15522             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15523     },
15524     
15525     setUTCDate: function(d) {
15526             this.date = d;
15527             this.setValue(this.formatDate(this.date));
15528     },
15529         
15530     onRender: function(ct, position)
15531     {
15532         
15533         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15534         
15535         this.language = this.language || 'en';
15536         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15537         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15538         
15539         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15540         this.format = this.format || 'm/d/y';
15541         this.isInline = false;
15542         this.isInput = true;
15543         this.component = this.el.select('.add-on', true).first() || false;
15544         this.component = (this.component && this.component.length === 0) ? false : this.component;
15545         this.hasInput = this.component && this.inputEL().length;
15546         
15547         if (typeof(this.minViewMode === 'string')) {
15548             switch (this.minViewMode) {
15549                 case 'months':
15550                     this.minViewMode = 1;
15551                     break;
15552                 case 'years':
15553                     this.minViewMode = 2;
15554                     break;
15555                 default:
15556                     this.minViewMode = 0;
15557                     break;
15558             }
15559         }
15560         
15561         if (typeof(this.viewMode === 'string')) {
15562             switch (this.viewMode) {
15563                 case 'months':
15564                     this.viewMode = 1;
15565                     break;
15566                 case 'years':
15567                     this.viewMode = 2;
15568                     break;
15569                 default:
15570                     this.viewMode = 0;
15571                     break;
15572             }
15573         }
15574                 
15575         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15576         
15577 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15578         
15579         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15580         
15581         this.picker().on('mousedown', this.onMousedown, this);
15582         this.picker().on('click', this.onClick, this);
15583         
15584         this.picker().addClass('datepicker-dropdown');
15585         
15586         this.startViewMode = this.viewMode;
15587         
15588         if(this.singleMode){
15589             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15590                 v.setVisibilityMode(Roo.Element.DISPLAY)
15591                 v.hide();
15592             });
15593             
15594             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15595                 v.setStyle('width', '189px');
15596             });
15597         }
15598         
15599         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15600             if(!this.calendarWeeks){
15601                 v.remove();
15602                 return;
15603             }
15604             
15605             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15606             v.attr('colspan', function(i, val){
15607                 return parseInt(val) + 1;
15608             });
15609         })
15610                         
15611         
15612         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15613         
15614         this.setStartDate(this.startDate);
15615         this.setEndDate(this.endDate);
15616         
15617         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15618         
15619         this.fillDow();
15620         this.fillMonths();
15621         this.update();
15622         this.showMode();
15623         
15624         if(this.isInline) {
15625             this.show();
15626         }
15627     },
15628     
15629     picker : function()
15630     {
15631         return this.pickerEl;
15632 //        return this.el.select('.datepicker', true).first();
15633     },
15634     
15635     fillDow: function()
15636     {
15637         var dowCnt = this.weekStart;
15638         
15639         var dow = {
15640             tag: 'tr',
15641             cn: [
15642                 
15643             ]
15644         };
15645         
15646         if(this.calendarWeeks){
15647             dow.cn.push({
15648                 tag: 'th',
15649                 cls: 'cw',
15650                 html: '&nbsp;'
15651             })
15652         }
15653         
15654         while (dowCnt < this.weekStart + 7) {
15655             dow.cn.push({
15656                 tag: 'th',
15657                 cls: 'dow',
15658                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15659             });
15660         }
15661         
15662         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15663     },
15664     
15665     fillMonths: function()
15666     {    
15667         var i = 0;
15668         var months = this.picker().select('>.datepicker-months td', true).first();
15669         
15670         months.dom.innerHTML = '';
15671         
15672         while (i < 12) {
15673             var month = {
15674                 tag: 'span',
15675                 cls: 'month',
15676                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15677             }
15678             
15679             months.createChild(month);
15680         }
15681         
15682     },
15683     
15684     update: function()
15685     {
15686         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;
15687         
15688         if (this.date < this.startDate) {
15689             this.viewDate = new Date(this.startDate);
15690         } else if (this.date > this.endDate) {
15691             this.viewDate = new Date(this.endDate);
15692         } else {
15693             this.viewDate = new Date(this.date);
15694         }
15695         
15696         this.fill();
15697     },
15698     
15699     fill: function() 
15700     {
15701         var d = new Date(this.viewDate),
15702                 year = d.getUTCFullYear(),
15703                 month = d.getUTCMonth(),
15704                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15705                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15706                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15707                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15708                 currentDate = this.date && this.date.valueOf(),
15709                 today = this.UTCToday();
15710         
15711         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15712         
15713 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15714         
15715 //        this.picker.select('>tfoot th.today').
15716 //                                              .text(dates[this.language].today)
15717 //                                              .toggle(this.todayBtn !== false);
15718     
15719         this.updateNavArrows();
15720         this.fillMonths();
15721                                                 
15722         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15723         
15724         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15725          
15726         prevMonth.setUTCDate(day);
15727         
15728         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15729         
15730         var nextMonth = new Date(prevMonth);
15731         
15732         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15733         
15734         nextMonth = nextMonth.valueOf();
15735         
15736         var fillMonths = false;
15737         
15738         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15739         
15740         while(prevMonth.valueOf() < nextMonth) {
15741             var clsName = '';
15742             
15743             if (prevMonth.getUTCDay() === this.weekStart) {
15744                 if(fillMonths){
15745                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15746                 }
15747                     
15748                 fillMonths = {
15749                     tag: 'tr',
15750                     cn: []
15751                 };
15752                 
15753                 if(this.calendarWeeks){
15754                     // ISO 8601: First week contains first thursday.
15755                     // ISO also states week starts on Monday, but we can be more abstract here.
15756                     var
15757                     // Start of current week: based on weekstart/current date
15758                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15759                     // Thursday of this week
15760                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15761                     // First Thursday of year, year from thursday
15762                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15763                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15764                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15765                     
15766                     fillMonths.cn.push({
15767                         tag: 'td',
15768                         cls: 'cw',
15769                         html: calWeek
15770                     });
15771                 }
15772             }
15773             
15774             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15775                 clsName += ' old';
15776             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15777                 clsName += ' new';
15778             }
15779             if (this.todayHighlight &&
15780                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15781                 prevMonth.getUTCMonth() == today.getMonth() &&
15782                 prevMonth.getUTCDate() == today.getDate()) {
15783                 clsName += ' today';
15784             }
15785             
15786             if (currentDate && prevMonth.valueOf() === currentDate) {
15787                 clsName += ' active';
15788             }
15789             
15790             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15791                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15792                     clsName += ' disabled';
15793             }
15794             
15795             fillMonths.cn.push({
15796                 tag: 'td',
15797                 cls: 'day ' + clsName,
15798                 html: prevMonth.getDate()
15799             })
15800             
15801             prevMonth.setDate(prevMonth.getDate()+1);
15802         }
15803           
15804         var currentYear = this.date && this.date.getUTCFullYear();
15805         var currentMonth = this.date && this.date.getUTCMonth();
15806         
15807         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15808         
15809         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15810             v.removeClass('active');
15811             
15812             if(currentYear === year && k === currentMonth){
15813                 v.addClass('active');
15814             }
15815             
15816             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15817                 v.addClass('disabled');
15818             }
15819             
15820         });
15821         
15822         
15823         year = parseInt(year/10, 10) * 10;
15824         
15825         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15826         
15827         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15828         
15829         year -= 1;
15830         for (var i = -1; i < 11; i++) {
15831             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15832                 tag: 'span',
15833                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15834                 html: year
15835             })
15836             
15837             year += 1;
15838         }
15839     },
15840     
15841     showMode: function(dir) 
15842     {
15843         if (dir) {
15844             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15845         }
15846         
15847         Roo.each(this.picker().select('>div',true).elements, function(v){
15848             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15849             v.hide();
15850         });
15851         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15852     },
15853     
15854     place: function()
15855     {
15856         if(this.isInline) return;
15857         
15858         this.picker().removeClass(['bottom', 'top']);
15859         
15860         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15861             /*
15862              * place to the top of element!
15863              *
15864              */
15865             
15866             this.picker().addClass('top');
15867             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15868             
15869             return;
15870         }
15871         
15872         this.picker().addClass('bottom');
15873         
15874         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15875     },
15876     
15877     parseDate : function(value)
15878     {
15879         if(!value || value instanceof Date){
15880             return value;
15881         }
15882         var v = Date.parseDate(value, this.format);
15883         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15884             v = Date.parseDate(value, 'Y-m-d');
15885         }
15886         if(!v && this.altFormats){
15887             if(!this.altFormatsArray){
15888                 this.altFormatsArray = this.altFormats.split("|");
15889             }
15890             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15891                 v = Date.parseDate(value, this.altFormatsArray[i]);
15892             }
15893         }
15894         return v;
15895     },
15896     
15897     formatDate : function(date, fmt)
15898     {   
15899         return (!date || !(date instanceof Date)) ?
15900         date : date.dateFormat(fmt || this.format);
15901     },
15902     
15903     onFocus : function()
15904     {
15905         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15906         this.show();
15907     },
15908     
15909     onBlur : function()
15910     {
15911         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15912         
15913         var d = this.inputEl().getValue();
15914         
15915         this.setValue(d);
15916                 
15917         this.hide();
15918     },
15919     
15920     show : function()
15921     {
15922         this.picker().show();
15923         this.update();
15924         this.place();
15925         
15926         this.fireEvent('show', this, this.date);
15927     },
15928     
15929     hide : function()
15930     {
15931         if(this.isInline) return;
15932         this.picker().hide();
15933         this.viewMode = this.startViewMode;
15934         this.showMode();
15935         
15936         this.fireEvent('hide', this, this.date);
15937         
15938     },
15939     
15940     onMousedown: function(e)
15941     {
15942         e.stopPropagation();
15943         e.preventDefault();
15944     },
15945     
15946     keyup: function(e)
15947     {
15948         Roo.bootstrap.DateField.superclass.keyup.call(this);
15949         this.update();
15950     },
15951
15952     setValue: function(v)
15953     {
15954         
15955         // v can be a string or a date..
15956         
15957         
15958         var d = new Date(this.parseDate(v) ).clearTime();
15959         
15960         if(isNaN(d.getTime())){
15961             this.date = this.viewDate = '';
15962             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15963             return;
15964         }
15965         
15966         v = this.formatDate(d);
15967         
15968         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15969         
15970         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15971      
15972         this.update();
15973
15974         this.fireEvent('select', this, this.date);
15975         
15976     },
15977     
15978     getValue: function()
15979     {
15980         return this.formatDate(this.date);
15981     },
15982     
15983     fireKey: function(e)
15984     {
15985         if (!this.picker().isVisible()){
15986             if (e.keyCode == 27) // allow escape to hide and re-show picker
15987                 this.show();
15988             return;
15989         }
15990         
15991         var dateChanged = false,
15992         dir, day, month,
15993         newDate, newViewDate;
15994         
15995         switch(e.keyCode){
15996             case 27: // escape
15997                 this.hide();
15998                 e.preventDefault();
15999                 break;
16000             case 37: // left
16001             case 39: // right
16002                 if (!this.keyboardNavigation) break;
16003                 dir = e.keyCode == 37 ? -1 : 1;
16004                 
16005                 if (e.ctrlKey){
16006                     newDate = this.moveYear(this.date, dir);
16007                     newViewDate = this.moveYear(this.viewDate, dir);
16008                 } else if (e.shiftKey){
16009                     newDate = this.moveMonth(this.date, dir);
16010                     newViewDate = this.moveMonth(this.viewDate, dir);
16011                 } else {
16012                     newDate = new Date(this.date);
16013                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16014                     newViewDate = new Date(this.viewDate);
16015                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16016                 }
16017                 if (this.dateWithinRange(newDate)){
16018                     this.date = newDate;
16019                     this.viewDate = newViewDate;
16020                     this.setValue(this.formatDate(this.date));
16021 //                    this.update();
16022                     e.preventDefault();
16023                     dateChanged = true;
16024                 }
16025                 break;
16026             case 38: // up
16027             case 40: // down
16028                 if (!this.keyboardNavigation) break;
16029                 dir = e.keyCode == 38 ? -1 : 1;
16030                 if (e.ctrlKey){
16031                     newDate = this.moveYear(this.date, dir);
16032                     newViewDate = this.moveYear(this.viewDate, dir);
16033                 } else if (e.shiftKey){
16034                     newDate = this.moveMonth(this.date, dir);
16035                     newViewDate = this.moveMonth(this.viewDate, dir);
16036                 } else {
16037                     newDate = new Date(this.date);
16038                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16039                     newViewDate = new Date(this.viewDate);
16040                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16041                 }
16042                 if (this.dateWithinRange(newDate)){
16043                     this.date = newDate;
16044                     this.viewDate = newViewDate;
16045                     this.setValue(this.formatDate(this.date));
16046 //                    this.update();
16047                     e.preventDefault();
16048                     dateChanged = true;
16049                 }
16050                 break;
16051             case 13: // enter
16052                 this.setValue(this.formatDate(this.date));
16053                 this.hide();
16054                 e.preventDefault();
16055                 break;
16056             case 9: // tab
16057                 this.setValue(this.formatDate(this.date));
16058                 this.hide();
16059                 break;
16060             case 16: // shift
16061             case 17: // ctrl
16062             case 18: // alt
16063                 break;
16064             default :
16065                 this.hide();
16066                 
16067         }
16068     },
16069     
16070     
16071     onClick: function(e) 
16072     {
16073         e.stopPropagation();
16074         e.preventDefault();
16075         
16076         var target = e.getTarget();
16077         
16078         if(target.nodeName.toLowerCase() === 'i'){
16079             target = Roo.get(target).dom.parentNode;
16080         }
16081         
16082         var nodeName = target.nodeName;
16083         var className = target.className;
16084         var html = target.innerHTML;
16085         //Roo.log(nodeName);
16086         
16087         switch(nodeName.toLowerCase()) {
16088             case 'th':
16089                 switch(className) {
16090                     case 'switch':
16091                         this.showMode(1);
16092                         break;
16093                     case 'prev':
16094                     case 'next':
16095                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16096                         switch(this.viewMode){
16097                                 case 0:
16098                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16099                                         break;
16100                                 case 1:
16101                                 case 2:
16102                                         this.viewDate = this.moveYear(this.viewDate, dir);
16103                                         break;
16104                         }
16105                         this.fill();
16106                         break;
16107                     case 'today':
16108                         var date = new Date();
16109                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16110 //                        this.fill()
16111                         this.setValue(this.formatDate(this.date));
16112                         
16113                         this.hide();
16114                         break;
16115                 }
16116                 break;
16117             case 'span':
16118                 if (className.indexOf('disabled') < 0) {
16119                     this.viewDate.setUTCDate(1);
16120                     if (className.indexOf('month') > -1) {
16121                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16122                     } else {
16123                         var year = parseInt(html, 10) || 0;
16124                         this.viewDate.setUTCFullYear(year);
16125                         
16126                     }
16127                     
16128                     if(this.singleMode){
16129                         this.setValue(this.formatDate(this.viewDate));
16130                         this.hide();
16131                         return;
16132                     }
16133                     
16134                     this.showMode(-1);
16135                     this.fill();
16136                 }
16137                 break;
16138                 
16139             case 'td':
16140                 //Roo.log(className);
16141                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16142                     var day = parseInt(html, 10) || 1;
16143                     var year = this.viewDate.getUTCFullYear(),
16144                         month = this.viewDate.getUTCMonth();
16145
16146                     if (className.indexOf('old') > -1) {
16147                         if(month === 0 ){
16148                             month = 11;
16149                             year -= 1;
16150                         }else{
16151                             month -= 1;
16152                         }
16153                     } else if (className.indexOf('new') > -1) {
16154                         if (month == 11) {
16155                             month = 0;
16156                             year += 1;
16157                         } else {
16158                             month += 1;
16159                         }
16160                     }
16161                     //Roo.log([year,month,day]);
16162                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16163                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16164 //                    this.fill();
16165                     //Roo.log(this.formatDate(this.date));
16166                     this.setValue(this.formatDate(this.date));
16167                     this.hide();
16168                 }
16169                 break;
16170         }
16171     },
16172     
16173     setStartDate: function(startDate)
16174     {
16175         this.startDate = startDate || -Infinity;
16176         if (this.startDate !== -Infinity) {
16177             this.startDate = this.parseDate(this.startDate);
16178         }
16179         this.update();
16180         this.updateNavArrows();
16181     },
16182
16183     setEndDate: function(endDate)
16184     {
16185         this.endDate = endDate || Infinity;
16186         if (this.endDate !== Infinity) {
16187             this.endDate = this.parseDate(this.endDate);
16188         }
16189         this.update();
16190         this.updateNavArrows();
16191     },
16192     
16193     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16194     {
16195         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16196         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16197             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16198         }
16199         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16200             return parseInt(d, 10);
16201         });
16202         this.update();
16203         this.updateNavArrows();
16204     },
16205     
16206     updateNavArrows: function() 
16207     {
16208         if(this.singleMode){
16209             return;
16210         }
16211         
16212         var d = new Date(this.viewDate),
16213         year = d.getUTCFullYear(),
16214         month = d.getUTCMonth();
16215         
16216         Roo.each(this.picker().select('.prev', true).elements, function(v){
16217             v.show();
16218             switch (this.viewMode) {
16219                 case 0:
16220
16221                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16222                         v.hide();
16223                     }
16224                     break;
16225                 case 1:
16226                 case 2:
16227                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16228                         v.hide();
16229                     }
16230                     break;
16231             }
16232         });
16233         
16234         Roo.each(this.picker().select('.next', true).elements, function(v){
16235             v.show();
16236             switch (this.viewMode) {
16237                 case 0:
16238
16239                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16240                         v.hide();
16241                     }
16242                     break;
16243                 case 1:
16244                 case 2:
16245                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16246                         v.hide();
16247                     }
16248                     break;
16249             }
16250         })
16251     },
16252     
16253     moveMonth: function(date, dir)
16254     {
16255         if (!dir) return date;
16256         var new_date = new Date(date.valueOf()),
16257         day = new_date.getUTCDate(),
16258         month = new_date.getUTCMonth(),
16259         mag = Math.abs(dir),
16260         new_month, test;
16261         dir = dir > 0 ? 1 : -1;
16262         if (mag == 1){
16263             test = dir == -1
16264             // If going back one month, make sure month is not current month
16265             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16266             ? function(){
16267                 return new_date.getUTCMonth() == month;
16268             }
16269             // If going forward one month, make sure month is as expected
16270             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16271             : function(){
16272                 return new_date.getUTCMonth() != new_month;
16273             };
16274             new_month = month + dir;
16275             new_date.setUTCMonth(new_month);
16276             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16277             if (new_month < 0 || new_month > 11)
16278                 new_month = (new_month + 12) % 12;
16279         } else {
16280             // For magnitudes >1, move one month at a time...
16281             for (var i=0; i<mag; i++)
16282                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16283                 new_date = this.moveMonth(new_date, dir);
16284             // ...then reset the day, keeping it in the new month
16285             new_month = new_date.getUTCMonth();
16286             new_date.setUTCDate(day);
16287             test = function(){
16288                 return new_month != new_date.getUTCMonth();
16289             };
16290         }
16291         // Common date-resetting loop -- if date is beyond end of month, make it
16292         // end of month
16293         while (test()){
16294             new_date.setUTCDate(--day);
16295             new_date.setUTCMonth(new_month);
16296         }
16297         return new_date;
16298     },
16299
16300     moveYear: function(date, dir)
16301     {
16302         return this.moveMonth(date, dir*12);
16303     },
16304
16305     dateWithinRange: function(date)
16306     {
16307         return date >= this.startDate && date <= this.endDate;
16308     },
16309
16310     
16311     remove: function() 
16312     {
16313         this.picker().remove();
16314     }
16315    
16316 });
16317
16318 Roo.apply(Roo.bootstrap.DateField,  {
16319     
16320     head : {
16321         tag: 'thead',
16322         cn: [
16323         {
16324             tag: 'tr',
16325             cn: [
16326             {
16327                 tag: 'th',
16328                 cls: 'prev',
16329                 html: '<i class="fa fa-arrow-left"/>'
16330             },
16331             {
16332                 tag: 'th',
16333                 cls: 'switch',
16334                 colspan: '5'
16335             },
16336             {
16337                 tag: 'th',
16338                 cls: 'next',
16339                 html: '<i class="fa fa-arrow-right"/>'
16340             }
16341
16342             ]
16343         }
16344         ]
16345     },
16346     
16347     content : {
16348         tag: 'tbody',
16349         cn: [
16350         {
16351             tag: 'tr',
16352             cn: [
16353             {
16354                 tag: 'td',
16355                 colspan: '7'
16356             }
16357             ]
16358         }
16359         ]
16360     },
16361     
16362     footer : {
16363         tag: 'tfoot',
16364         cn: [
16365         {
16366             tag: 'tr',
16367             cn: [
16368             {
16369                 tag: 'th',
16370                 colspan: '7',
16371                 cls: 'today'
16372             }
16373                     
16374             ]
16375         }
16376         ]
16377     },
16378     
16379     dates:{
16380         en: {
16381             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16382             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16383             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16384             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16385             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16386             today: "Today"
16387         }
16388     },
16389     
16390     modes: [
16391     {
16392         clsName: 'days',
16393         navFnc: 'Month',
16394         navStep: 1
16395     },
16396     {
16397         clsName: 'months',
16398         navFnc: 'FullYear',
16399         navStep: 1
16400     },
16401     {
16402         clsName: 'years',
16403         navFnc: 'FullYear',
16404         navStep: 10
16405     }]
16406 });
16407
16408 Roo.apply(Roo.bootstrap.DateField,  {
16409   
16410     template : {
16411         tag: 'div',
16412         cls: 'datepicker dropdown-menu roo-dynamic',
16413         cn: [
16414         {
16415             tag: 'div',
16416             cls: 'datepicker-days',
16417             cn: [
16418             {
16419                 tag: 'table',
16420                 cls: 'table-condensed',
16421                 cn:[
16422                 Roo.bootstrap.DateField.head,
16423                 {
16424                     tag: 'tbody'
16425                 },
16426                 Roo.bootstrap.DateField.footer
16427                 ]
16428             }
16429             ]
16430         },
16431         {
16432             tag: 'div',
16433             cls: 'datepicker-months',
16434             cn: [
16435             {
16436                 tag: 'table',
16437                 cls: 'table-condensed',
16438                 cn:[
16439                 Roo.bootstrap.DateField.head,
16440                 Roo.bootstrap.DateField.content,
16441                 Roo.bootstrap.DateField.footer
16442                 ]
16443             }
16444             ]
16445         },
16446         {
16447             tag: 'div',
16448             cls: 'datepicker-years',
16449             cn: [
16450             {
16451                 tag: 'table',
16452                 cls: 'table-condensed',
16453                 cn:[
16454                 Roo.bootstrap.DateField.head,
16455                 Roo.bootstrap.DateField.content,
16456                 Roo.bootstrap.DateField.footer
16457                 ]
16458             }
16459             ]
16460         }
16461         ]
16462     }
16463 });
16464
16465  
16466
16467  /*
16468  * - LGPL
16469  *
16470  * TimeField
16471  * 
16472  */
16473
16474 /**
16475  * @class Roo.bootstrap.TimeField
16476  * @extends Roo.bootstrap.Input
16477  * Bootstrap DateField class
16478  * 
16479  * 
16480  * @constructor
16481  * Create a new TimeField
16482  * @param {Object} config The config object
16483  */
16484
16485 Roo.bootstrap.TimeField = function(config){
16486     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16487     this.addEvents({
16488             /**
16489              * @event show
16490              * Fires when this field show.
16491              * @param {Roo.bootstrap.DateField} thisthis
16492              * @param {Mixed} date The date value
16493              */
16494             show : true,
16495             /**
16496              * @event show
16497              * Fires when this field hide.
16498              * @param {Roo.bootstrap.DateField} this
16499              * @param {Mixed} date The date value
16500              */
16501             hide : true,
16502             /**
16503              * @event select
16504              * Fires when select a date.
16505              * @param {Roo.bootstrap.DateField} this
16506              * @param {Mixed} date The date value
16507              */
16508             select : true
16509         });
16510 };
16511
16512 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16513     
16514     /**
16515      * @cfg {String} format
16516      * The default time format string which can be overriden for localization support.  The format must be
16517      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16518      */
16519     format : "H:i",
16520        
16521     onRender: function(ct, position)
16522     {
16523         
16524         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16525                 
16526         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16527         
16528         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16529         
16530         this.pop = this.picker().select('>.datepicker-time',true).first();
16531         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16532         
16533         this.picker().on('mousedown', this.onMousedown, this);
16534         this.picker().on('click', this.onClick, this);
16535         
16536         this.picker().addClass('datepicker-dropdown');
16537     
16538         this.fillTime();
16539         this.update();
16540             
16541         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16542         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16543         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16544         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16545         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16546         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16547
16548     },
16549     
16550     fireKey: function(e){
16551         if (!this.picker().isVisible()){
16552             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16553                 this.show();
16554             }
16555             return;
16556         }
16557
16558         e.preventDefault();
16559         
16560         switch(e.keyCode){
16561             case 27: // escape
16562                 this.hide();
16563                 break;
16564             case 37: // left
16565             case 39: // right
16566                 this.onTogglePeriod();
16567                 break;
16568             case 38: // up
16569                 this.onIncrementMinutes();
16570                 break;
16571             case 40: // down
16572                 this.onDecrementMinutes();
16573                 break;
16574             case 13: // enter
16575             case 9: // tab
16576                 this.setTime();
16577                 break;
16578         }
16579     },
16580     
16581     onClick: function(e) {
16582         e.stopPropagation();
16583         e.preventDefault();
16584     },
16585     
16586     picker : function()
16587     {
16588         return this.el.select('.datepicker', true).first();
16589     },
16590     
16591     fillTime: function()
16592     {    
16593         var time = this.pop.select('tbody', true).first();
16594         
16595         time.dom.innerHTML = '';
16596         
16597         time.createChild({
16598             tag: 'tr',
16599             cn: [
16600                 {
16601                     tag: 'td',
16602                     cn: [
16603                         {
16604                             tag: 'a',
16605                             href: '#',
16606                             cls: 'btn',
16607                             cn: [
16608                                 {
16609                                     tag: 'span',
16610                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16611                                 }
16612                             ]
16613                         } 
16614                     ]
16615                 },
16616                 {
16617                     tag: 'td',
16618                     cls: 'separator'
16619                 },
16620                 {
16621                     tag: 'td',
16622                     cn: [
16623                         {
16624                             tag: 'a',
16625                             href: '#',
16626                             cls: 'btn',
16627                             cn: [
16628                                 {
16629                                     tag: 'span',
16630                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16631                                 }
16632                             ]
16633                         }
16634                     ]
16635                 },
16636                 {
16637                     tag: 'td',
16638                     cls: 'separator'
16639                 }
16640             ]
16641         });
16642         
16643         time.createChild({
16644             tag: 'tr',
16645             cn: [
16646                 {
16647                     tag: 'td',
16648                     cn: [
16649                         {
16650                             tag: 'span',
16651                             cls: 'timepicker-hour',
16652                             html: '00'
16653                         }  
16654                     ]
16655                 },
16656                 {
16657                     tag: 'td',
16658                     cls: 'separator',
16659                     html: ':'
16660                 },
16661                 {
16662                     tag: 'td',
16663                     cn: [
16664                         {
16665                             tag: 'span',
16666                             cls: 'timepicker-minute',
16667                             html: '00'
16668                         }  
16669                     ]
16670                 },
16671                 {
16672                     tag: 'td',
16673                     cls: 'separator'
16674                 },
16675                 {
16676                     tag: 'td',
16677                     cn: [
16678                         {
16679                             tag: 'button',
16680                             type: 'button',
16681                             cls: 'btn btn-primary period',
16682                             html: 'AM'
16683                             
16684                         }
16685                     ]
16686                 }
16687             ]
16688         });
16689         
16690         time.createChild({
16691             tag: 'tr',
16692             cn: [
16693                 {
16694                     tag: 'td',
16695                     cn: [
16696                         {
16697                             tag: 'a',
16698                             href: '#',
16699                             cls: 'btn',
16700                             cn: [
16701                                 {
16702                                     tag: 'span',
16703                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16704                                 }
16705                             ]
16706                         }
16707                     ]
16708                 },
16709                 {
16710                     tag: 'td',
16711                     cls: 'separator'
16712                 },
16713                 {
16714                     tag: 'td',
16715                     cn: [
16716                         {
16717                             tag: 'a',
16718                             href: '#',
16719                             cls: 'btn',
16720                             cn: [
16721                                 {
16722                                     tag: 'span',
16723                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16724                                 }
16725                             ]
16726                         }
16727                     ]
16728                 },
16729                 {
16730                     tag: 'td',
16731                     cls: 'separator'
16732                 }
16733             ]
16734         });
16735         
16736     },
16737     
16738     update: function()
16739     {
16740         
16741         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16742         
16743         this.fill();
16744     },
16745     
16746     fill: function() 
16747     {
16748         var hours = this.time.getHours();
16749         var minutes = this.time.getMinutes();
16750         var period = 'AM';
16751         
16752         if(hours > 11){
16753             period = 'PM';
16754         }
16755         
16756         if(hours == 0){
16757             hours = 12;
16758         }
16759         
16760         
16761         if(hours > 12){
16762             hours = hours - 12;
16763         }
16764         
16765         if(hours < 10){
16766             hours = '0' + hours;
16767         }
16768         
16769         if(minutes < 10){
16770             minutes = '0' + minutes;
16771         }
16772         
16773         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16774         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16775         this.pop.select('button', true).first().dom.innerHTML = period;
16776         
16777     },
16778     
16779     place: function()
16780     {   
16781         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16782         
16783         var cls = ['bottom'];
16784         
16785         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16786             cls.pop();
16787             cls.push('top');
16788         }
16789         
16790         cls.push('right');
16791         
16792         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16793             cls.pop();
16794             cls.push('left');
16795         }
16796         
16797         this.picker().addClass(cls.join('-'));
16798         
16799         var _this = this;
16800         
16801         Roo.each(cls, function(c){
16802             if(c == 'bottom'){
16803                 _this.picker().setTop(_this.inputEl().getHeight());
16804                 return;
16805             }
16806             if(c == 'top'){
16807                 _this.picker().setTop(0 - _this.picker().getHeight());
16808                 return;
16809             }
16810             
16811             if(c == 'left'){
16812                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16813                 return;
16814             }
16815             if(c == 'right'){
16816                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16817                 return;
16818             }
16819         });
16820         
16821     },
16822   
16823     onFocus : function()
16824     {
16825         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16826         this.show();
16827     },
16828     
16829     onBlur : function()
16830     {
16831         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16832         this.hide();
16833     },
16834     
16835     show : function()
16836     {
16837         this.picker().show();
16838         this.pop.show();
16839         this.update();
16840         this.place();
16841         
16842         this.fireEvent('show', this, this.date);
16843     },
16844     
16845     hide : function()
16846     {
16847         this.picker().hide();
16848         this.pop.hide();
16849         
16850         this.fireEvent('hide', this, this.date);
16851     },
16852     
16853     setTime : function()
16854     {
16855         this.hide();
16856         this.setValue(this.time.format(this.format));
16857         
16858         this.fireEvent('select', this, this.date);
16859         
16860         
16861     },
16862     
16863     onMousedown: function(e){
16864         e.stopPropagation();
16865         e.preventDefault();
16866     },
16867     
16868     onIncrementHours: function()
16869     {
16870         Roo.log('onIncrementHours');
16871         this.time = this.time.add(Date.HOUR, 1);
16872         this.update();
16873         
16874     },
16875     
16876     onDecrementHours: function()
16877     {
16878         Roo.log('onDecrementHours');
16879         this.time = this.time.add(Date.HOUR, -1);
16880         this.update();
16881     },
16882     
16883     onIncrementMinutes: function()
16884     {
16885         Roo.log('onIncrementMinutes');
16886         this.time = this.time.add(Date.MINUTE, 1);
16887         this.update();
16888     },
16889     
16890     onDecrementMinutes: function()
16891     {
16892         Roo.log('onDecrementMinutes');
16893         this.time = this.time.add(Date.MINUTE, -1);
16894         this.update();
16895     },
16896     
16897     onTogglePeriod: function()
16898     {
16899         Roo.log('onTogglePeriod');
16900         this.time = this.time.add(Date.HOUR, 12);
16901         this.update();
16902     }
16903     
16904    
16905 });
16906
16907 Roo.apply(Roo.bootstrap.TimeField,  {
16908     
16909     content : {
16910         tag: 'tbody',
16911         cn: [
16912             {
16913                 tag: 'tr',
16914                 cn: [
16915                 {
16916                     tag: 'td',
16917                     colspan: '7'
16918                 }
16919                 ]
16920             }
16921         ]
16922     },
16923     
16924     footer : {
16925         tag: 'tfoot',
16926         cn: [
16927             {
16928                 tag: 'tr',
16929                 cn: [
16930                 {
16931                     tag: 'th',
16932                     colspan: '7',
16933                     cls: '',
16934                     cn: [
16935                         {
16936                             tag: 'button',
16937                             cls: 'btn btn-info ok',
16938                             html: 'OK'
16939                         }
16940                     ]
16941                 }
16942
16943                 ]
16944             }
16945         ]
16946     }
16947 });
16948
16949 Roo.apply(Roo.bootstrap.TimeField,  {
16950   
16951     template : {
16952         tag: 'div',
16953         cls: 'datepicker dropdown-menu',
16954         cn: [
16955             {
16956                 tag: 'div',
16957                 cls: 'datepicker-time',
16958                 cn: [
16959                 {
16960                     tag: 'table',
16961                     cls: 'table-condensed',
16962                     cn:[
16963                     Roo.bootstrap.TimeField.content,
16964                     Roo.bootstrap.TimeField.footer
16965                     ]
16966                 }
16967                 ]
16968             }
16969         ]
16970     }
16971 });
16972
16973  
16974
16975  /*
16976  * - LGPL
16977  *
16978  * MonthField
16979  * 
16980  */
16981
16982 /**
16983  * @class Roo.bootstrap.MonthField
16984  * @extends Roo.bootstrap.Input
16985  * Bootstrap MonthField class
16986  * 
16987  * @cfg {String} language default en
16988  * 
16989  * @constructor
16990  * Create a new MonthField
16991  * @param {Object} config The config object
16992  */
16993
16994 Roo.bootstrap.MonthField = function(config){
16995     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16996     
16997     this.addEvents({
16998         /**
16999          * @event show
17000          * Fires when this field show.
17001          * @param {Roo.bootstrap.MonthField} this
17002          * @param {Mixed} date The date value
17003          */
17004         show : true,
17005         /**
17006          * @event show
17007          * Fires when this field hide.
17008          * @param {Roo.bootstrap.MonthField} this
17009          * @param {Mixed} date The date value
17010          */
17011         hide : true,
17012         /**
17013          * @event select
17014          * Fires when select a date.
17015          * @param {Roo.bootstrap.MonthField} this
17016          * @param {String} oldvalue The old value
17017          * @param {String} newvalue The new value
17018          */
17019         select : true
17020     });
17021 };
17022
17023 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17024     
17025     onRender: function(ct, position)
17026     {
17027         
17028         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17029         
17030         this.language = this.language || 'en';
17031         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17032         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17033         
17034         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17035         this.isInline = false;
17036         this.isInput = true;
17037         this.component = this.el.select('.add-on', true).first() || false;
17038         this.component = (this.component && this.component.length === 0) ? false : this.component;
17039         this.hasInput = this.component && this.inputEL().length;
17040         
17041         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17042         
17043         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17044         
17045         this.picker().on('mousedown', this.onMousedown, this);
17046         this.picker().on('click', this.onClick, this);
17047         
17048         this.picker().addClass('datepicker-dropdown');
17049         
17050         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17051             v.setStyle('width', '189px');
17052         });
17053         
17054         this.fillMonths();
17055         
17056         this.update();
17057         
17058         if(this.isInline) {
17059             this.show();
17060         }
17061         
17062     },
17063     
17064     setValue: function(v, suppressEvent)
17065     {   
17066         var o = this.getValue();
17067         
17068         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17069         
17070         this.update();
17071
17072         if(suppressEvent !== true){
17073             this.fireEvent('select', this, o, v);
17074         }
17075         
17076     },
17077     
17078     getValue: function()
17079     {
17080         return this.value;
17081     },
17082     
17083     onClick: function(e) 
17084     {
17085         e.stopPropagation();
17086         e.preventDefault();
17087         
17088         var target = e.getTarget();
17089         
17090         if(target.nodeName.toLowerCase() === 'i'){
17091             target = Roo.get(target).dom.parentNode;
17092         }
17093         
17094         var nodeName = target.nodeName;
17095         var className = target.className;
17096         var html = target.innerHTML;
17097         
17098         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17099             return;
17100         }
17101         
17102         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17103         
17104         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17105         
17106         this.hide();
17107                         
17108     },
17109     
17110     picker : function()
17111     {
17112         return this.pickerEl;
17113     },
17114     
17115     fillMonths: function()
17116     {    
17117         var i = 0;
17118         var months = this.picker().select('>.datepicker-months td', true).first();
17119         
17120         months.dom.innerHTML = '';
17121         
17122         while (i < 12) {
17123             var month = {
17124                 tag: 'span',
17125                 cls: 'month',
17126                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17127             }
17128             
17129             months.createChild(month);
17130         }
17131         
17132     },
17133     
17134     update: function()
17135     {
17136         var _this = this;
17137         
17138         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17139             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17140         }
17141         
17142         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17143             e.removeClass('active');
17144             
17145             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17146                 e.addClass('active');
17147             }
17148         })
17149     },
17150     
17151     place: function()
17152     {
17153         if(this.isInline) return;
17154         
17155         this.picker().removeClass(['bottom', 'top']);
17156         
17157         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17158             /*
17159              * place to the top of element!
17160              *
17161              */
17162             
17163             this.picker().addClass('top');
17164             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17165             
17166             return;
17167         }
17168         
17169         this.picker().addClass('bottom');
17170         
17171         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17172     },
17173     
17174     onFocus : function()
17175     {
17176         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17177         this.show();
17178     },
17179     
17180     onBlur : function()
17181     {
17182         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17183         
17184         var d = this.inputEl().getValue();
17185         
17186         this.setValue(d);
17187                 
17188         this.hide();
17189     },
17190     
17191     show : function()
17192     {
17193         this.picker().show();
17194         this.picker().select('>.datepicker-months', true).first().show();
17195         this.update();
17196         this.place();
17197         
17198         this.fireEvent('show', this, this.date);
17199     },
17200     
17201     hide : function()
17202     {
17203         if(this.isInline) return;
17204         this.picker().hide();
17205         this.fireEvent('hide', this, this.date);
17206         
17207     },
17208     
17209     onMousedown: function(e)
17210     {
17211         e.stopPropagation();
17212         e.preventDefault();
17213     },
17214     
17215     keyup: function(e)
17216     {
17217         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17218         this.update();
17219     },
17220
17221     fireKey: function(e)
17222     {
17223         if (!this.picker().isVisible()){
17224             if (e.keyCode == 27) // allow escape to hide and re-show picker
17225                 this.show();
17226             return;
17227         }
17228         
17229         var dir;
17230         
17231         switch(e.keyCode){
17232             case 27: // escape
17233                 this.hide();
17234                 e.preventDefault();
17235                 break;
17236             case 37: // left
17237             case 39: // right
17238                 dir = e.keyCode == 37 ? -1 : 1;
17239                 
17240                 this.vIndex = this.vIndex + dir;
17241                 
17242                 if(this.vIndex < 0){
17243                     this.vIndex = 0;
17244                 }
17245                 
17246                 if(this.vIndex > 11){
17247                     this.vIndex = 11;
17248                 }
17249                 
17250                 if(isNaN(this.vIndex)){
17251                     this.vIndex = 0;
17252                 }
17253                 
17254                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17255                 
17256                 break;
17257             case 38: // up
17258             case 40: // down
17259                 
17260                 dir = e.keyCode == 38 ? -1 : 1;
17261                 
17262                 this.vIndex = this.vIndex + dir * 4;
17263                 
17264                 if(this.vIndex < 0){
17265                     this.vIndex = 0;
17266                 }
17267                 
17268                 if(this.vIndex > 11){
17269                     this.vIndex = 11;
17270                 }
17271                 
17272                 if(isNaN(this.vIndex)){
17273                     this.vIndex = 0;
17274                 }
17275                 
17276                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17277                 break;
17278                 
17279             case 13: // enter
17280                 
17281                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17282                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17283                 }
17284                 
17285                 this.hide();
17286                 e.preventDefault();
17287                 break;
17288             case 9: // tab
17289                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17290                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17291                 }
17292                 this.hide();
17293                 break;
17294             case 16: // shift
17295             case 17: // ctrl
17296             case 18: // alt
17297                 break;
17298             default :
17299                 this.hide();
17300                 
17301         }
17302     },
17303     
17304     remove: function() 
17305     {
17306         this.picker().remove();
17307     }
17308    
17309 });
17310
17311 Roo.apply(Roo.bootstrap.MonthField,  {
17312     
17313     content : {
17314         tag: 'tbody',
17315         cn: [
17316         {
17317             tag: 'tr',
17318             cn: [
17319             {
17320                 tag: 'td',
17321                 colspan: '7'
17322             }
17323             ]
17324         }
17325         ]
17326     },
17327     
17328     dates:{
17329         en: {
17330             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17331             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17332         }
17333     }
17334 });
17335
17336 Roo.apply(Roo.bootstrap.MonthField,  {
17337   
17338     template : {
17339         tag: 'div',
17340         cls: 'datepicker dropdown-menu roo-dynamic',
17341         cn: [
17342             {
17343                 tag: 'div',
17344                 cls: 'datepicker-months',
17345                 cn: [
17346                 {
17347                     tag: 'table',
17348                     cls: 'table-condensed',
17349                     cn:[
17350                         Roo.bootstrap.DateField.content
17351                     ]
17352                 }
17353                 ]
17354             }
17355         ]
17356     }
17357 });
17358
17359  
17360
17361  
17362  /*
17363  * - LGPL
17364  *
17365  * CheckBox
17366  * 
17367  */
17368
17369 /**
17370  * @class Roo.bootstrap.CheckBox
17371  * @extends Roo.bootstrap.Input
17372  * Bootstrap CheckBox class
17373  * 
17374  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17375  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17376  * @cfg {String} boxLabel The text that appears beside the checkbox
17377  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17378  * @cfg {Boolean} checked initnal the element
17379  * @cfg {Boolean} inline inline the element (default false)
17380  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17381  * 
17382  * @constructor
17383  * Create a new CheckBox
17384  * @param {Object} config The config object
17385  */
17386
17387 Roo.bootstrap.CheckBox = function(config){
17388     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17389    
17390     this.addEvents({
17391         /**
17392         * @event check
17393         * Fires when the element is checked or unchecked.
17394         * @param {Roo.bootstrap.CheckBox} this This input
17395         * @param {Boolean} checked The new checked value
17396         */
17397        check : true
17398     });
17399     
17400 };
17401
17402 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17403   
17404     inputType: 'checkbox',
17405     inputValue: 1,
17406     valueOff: 0,
17407     boxLabel: false,
17408     checked: false,
17409     weight : false,
17410     inline: false,
17411     
17412     getAutoCreate : function()
17413     {
17414         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17415         
17416         var id = Roo.id();
17417         
17418         var cfg = {};
17419         
17420         cfg.cls = 'form-group ' + this.inputType; //input-group
17421         
17422         if(this.inline){
17423             cfg.cls += ' ' + this.inputType + '-inline';
17424         }
17425         
17426         var input =  {
17427             tag: 'input',
17428             id : id,
17429             type : this.inputType,
17430             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17431             cls : 'roo-' + this.inputType, //'form-box',
17432             placeholder : this.placeholder || ''
17433             
17434         };
17435         
17436         if (this.weight) { // Validity check?
17437             cfg.cls += " " + this.inputType + "-" + this.weight;
17438         }
17439         
17440         if (this.disabled) {
17441             input.disabled=true;
17442         }
17443         
17444         if(this.checked){
17445             input.checked = this.checked;
17446         }
17447         
17448         if (this.name) {
17449             input.name = this.name;
17450         }
17451         
17452         if (this.size) {
17453             input.cls += ' input-' + this.size;
17454         }
17455         
17456         var settings=this;
17457         
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         var inputblock = input;
17465          
17466         if (this.before || this.after) {
17467             
17468             inputblock = {
17469                 cls : 'input-group',
17470                 cn :  [] 
17471             };
17472             
17473             if (this.before) {
17474                 inputblock.cn.push({
17475                     tag :'span',
17476                     cls : 'input-group-addon',
17477                     html : this.before
17478                 });
17479             }
17480             
17481             inputblock.cn.push(input);
17482             
17483             if (this.after) {
17484                 inputblock.cn.push({
17485                     tag :'span',
17486                     cls : 'input-group-addon',
17487                     html : this.after
17488                 });
17489             }
17490             
17491         }
17492         
17493         if (align ==='left' && this.fieldLabel.length) {
17494                 Roo.log("left and has label");
17495                 cfg.cn = [
17496                     
17497                     {
17498                         tag: 'label',
17499                         'for' :  id,
17500                         cls : 'control-label col-md-' + this.labelWidth,
17501                         html : this.fieldLabel
17502                         
17503                     },
17504                     {
17505                         cls : "col-md-" + (12 - this.labelWidth), 
17506                         cn: [
17507                             inputblock
17508                         ]
17509                     }
17510                     
17511                 ];
17512         } else if ( this.fieldLabel.length) {
17513                 Roo.log(" label");
17514                 cfg.cn = [
17515                    
17516                     {
17517                         tag: this.boxLabel ? 'span' : 'label',
17518                         'for': id,
17519                         cls: 'control-label box-input-label',
17520                         //cls : 'input-group-addon',
17521                         html : this.fieldLabel
17522                         
17523                     },
17524                     
17525                     inputblock
17526                     
17527                 ];
17528
17529         } else {
17530             
17531                 Roo.log(" no label && no align");
17532                 cfg.cn = [  inputblock ] ;
17533                 
17534                 
17535         }
17536         if(this.boxLabel){
17537              var boxLabelCfg = {
17538                 tag: 'label',
17539                 //'for': id, // box label is handled by onclick - so no for...
17540                 cls: 'box-label',
17541                 html: this.boxLabel
17542             }
17543             
17544             if(this.tooltip){
17545                 boxLabelCfg.tooltip = this.tooltip;
17546             }
17547              
17548             cfg.cn.push(boxLabelCfg);
17549         }
17550         
17551         
17552        
17553         return cfg;
17554         
17555     },
17556     
17557     /**
17558      * return the real input element.
17559      */
17560     inputEl: function ()
17561     {
17562         return this.el.select('input.roo-' + this.inputType,true).first();
17563     },
17564     
17565     labelEl: function()
17566     {
17567         return this.el.select('label.control-label',true).first();
17568     },
17569     /* depricated... */
17570     
17571     label: function()
17572     {
17573         return this.labelEl();
17574     },
17575     
17576     initEvents : function()
17577     {
17578 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17579         
17580         this.inputEl().on('click', this.onClick,  this);
17581         
17582         if (this.boxLabel) { 
17583             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17584         }
17585         
17586         this.startValue = this.getValue();
17587         
17588         if(this.groupId){
17589             Roo.bootstrap.CheckBox.register(this);
17590         }
17591     },
17592     
17593     onClick : function()
17594     {   
17595         this.setChecked(!this.checked);
17596     },
17597     
17598     setChecked : function(state,suppressEvent)
17599     {
17600         this.startValue = this.getValue();
17601         
17602         if(this.inputType == 'radio'){
17603             
17604             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17605                 e.dom.checked = false;
17606             });
17607             
17608             this.inputEl().dom.checked = true;
17609             
17610             this.inputEl().dom.value = this.inputValue;
17611             
17612             if(suppressEvent !== true){
17613                 this.fireEvent('check', this, true);
17614             }
17615             
17616             this.validate();
17617             
17618             return;
17619         }
17620         
17621         this.checked = state;
17622         
17623         this.inputEl().dom.checked = state;
17624         
17625         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17626         
17627         if(suppressEvent !== true){
17628             this.fireEvent('check', this, state);
17629         }
17630         
17631         this.validate();
17632     },
17633     
17634     getValue : function()
17635     {
17636         if(this.inputType == 'radio'){
17637             return this.getGroupValue();
17638         }
17639         
17640         return this.inputEl().getValue();
17641         
17642     },
17643     
17644     getGroupValue : function()
17645     {
17646         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17647             return '';
17648         }
17649         
17650         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17651     },
17652     
17653     setValue : function(v,suppressEvent)
17654     {
17655         if(this.inputType == 'radio'){
17656             this.setGroupValue(v, suppressEvent);
17657             return;
17658         }
17659         
17660         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17661         
17662         this.validate();
17663     },
17664     
17665     setGroupValue : function(v, suppressEvent)
17666     {
17667         this.startValue = this.getValue();
17668         
17669         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17670             e.dom.checked = false;
17671             
17672             if(e.dom.value == v){
17673                 e.dom.checked = true;
17674             }
17675         });
17676         
17677         if(suppressEvent !== true){
17678             this.fireEvent('check', this, true);
17679         }
17680
17681         this.validate();
17682         
17683         return;
17684     },
17685     
17686     validate : function()
17687     {
17688         if(
17689                 this.disabled || 
17690                 (this.inputType == 'radio' && this.validateRadio()) ||
17691                 (this.inputType == 'checkbox' && this.validateCheckbox())
17692         ){
17693             this.markValid();
17694             return true;
17695         }
17696         
17697         this.markInvalid();
17698         return false;
17699     },
17700     
17701     validateRadio : function()
17702     {
17703         var valid = false;
17704         
17705         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17706             if(!e.dom.checked){
17707                 return;
17708             }
17709             
17710             valid = true;
17711             
17712             return false;
17713         });
17714         
17715         return valid;
17716     },
17717     
17718     validateCheckbox : function()
17719     {
17720         if(!this.groupId){
17721             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17722         }
17723         
17724         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17725         
17726         if(!group){
17727             return false;
17728         }
17729         
17730         var r = false;
17731         
17732         for(var i in group){
17733             if(r){
17734                 break;
17735             }
17736             
17737             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17738         }
17739         
17740         return r;
17741     },
17742     
17743     /**
17744      * Mark this field as valid
17745      */
17746     markValid : function()
17747     {
17748         if(this.allowBlank){
17749             return;
17750         }
17751         
17752         var _this = this;
17753         
17754         this.fireEvent('valid', this);
17755         
17756         if(this.inputType == 'radio'){
17757             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17758                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17759                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17760             });
17761             
17762             return;
17763         }
17764         
17765         if(!this.groupId){
17766             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17767             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17768             return;
17769         }
17770         
17771         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17772             
17773         if(!group){
17774             return;
17775         }
17776         
17777         for(var i in group){
17778             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17779             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17780         }
17781     },
17782     
17783      /**
17784      * Mark this field as invalid
17785      * @param {String} msg The validation message
17786      */
17787     markInvalid : function(msg)
17788     {
17789         if(this.allowBlank){
17790             return;
17791         }
17792         
17793         var _this = this;
17794         
17795         this.fireEvent('invalid', this, msg);
17796         
17797         if(this.inputType == 'radio'){
17798             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17799                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17800                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17801             });
17802             
17803             return;
17804         }
17805         
17806         if(!this.groupId){
17807             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17808             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17809             return;
17810         }
17811         
17812         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17813             
17814         if(!group){
17815             return;
17816         }
17817         
17818         for(var i in group){
17819             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17820             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17821         }
17822         
17823     }
17824     
17825 });
17826
17827 Roo.apply(Roo.bootstrap.CheckBox, {
17828     
17829     groups: {},
17830     
17831      /**
17832     * register a CheckBox Group
17833     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17834     */
17835     register : function(checkbox)
17836     {
17837         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17838             this.groups[checkbox.groupId] = {};
17839         }
17840         
17841         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17842             return;
17843         }
17844         
17845         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17846         
17847     },
17848     /**
17849     * fetch a CheckBox Group based on the group ID
17850     * @param {string} the group ID
17851     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17852     */
17853     get: function(groupId) {
17854         if (typeof(this.groups[groupId]) == 'undefined') {
17855             return false;
17856         }
17857         
17858         return this.groups[groupId] ;
17859     }
17860     
17861     
17862 });
17863 /*
17864  * - LGPL
17865  *
17866  * Radio
17867  *
17868  *
17869  * not inline
17870  *<div class="radio">
17871   <label>
17872     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17873     Option one is this and that&mdash;be sure to include why it's great
17874   </label>
17875 </div>
17876  *
17877  *
17878  *inline
17879  *<span>
17880  *<label class="radio-inline">fieldLabel</label>
17881  *<label class="radio-inline">
17882   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17883 </label>
17884 <span>
17885  * 
17886  * 
17887  */
17888
17889 /**
17890  * @class Roo.bootstrap.Radio
17891  * @extends Roo.bootstrap.CheckBox
17892  * Bootstrap Radio class
17893
17894  * @constructor
17895  * Create a new Radio
17896  * @param {Object} config The config object
17897  */
17898
17899 Roo.bootstrap.Radio = function(config){
17900     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17901    
17902 };
17903
17904 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17905     
17906     inputType: 'radio',
17907     inputValue: '',
17908     valueOff: '',
17909     
17910     getAutoCreate : function()
17911     {
17912         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17913         align = align || 'left'; // default...
17914         
17915         
17916         
17917         var id = Roo.id();
17918         
17919         var cfg = {
17920                 tag : this.inline ? 'span' : 'div',
17921                 cls : '',
17922                 cn : []
17923         };
17924         
17925         var inline = this.inline ? ' radio-inline' : '';
17926         
17927         var lbl = {
17928                 tag: 'label' ,
17929                 // does not need for, as we wrap the input with it..
17930                 'for' : id,
17931                 cls : 'control-label box-label' + inline,
17932                 cn : []
17933         };
17934         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17935         
17936         var fieldLabel = {
17937             tag: 'label' ,
17938             //cls : 'control-label' + inline,
17939             html : this.fieldLabel,
17940             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17941         };
17942         
17943  
17944         
17945         
17946         var input =  {
17947             tag: 'input',
17948             id : id,
17949             type : this.inputType,
17950             //value : (!this.checked) ? this.valueOff : this.inputValue,
17951             value : this.inputValue,
17952             cls : 'roo-radio',
17953             placeholder : this.placeholder || '' // ?? needed????
17954             
17955         };
17956         if (this.weight) { // Validity check?
17957             input.cls += " radio-" + this.weight;
17958         }
17959         if (this.disabled) {
17960             input.disabled=true;
17961         }
17962         
17963         if(this.checked){
17964             input.checked = this.checked;
17965         }
17966         
17967         if (this.name) {
17968             input.name = this.name;
17969         }
17970         
17971         if (this.size) {
17972             input.cls += ' input-' + this.size;
17973         }
17974         
17975         //?? can span's inline have a width??
17976         
17977         var settings=this;
17978         ['xs','sm','md','lg'].map(function(size){
17979             if (settings[size]) {
17980                 cfg.cls += ' col-' + size + '-' + settings[size];
17981             }
17982         });
17983         
17984         var inputblock = input;
17985         
17986         if (this.before || this.after) {
17987             
17988             inputblock = {
17989                 cls : 'input-group',
17990                 tag : 'span',
17991                 cn :  [] 
17992             };
17993             if (this.before) {
17994                 inputblock.cn.push({
17995                     tag :'span',
17996                     cls : 'input-group-addon',
17997                     html : this.before
17998                 });
17999             }
18000             inputblock.cn.push(input);
18001             if (this.after) {
18002                 inputblock.cn.push({
18003                     tag :'span',
18004                     cls : 'input-group-addon',
18005                     html : this.after
18006                 });
18007             }
18008             
18009         };
18010         
18011         
18012         if (this.fieldLabel && this.fieldLabel.length) {
18013             cfg.cn.push(fieldLabel);
18014         }
18015        
18016         // normal bootstrap puts the input inside the label.
18017         // however with our styled version - it has to go after the input.
18018        
18019         //lbl.cn.push(inputblock);
18020         
18021         var lblwrap =  {
18022             tag: 'span',
18023             cls: 'radio' + inline,
18024             cn: [
18025                 inputblock,
18026                 lbl
18027             ]
18028         };
18029         
18030         cfg.cn.push( lblwrap);
18031         
18032         if(this.boxLabel){
18033             lbl.cn.push({
18034                 tag: 'span',
18035                 html: this.boxLabel
18036             })
18037         }
18038          
18039         
18040         return cfg;
18041         
18042     },
18043     
18044     initEvents : function()
18045     {
18046 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18047         
18048         this.inputEl().on('click', this.onClick,  this);
18049         if (this.boxLabel) {
18050             Roo.log('find label')
18051             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18052         }
18053         
18054     },
18055     
18056     inputEl: function ()
18057     {
18058         return this.el.select('input.roo-radio',true).first();
18059     },
18060     onClick : function()
18061     {   
18062         Roo.log("click");
18063         this.setChecked(true);
18064     },
18065     
18066     setChecked : function(state,suppressEvent)
18067     {
18068         if(state){
18069             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18070                 v.dom.checked = false;
18071             });
18072         }
18073         Roo.log(this.inputEl().dom);
18074         this.checked = state;
18075         this.inputEl().dom.checked = state;
18076         
18077         if(suppressEvent !== true){
18078             this.fireEvent('check', this, state);
18079         }
18080         
18081         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18082         
18083     },
18084     
18085     getGroupValue : function()
18086     {
18087         var value = '';
18088         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18089             if(v.dom.checked == true){
18090                 value = v.dom.value;
18091             }
18092         });
18093         
18094         return value;
18095     },
18096     
18097     /**
18098      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18099      * @return {Mixed} value The field value
18100      */
18101     getValue : function(){
18102         return this.getGroupValue();
18103     }
18104     
18105 });
18106
18107  
18108 //<script type="text/javascript">
18109
18110 /*
18111  * Based  Ext JS Library 1.1.1
18112  * Copyright(c) 2006-2007, Ext JS, LLC.
18113  * LGPL
18114  *
18115  */
18116  
18117 /**
18118  * @class Roo.HtmlEditorCore
18119  * @extends Roo.Component
18120  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18121  *
18122  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18123  */
18124
18125 Roo.HtmlEditorCore = function(config){
18126     
18127     
18128     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18129     
18130     
18131     this.addEvents({
18132         /**
18133          * @event initialize
18134          * Fires when the editor is fully initialized (including the iframe)
18135          * @param {Roo.HtmlEditorCore} this
18136          */
18137         initialize: true,
18138         /**
18139          * @event activate
18140          * Fires when the editor is first receives the focus. Any insertion must wait
18141          * until after this event.
18142          * @param {Roo.HtmlEditorCore} this
18143          */
18144         activate: true,
18145          /**
18146          * @event beforesync
18147          * Fires before the textarea is updated with content from the editor iframe. Return false
18148          * to cancel the sync.
18149          * @param {Roo.HtmlEditorCore} this
18150          * @param {String} html
18151          */
18152         beforesync: true,
18153          /**
18154          * @event beforepush
18155          * Fires before the iframe editor is updated with content from the textarea. Return false
18156          * to cancel the push.
18157          * @param {Roo.HtmlEditorCore} this
18158          * @param {String} html
18159          */
18160         beforepush: true,
18161          /**
18162          * @event sync
18163          * Fires when the textarea is updated with content from the editor iframe.
18164          * @param {Roo.HtmlEditorCore} this
18165          * @param {String} html
18166          */
18167         sync: true,
18168          /**
18169          * @event push
18170          * Fires when the iframe editor is updated with content from the textarea.
18171          * @param {Roo.HtmlEditorCore} this
18172          * @param {String} html
18173          */
18174         push: true,
18175         
18176         /**
18177          * @event editorevent
18178          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18179          * @param {Roo.HtmlEditorCore} this
18180          */
18181         editorevent: true
18182         
18183     });
18184     
18185     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18186     
18187     // defaults : white / black...
18188     this.applyBlacklists();
18189     
18190     
18191     
18192 };
18193
18194
18195 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18196
18197
18198      /**
18199      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18200      */
18201     
18202     owner : false,
18203     
18204      /**
18205      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18206      *                        Roo.resizable.
18207      */
18208     resizable : false,
18209      /**
18210      * @cfg {Number} height (in pixels)
18211      */   
18212     height: 300,
18213    /**
18214      * @cfg {Number} width (in pixels)
18215      */   
18216     width: 500,
18217     
18218     /**
18219      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18220      * 
18221      */
18222     stylesheets: false,
18223     
18224     // id of frame..
18225     frameId: false,
18226     
18227     // private properties
18228     validationEvent : false,
18229     deferHeight: true,
18230     initialized : false,
18231     activated : false,
18232     sourceEditMode : false,
18233     onFocus : Roo.emptyFn,
18234     iframePad:3,
18235     hideMode:'offsets',
18236     
18237     clearUp: true,
18238     
18239     // blacklist + whitelisted elements..
18240     black: false,
18241     white: false,
18242      
18243     
18244
18245     /**
18246      * Protected method that will not generally be called directly. It
18247      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18248      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18249      */
18250     getDocMarkup : function(){
18251         // body styles..
18252         var st = '';
18253         
18254         // inherit styels from page...?? 
18255         if (this.stylesheets === false) {
18256             
18257             Roo.get(document.head).select('style').each(function(node) {
18258                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18259             });
18260             
18261             Roo.get(document.head).select('link').each(function(node) { 
18262                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18263             });
18264             
18265         } else if (!this.stylesheets.length) {
18266                 // simple..
18267                 st = '<style type="text/css">' +
18268                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18269                    '</style>';
18270         } else { 
18271             
18272         }
18273         
18274         st +=  '<style type="text/css">' +
18275             'IMG { cursor: pointer } ' +
18276         '</style>';
18277
18278         
18279         return '<html><head>' + st  +
18280             //<style type="text/css">' +
18281             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18282             //'</style>' +
18283             ' </head><body class="roo-htmleditor-body"></body></html>';
18284     },
18285
18286     // private
18287     onRender : function(ct, position)
18288     {
18289         var _t = this;
18290         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18291         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18292         
18293         
18294         this.el.dom.style.border = '0 none';
18295         this.el.dom.setAttribute('tabIndex', -1);
18296         this.el.addClass('x-hidden hide');
18297         
18298         
18299         
18300         if(Roo.isIE){ // fix IE 1px bogus margin
18301             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18302         }
18303        
18304         
18305         this.frameId = Roo.id();
18306         
18307          
18308         
18309         var iframe = this.owner.wrap.createChild({
18310             tag: 'iframe',
18311             cls: 'form-control', // bootstrap..
18312             id: this.frameId,
18313             name: this.frameId,
18314             frameBorder : 'no',
18315             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18316         }, this.el
18317         );
18318         
18319         
18320         this.iframe = iframe.dom;
18321
18322          this.assignDocWin();
18323         
18324         this.doc.designMode = 'on';
18325        
18326         this.doc.open();
18327         this.doc.write(this.getDocMarkup());
18328         this.doc.close();
18329
18330         
18331         var task = { // must defer to wait for browser to be ready
18332             run : function(){
18333                 //console.log("run task?" + this.doc.readyState);
18334                 this.assignDocWin();
18335                 if(this.doc.body || this.doc.readyState == 'complete'){
18336                     try {
18337                         this.doc.designMode="on";
18338                     } catch (e) {
18339                         return;
18340                     }
18341                     Roo.TaskMgr.stop(task);
18342                     this.initEditor.defer(10, this);
18343                 }
18344             },
18345             interval : 10,
18346             duration: 10000,
18347             scope: this
18348         };
18349         Roo.TaskMgr.start(task);
18350
18351     },
18352
18353     // private
18354     onResize : function(w, h)
18355     {
18356          Roo.log('resize: ' +w + ',' + h );
18357         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18358         if(!this.iframe){
18359             return;
18360         }
18361         if(typeof w == 'number'){
18362             
18363             this.iframe.style.width = w + 'px';
18364         }
18365         if(typeof h == 'number'){
18366             
18367             this.iframe.style.height = h + 'px';
18368             if(this.doc){
18369                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18370             }
18371         }
18372         
18373     },
18374
18375     /**
18376      * Toggles the editor between standard and source edit mode.
18377      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18378      */
18379     toggleSourceEdit : function(sourceEditMode){
18380         
18381         this.sourceEditMode = sourceEditMode === true;
18382         
18383         if(this.sourceEditMode){
18384  
18385             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18386             
18387         }else{
18388             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18389             //this.iframe.className = '';
18390             this.deferFocus();
18391         }
18392         //this.setSize(this.owner.wrap.getSize());
18393         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18394     },
18395
18396     
18397   
18398
18399     /**
18400      * Protected method that will not generally be called directly. If you need/want
18401      * custom HTML cleanup, this is the method you should override.
18402      * @param {String} html The HTML to be cleaned
18403      * return {String} The cleaned HTML
18404      */
18405     cleanHtml : function(html){
18406         html = String(html);
18407         if(html.length > 5){
18408             if(Roo.isSafari){ // strip safari nonsense
18409                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18410             }
18411         }
18412         if(html == '&nbsp;'){
18413             html = '';
18414         }
18415         return html;
18416     },
18417
18418     /**
18419      * HTML Editor -> Textarea
18420      * Protected method that will not generally be called directly. Syncs the contents
18421      * of the editor iframe with the textarea.
18422      */
18423     syncValue : function(){
18424         if(this.initialized){
18425             var bd = (this.doc.body || this.doc.documentElement);
18426             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18427             var html = bd.innerHTML;
18428             if(Roo.isSafari){
18429                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18430                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18431                 if(m && m[1]){
18432                     html = '<div style="'+m[0]+'">' + html + '</div>';
18433                 }
18434             }
18435             html = this.cleanHtml(html);
18436             // fix up the special chars.. normaly like back quotes in word...
18437             // however we do not want to do this with chinese..
18438             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18439                 var cc = b.charCodeAt();
18440                 if (
18441                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18442                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18443                     (cc >= 0xf900 && cc < 0xfb00 )
18444                 ) {
18445                         return b;
18446                 }
18447                 return "&#"+cc+";" 
18448             });
18449             if(this.owner.fireEvent('beforesync', this, html) !== false){
18450                 this.el.dom.value = html;
18451                 this.owner.fireEvent('sync', this, html);
18452             }
18453         }
18454     },
18455
18456     /**
18457      * Protected method that will not generally be called directly. Pushes the value of the textarea
18458      * into the iframe editor.
18459      */
18460     pushValue : function(){
18461         if(this.initialized){
18462             var v = this.el.dom.value.trim();
18463             
18464 //            if(v.length < 1){
18465 //                v = '&#160;';
18466 //            }
18467             
18468             if(this.owner.fireEvent('beforepush', this, v) !== false){
18469                 var d = (this.doc.body || this.doc.documentElement);
18470                 d.innerHTML = v;
18471                 this.cleanUpPaste();
18472                 this.el.dom.value = d.innerHTML;
18473                 this.owner.fireEvent('push', this, v);
18474             }
18475         }
18476     },
18477
18478     // private
18479     deferFocus : function(){
18480         this.focus.defer(10, this);
18481     },
18482
18483     // doc'ed in Field
18484     focus : function(){
18485         if(this.win && !this.sourceEditMode){
18486             this.win.focus();
18487         }else{
18488             this.el.focus();
18489         }
18490     },
18491     
18492     assignDocWin: function()
18493     {
18494         var iframe = this.iframe;
18495         
18496          if(Roo.isIE){
18497             this.doc = iframe.contentWindow.document;
18498             this.win = iframe.contentWindow;
18499         } else {
18500 //            if (!Roo.get(this.frameId)) {
18501 //                return;
18502 //            }
18503 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18504 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18505             
18506             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18507                 return;
18508             }
18509             
18510             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18511             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18512         }
18513     },
18514     
18515     // private
18516     initEditor : function(){
18517         //console.log("INIT EDITOR");
18518         this.assignDocWin();
18519         
18520         
18521         
18522         this.doc.designMode="on";
18523         this.doc.open();
18524         this.doc.write(this.getDocMarkup());
18525         this.doc.close();
18526         
18527         var dbody = (this.doc.body || this.doc.documentElement);
18528         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18529         // this copies styles from the containing element into thsi one..
18530         // not sure why we need all of this..
18531         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18532         
18533         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18534         //ss['background-attachment'] = 'fixed'; // w3c
18535         dbody.bgProperties = 'fixed'; // ie
18536         //Roo.DomHelper.applyStyles(dbody, ss);
18537         Roo.EventManager.on(this.doc, {
18538             //'mousedown': this.onEditorEvent,
18539             'mouseup': this.onEditorEvent,
18540             'dblclick': this.onEditorEvent,
18541             'click': this.onEditorEvent,
18542             'keyup': this.onEditorEvent,
18543             buffer:100,
18544             scope: this
18545         });
18546         if(Roo.isGecko){
18547             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18548         }
18549         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18550             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18551         }
18552         this.initialized = true;
18553
18554         this.owner.fireEvent('initialize', this);
18555         this.pushValue();
18556     },
18557
18558     // private
18559     onDestroy : function(){
18560         
18561         
18562         
18563         if(this.rendered){
18564             
18565             //for (var i =0; i < this.toolbars.length;i++) {
18566             //    // fixme - ask toolbars for heights?
18567             //    this.toolbars[i].onDestroy();
18568            // }
18569             
18570             //this.wrap.dom.innerHTML = '';
18571             //this.wrap.remove();
18572         }
18573     },
18574
18575     // private
18576     onFirstFocus : function(){
18577         
18578         this.assignDocWin();
18579         
18580         
18581         this.activated = true;
18582          
18583     
18584         if(Roo.isGecko){ // prevent silly gecko errors
18585             this.win.focus();
18586             var s = this.win.getSelection();
18587             if(!s.focusNode || s.focusNode.nodeType != 3){
18588                 var r = s.getRangeAt(0);
18589                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18590                 r.collapse(true);
18591                 this.deferFocus();
18592             }
18593             try{
18594                 this.execCmd('useCSS', true);
18595                 this.execCmd('styleWithCSS', false);
18596             }catch(e){}
18597         }
18598         this.owner.fireEvent('activate', this);
18599     },
18600
18601     // private
18602     adjustFont: function(btn){
18603         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18604         //if(Roo.isSafari){ // safari
18605         //    adjust *= 2;
18606        // }
18607         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18608         if(Roo.isSafari){ // safari
18609             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18610             v =  (v < 10) ? 10 : v;
18611             v =  (v > 48) ? 48 : v;
18612             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18613             
18614         }
18615         
18616         
18617         v = Math.max(1, v+adjust);
18618         
18619         this.execCmd('FontSize', v  );
18620     },
18621
18622     onEditorEvent : function(e)
18623     {
18624         this.owner.fireEvent('editorevent', this, e);
18625       //  this.updateToolbar();
18626         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18627     },
18628
18629     insertTag : function(tg)
18630     {
18631         // could be a bit smarter... -> wrap the current selected tRoo..
18632         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18633             
18634             range = this.createRange(this.getSelection());
18635             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18636             wrappingNode.appendChild(range.extractContents());
18637             range.insertNode(wrappingNode);
18638
18639             return;
18640             
18641             
18642             
18643         }
18644         this.execCmd("formatblock",   tg);
18645         
18646     },
18647     
18648     insertText : function(txt)
18649     {
18650         
18651         
18652         var range = this.createRange();
18653         range.deleteContents();
18654                //alert(Sender.getAttribute('label'));
18655                
18656         range.insertNode(this.doc.createTextNode(txt));
18657     } ,
18658     
18659      
18660
18661     /**
18662      * Executes a Midas editor command on the editor document and performs necessary focus and
18663      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18664      * @param {String} cmd The Midas command
18665      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18666      */
18667     relayCmd : function(cmd, value){
18668         this.win.focus();
18669         this.execCmd(cmd, value);
18670         this.owner.fireEvent('editorevent', this);
18671         //this.updateToolbar();
18672         this.owner.deferFocus();
18673     },
18674
18675     /**
18676      * Executes a Midas editor command directly on the editor document.
18677      * For visual commands, you should use {@link #relayCmd} instead.
18678      * <b>This should only be called after the editor is initialized.</b>
18679      * @param {String} cmd The Midas command
18680      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18681      */
18682     execCmd : function(cmd, value){
18683         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18684         this.syncValue();
18685     },
18686  
18687  
18688    
18689     /**
18690      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18691      * to insert tRoo.
18692      * @param {String} text | dom node.. 
18693      */
18694     insertAtCursor : function(text)
18695     {
18696         
18697         
18698         
18699         if(!this.activated){
18700             return;
18701         }
18702         /*
18703         if(Roo.isIE){
18704             this.win.focus();
18705             var r = this.doc.selection.createRange();
18706             if(r){
18707                 r.collapse(true);
18708                 r.pasteHTML(text);
18709                 this.syncValue();
18710                 this.deferFocus();
18711             
18712             }
18713             return;
18714         }
18715         */
18716         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18717             this.win.focus();
18718             
18719             
18720             // from jquery ui (MIT licenced)
18721             var range, node;
18722             var win = this.win;
18723             
18724             if (win.getSelection && win.getSelection().getRangeAt) {
18725                 range = win.getSelection().getRangeAt(0);
18726                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18727                 range.insertNode(node);
18728             } else if (win.document.selection && win.document.selection.createRange) {
18729                 // no firefox support
18730                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18731                 win.document.selection.createRange().pasteHTML(txt);
18732             } else {
18733                 // no firefox support
18734                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18735                 this.execCmd('InsertHTML', txt);
18736             } 
18737             
18738             this.syncValue();
18739             
18740             this.deferFocus();
18741         }
18742     },
18743  // private
18744     mozKeyPress : function(e){
18745         if(e.ctrlKey){
18746             var c = e.getCharCode(), cmd;
18747           
18748             if(c > 0){
18749                 c = String.fromCharCode(c).toLowerCase();
18750                 switch(c){
18751                     case 'b':
18752                         cmd = 'bold';
18753                         break;
18754                     case 'i':
18755                         cmd = 'italic';
18756                         break;
18757                     
18758                     case 'u':
18759                         cmd = 'underline';
18760                         break;
18761                     
18762                     case 'v':
18763                         this.cleanUpPaste.defer(100, this);
18764                         return;
18765                         
18766                 }
18767                 if(cmd){
18768                     this.win.focus();
18769                     this.execCmd(cmd);
18770                     this.deferFocus();
18771                     e.preventDefault();
18772                 }
18773                 
18774             }
18775         }
18776     },
18777
18778     // private
18779     fixKeys : function(){ // load time branching for fastest keydown performance
18780         if(Roo.isIE){
18781             return function(e){
18782                 var k = e.getKey(), r;
18783                 if(k == e.TAB){
18784                     e.stopEvent();
18785                     r = this.doc.selection.createRange();
18786                     if(r){
18787                         r.collapse(true);
18788                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18789                         this.deferFocus();
18790                     }
18791                     return;
18792                 }
18793                 
18794                 if(k == e.ENTER){
18795                     r = this.doc.selection.createRange();
18796                     if(r){
18797                         var target = r.parentElement();
18798                         if(!target || target.tagName.toLowerCase() != 'li'){
18799                             e.stopEvent();
18800                             r.pasteHTML('<br />');
18801                             r.collapse(false);
18802                             r.select();
18803                         }
18804                     }
18805                 }
18806                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18807                     this.cleanUpPaste.defer(100, this);
18808                     return;
18809                 }
18810                 
18811                 
18812             };
18813         }else if(Roo.isOpera){
18814             return function(e){
18815                 var k = e.getKey();
18816                 if(k == e.TAB){
18817                     e.stopEvent();
18818                     this.win.focus();
18819                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18820                     this.deferFocus();
18821                 }
18822                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18823                     this.cleanUpPaste.defer(100, this);
18824                     return;
18825                 }
18826                 
18827             };
18828         }else if(Roo.isSafari){
18829             return function(e){
18830                 var k = e.getKey();
18831                 
18832                 if(k == e.TAB){
18833                     e.stopEvent();
18834                     this.execCmd('InsertText','\t');
18835                     this.deferFocus();
18836                     return;
18837                 }
18838                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18839                     this.cleanUpPaste.defer(100, this);
18840                     return;
18841                 }
18842                 
18843              };
18844         }
18845     }(),
18846     
18847     getAllAncestors: function()
18848     {
18849         var p = this.getSelectedNode();
18850         var a = [];
18851         if (!p) {
18852             a.push(p); // push blank onto stack..
18853             p = this.getParentElement();
18854         }
18855         
18856         
18857         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18858             a.push(p);
18859             p = p.parentNode;
18860         }
18861         a.push(this.doc.body);
18862         return a;
18863     },
18864     lastSel : false,
18865     lastSelNode : false,
18866     
18867     
18868     getSelection : function() 
18869     {
18870         this.assignDocWin();
18871         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18872     },
18873     
18874     getSelectedNode: function() 
18875     {
18876         // this may only work on Gecko!!!
18877         
18878         // should we cache this!!!!
18879         
18880         
18881         
18882          
18883         var range = this.createRange(this.getSelection()).cloneRange();
18884         
18885         if (Roo.isIE) {
18886             var parent = range.parentElement();
18887             while (true) {
18888                 var testRange = range.duplicate();
18889                 testRange.moveToElementText(parent);
18890                 if (testRange.inRange(range)) {
18891                     break;
18892                 }
18893                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18894                     break;
18895                 }
18896                 parent = parent.parentElement;
18897             }
18898             return parent;
18899         }
18900         
18901         // is ancestor a text element.
18902         var ac =  range.commonAncestorContainer;
18903         if (ac.nodeType == 3) {
18904             ac = ac.parentNode;
18905         }
18906         
18907         var ar = ac.childNodes;
18908          
18909         var nodes = [];
18910         var other_nodes = [];
18911         var has_other_nodes = false;
18912         for (var i=0;i<ar.length;i++) {
18913             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18914                 continue;
18915             }
18916             // fullly contained node.
18917             
18918             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18919                 nodes.push(ar[i]);
18920                 continue;
18921             }
18922             
18923             // probably selected..
18924             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18925                 other_nodes.push(ar[i]);
18926                 continue;
18927             }
18928             // outer..
18929             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18930                 continue;
18931             }
18932             
18933             
18934             has_other_nodes = true;
18935         }
18936         if (!nodes.length && other_nodes.length) {
18937             nodes= other_nodes;
18938         }
18939         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18940             return false;
18941         }
18942         
18943         return nodes[0];
18944     },
18945     createRange: function(sel)
18946     {
18947         // this has strange effects when using with 
18948         // top toolbar - not sure if it's a great idea.
18949         //this.editor.contentWindow.focus();
18950         if (typeof sel != "undefined") {
18951             try {
18952                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18953             } catch(e) {
18954                 return this.doc.createRange();
18955             }
18956         } else {
18957             return this.doc.createRange();
18958         }
18959     },
18960     getParentElement: function()
18961     {
18962         
18963         this.assignDocWin();
18964         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18965         
18966         var range = this.createRange(sel);
18967          
18968         try {
18969             var p = range.commonAncestorContainer;
18970             while (p.nodeType == 3) { // text node
18971                 p = p.parentNode;
18972             }
18973             return p;
18974         } catch (e) {
18975             return null;
18976         }
18977     
18978     },
18979     /***
18980      *
18981      * Range intersection.. the hard stuff...
18982      *  '-1' = before
18983      *  '0' = hits..
18984      *  '1' = after.
18985      *         [ -- selected range --- ]
18986      *   [fail]                        [fail]
18987      *
18988      *    basically..
18989      *      if end is before start or  hits it. fail.
18990      *      if start is after end or hits it fail.
18991      *
18992      *   if either hits (but other is outside. - then it's not 
18993      *   
18994      *    
18995      **/
18996     
18997     
18998     // @see http://www.thismuchiknow.co.uk/?p=64.
18999     rangeIntersectsNode : function(range, node)
19000     {
19001         var nodeRange = node.ownerDocument.createRange();
19002         try {
19003             nodeRange.selectNode(node);
19004         } catch (e) {
19005             nodeRange.selectNodeContents(node);
19006         }
19007     
19008         var rangeStartRange = range.cloneRange();
19009         rangeStartRange.collapse(true);
19010     
19011         var rangeEndRange = range.cloneRange();
19012         rangeEndRange.collapse(false);
19013     
19014         var nodeStartRange = nodeRange.cloneRange();
19015         nodeStartRange.collapse(true);
19016     
19017         var nodeEndRange = nodeRange.cloneRange();
19018         nodeEndRange.collapse(false);
19019     
19020         return rangeStartRange.compareBoundaryPoints(
19021                  Range.START_TO_START, nodeEndRange) == -1 &&
19022                rangeEndRange.compareBoundaryPoints(
19023                  Range.START_TO_START, nodeStartRange) == 1;
19024         
19025          
19026     },
19027     rangeCompareNode : function(range, node)
19028     {
19029         var nodeRange = node.ownerDocument.createRange();
19030         try {
19031             nodeRange.selectNode(node);
19032         } catch (e) {
19033             nodeRange.selectNodeContents(node);
19034         }
19035         
19036         
19037         range.collapse(true);
19038     
19039         nodeRange.collapse(true);
19040      
19041         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19042         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19043          
19044         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19045         
19046         var nodeIsBefore   =  ss == 1;
19047         var nodeIsAfter    = ee == -1;
19048         
19049         if (nodeIsBefore && nodeIsAfter)
19050             return 0; // outer
19051         if (!nodeIsBefore && nodeIsAfter)
19052             return 1; //right trailed.
19053         
19054         if (nodeIsBefore && !nodeIsAfter)
19055             return 2;  // left trailed.
19056         // fully contined.
19057         return 3;
19058     },
19059
19060     // private? - in a new class?
19061     cleanUpPaste :  function()
19062     {
19063         // cleans up the whole document..
19064         Roo.log('cleanuppaste');
19065         
19066         this.cleanUpChildren(this.doc.body);
19067         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19068         if (clean != this.doc.body.innerHTML) {
19069             this.doc.body.innerHTML = clean;
19070         }
19071         
19072     },
19073     
19074     cleanWordChars : function(input) {// change the chars to hex code
19075         var he = Roo.HtmlEditorCore;
19076         
19077         var output = input;
19078         Roo.each(he.swapCodes, function(sw) { 
19079             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19080             
19081             output = output.replace(swapper, sw[1]);
19082         });
19083         
19084         return output;
19085     },
19086     
19087     
19088     cleanUpChildren : function (n)
19089     {
19090         if (!n.childNodes.length) {
19091             return;
19092         }
19093         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19094            this.cleanUpChild(n.childNodes[i]);
19095         }
19096     },
19097     
19098     
19099         
19100     
19101     cleanUpChild : function (node)
19102     {
19103         var ed = this;
19104         //console.log(node);
19105         if (node.nodeName == "#text") {
19106             // clean up silly Windows -- stuff?
19107             return; 
19108         }
19109         if (node.nodeName == "#comment") {
19110             node.parentNode.removeChild(node);
19111             // clean up silly Windows -- stuff?
19112             return; 
19113         }
19114         var lcname = node.tagName.toLowerCase();
19115         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19116         // whitelist of tags..
19117         
19118         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19119             // remove node.
19120             node.parentNode.removeChild(node);
19121             return;
19122             
19123         }
19124         
19125         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19126         
19127         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19128         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19129         
19130         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19131         //    remove_keep_children = true;
19132         //}
19133         
19134         if (remove_keep_children) {
19135             this.cleanUpChildren(node);
19136             // inserts everything just before this node...
19137             while (node.childNodes.length) {
19138                 var cn = node.childNodes[0];
19139                 node.removeChild(cn);
19140                 node.parentNode.insertBefore(cn, node);
19141             }
19142             node.parentNode.removeChild(node);
19143             return;
19144         }
19145         
19146         if (!node.attributes || !node.attributes.length) {
19147             this.cleanUpChildren(node);
19148             return;
19149         }
19150         
19151         function cleanAttr(n,v)
19152         {
19153             
19154             if (v.match(/^\./) || v.match(/^\//)) {
19155                 return;
19156             }
19157             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19158                 return;
19159             }
19160             if (v.match(/^#/)) {
19161                 return;
19162             }
19163 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19164             node.removeAttribute(n);
19165             
19166         }
19167         
19168         var cwhite = this.cwhite;
19169         var cblack = this.cblack;
19170             
19171         function cleanStyle(n,v)
19172         {
19173             if (v.match(/expression/)) { //XSS?? should we even bother..
19174                 node.removeAttribute(n);
19175                 return;
19176             }
19177             
19178             var parts = v.split(/;/);
19179             var clean = [];
19180             
19181             Roo.each(parts, function(p) {
19182                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19183                 if (!p.length) {
19184                     return true;
19185                 }
19186                 var l = p.split(':').shift().replace(/\s+/g,'');
19187                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19188                 
19189                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19190 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19191                     //node.removeAttribute(n);
19192                     return true;
19193                 }
19194                 //Roo.log()
19195                 // only allow 'c whitelisted system attributes'
19196                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19197 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19198                     //node.removeAttribute(n);
19199                     return true;
19200                 }
19201                 
19202                 
19203                  
19204                 
19205                 clean.push(p);
19206                 return true;
19207             });
19208             if (clean.length) { 
19209                 node.setAttribute(n, clean.join(';'));
19210             } else {
19211                 node.removeAttribute(n);
19212             }
19213             
19214         }
19215         
19216         
19217         for (var i = node.attributes.length-1; i > -1 ; i--) {
19218             var a = node.attributes[i];
19219             //console.log(a);
19220             
19221             if (a.name.toLowerCase().substr(0,2)=='on')  {
19222                 node.removeAttribute(a.name);
19223                 continue;
19224             }
19225             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19226                 node.removeAttribute(a.name);
19227                 continue;
19228             }
19229             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19230                 cleanAttr(a.name,a.value); // fixme..
19231                 continue;
19232             }
19233             if (a.name == 'style') {
19234                 cleanStyle(a.name,a.value);
19235                 continue;
19236             }
19237             /// clean up MS crap..
19238             // tecnically this should be a list of valid class'es..
19239             
19240             
19241             if (a.name == 'class') {
19242                 if (a.value.match(/^Mso/)) {
19243                     node.className = '';
19244                 }
19245                 
19246                 if (a.value.match(/body/)) {
19247                     node.className = '';
19248                 }
19249                 continue;
19250             }
19251             
19252             // style cleanup!?
19253             // class cleanup?
19254             
19255         }
19256         
19257         
19258         this.cleanUpChildren(node);
19259         
19260         
19261     },
19262     
19263     /**
19264      * Clean up MS wordisms...
19265      */
19266     cleanWord : function(node)
19267     {
19268         
19269         
19270         if (!node) {
19271             this.cleanWord(this.doc.body);
19272             return;
19273         }
19274         if (node.nodeName == "#text") {
19275             // clean up silly Windows -- stuff?
19276             return; 
19277         }
19278         if (node.nodeName == "#comment") {
19279             node.parentNode.removeChild(node);
19280             // clean up silly Windows -- stuff?
19281             return; 
19282         }
19283         
19284         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19285             node.parentNode.removeChild(node);
19286             return;
19287         }
19288         
19289         // remove - but keep children..
19290         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19291             while (node.childNodes.length) {
19292                 var cn = node.childNodes[0];
19293                 node.removeChild(cn);
19294                 node.parentNode.insertBefore(cn, node);
19295             }
19296             node.parentNode.removeChild(node);
19297             this.iterateChildren(node, this.cleanWord);
19298             return;
19299         }
19300         // clean styles
19301         if (node.className.length) {
19302             
19303             var cn = node.className.split(/\W+/);
19304             var cna = [];
19305             Roo.each(cn, function(cls) {
19306                 if (cls.match(/Mso[a-zA-Z]+/)) {
19307                     return;
19308                 }
19309                 cna.push(cls);
19310             });
19311             node.className = cna.length ? cna.join(' ') : '';
19312             if (!cna.length) {
19313                 node.removeAttribute("class");
19314             }
19315         }
19316         
19317         if (node.hasAttribute("lang")) {
19318             node.removeAttribute("lang");
19319         }
19320         
19321         if (node.hasAttribute("style")) {
19322             
19323             var styles = node.getAttribute("style").split(";");
19324             var nstyle = [];
19325             Roo.each(styles, function(s) {
19326                 if (!s.match(/:/)) {
19327                     return;
19328                 }
19329                 var kv = s.split(":");
19330                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19331                     return;
19332                 }
19333                 // what ever is left... we allow.
19334                 nstyle.push(s);
19335             });
19336             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19337             if (!nstyle.length) {
19338                 node.removeAttribute('style');
19339             }
19340         }
19341         this.iterateChildren(node, this.cleanWord);
19342         
19343         
19344         
19345     },
19346     /**
19347      * iterateChildren of a Node, calling fn each time, using this as the scole..
19348      * @param {DomNode} node node to iterate children of.
19349      * @param {Function} fn method of this class to call on each item.
19350      */
19351     iterateChildren : function(node, fn)
19352     {
19353         if (!node.childNodes.length) {
19354                 return;
19355         }
19356         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19357            fn.call(this, node.childNodes[i])
19358         }
19359     },
19360     
19361     
19362     /**
19363      * cleanTableWidths.
19364      *
19365      * Quite often pasting from word etc.. results in tables with column and widths.
19366      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19367      *
19368      */
19369     cleanTableWidths : function(node)
19370     {
19371          
19372          
19373         if (!node) {
19374             this.cleanTableWidths(this.doc.body);
19375             return;
19376         }
19377         
19378         // ignore list...
19379         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19380             return; 
19381         }
19382         Roo.log(node.tagName);
19383         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19384             this.iterateChildren(node, this.cleanTableWidths);
19385             return;
19386         }
19387         if (node.hasAttribute('width')) {
19388             node.removeAttribute('width');
19389         }
19390         
19391          
19392         if (node.hasAttribute("style")) {
19393             // pretty basic...
19394             
19395             var styles = node.getAttribute("style").split(";");
19396             var nstyle = [];
19397             Roo.each(styles, function(s) {
19398                 if (!s.match(/:/)) {
19399                     return;
19400                 }
19401                 var kv = s.split(":");
19402                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
19403                     return;
19404                 }
19405                 // what ever is left... we allow.
19406                 nstyle.push(s);
19407             });
19408             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19409             if (!nstyle.length) {
19410                 node.removeAttribute('style');
19411             }
19412         }
19413         
19414         this.iterateChildren(node, this.cleanTableWidths);
19415         
19416         
19417     },
19418     
19419     
19420     
19421     
19422     domToHTML : function(currentElement, depth, nopadtext) {
19423         
19424         depth = depth || 0;
19425         nopadtext = nopadtext || false;
19426     
19427         if (!currentElement) {
19428             return this.domToHTML(this.doc.body);
19429         }
19430         
19431         //Roo.log(currentElement);
19432         var j;
19433         var allText = false;
19434         var nodeName = currentElement.nodeName;
19435         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19436         
19437         if  (nodeName == '#text') {
19438             
19439             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19440         }
19441         
19442         
19443         var ret = '';
19444         if (nodeName != 'BODY') {
19445              
19446             var i = 0;
19447             // Prints the node tagName, such as <A>, <IMG>, etc
19448             if (tagName) {
19449                 var attr = [];
19450                 for(i = 0; i < currentElement.attributes.length;i++) {
19451                     // quoting?
19452                     var aname = currentElement.attributes.item(i).name;
19453                     if (!currentElement.attributes.item(i).value.length) {
19454                         continue;
19455                     }
19456                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19457                 }
19458                 
19459                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19460             } 
19461             else {
19462                 
19463                 // eack
19464             }
19465         } else {
19466             tagName = false;
19467         }
19468         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19469             return ret;
19470         }
19471         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19472             nopadtext = true;
19473         }
19474         
19475         
19476         // Traverse the tree
19477         i = 0;
19478         var currentElementChild = currentElement.childNodes.item(i);
19479         var allText = true;
19480         var innerHTML  = '';
19481         lastnode = '';
19482         while (currentElementChild) {
19483             // Formatting code (indent the tree so it looks nice on the screen)
19484             var nopad = nopadtext;
19485             if (lastnode == 'SPAN') {
19486                 nopad  = true;
19487             }
19488             // text
19489             if  (currentElementChild.nodeName == '#text') {
19490                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19491                 toadd = nopadtext ? toadd : toadd.trim();
19492                 if (!nopad && toadd.length > 80) {
19493                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19494                 }
19495                 innerHTML  += toadd;
19496                 
19497                 i++;
19498                 currentElementChild = currentElement.childNodes.item(i);
19499                 lastNode = '';
19500                 continue;
19501             }
19502             allText = false;
19503             
19504             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19505                 
19506             // Recursively traverse the tree structure of the child node
19507             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19508             lastnode = currentElementChild.nodeName;
19509             i++;
19510             currentElementChild=currentElement.childNodes.item(i);
19511         }
19512         
19513         ret += innerHTML;
19514         
19515         if (!allText) {
19516                 // The remaining code is mostly for formatting the tree
19517             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19518         }
19519         
19520         
19521         if (tagName) {
19522             ret+= "</"+tagName+">";
19523         }
19524         return ret;
19525         
19526     },
19527         
19528     applyBlacklists : function()
19529     {
19530         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19531         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19532         
19533         this.white = [];
19534         this.black = [];
19535         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19536             if (b.indexOf(tag) > -1) {
19537                 return;
19538             }
19539             this.white.push(tag);
19540             
19541         }, this);
19542         
19543         Roo.each(w, function(tag) {
19544             if (b.indexOf(tag) > -1) {
19545                 return;
19546             }
19547             if (this.white.indexOf(tag) > -1) {
19548                 return;
19549             }
19550             this.white.push(tag);
19551             
19552         }, this);
19553         
19554         
19555         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19556             if (w.indexOf(tag) > -1) {
19557                 return;
19558             }
19559             this.black.push(tag);
19560             
19561         }, this);
19562         
19563         Roo.each(b, function(tag) {
19564             if (w.indexOf(tag) > -1) {
19565                 return;
19566             }
19567             if (this.black.indexOf(tag) > -1) {
19568                 return;
19569             }
19570             this.black.push(tag);
19571             
19572         }, this);
19573         
19574         
19575         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19576         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19577         
19578         this.cwhite = [];
19579         this.cblack = [];
19580         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19581             if (b.indexOf(tag) > -1) {
19582                 return;
19583             }
19584             this.cwhite.push(tag);
19585             
19586         }, this);
19587         
19588         Roo.each(w, function(tag) {
19589             if (b.indexOf(tag) > -1) {
19590                 return;
19591             }
19592             if (this.cwhite.indexOf(tag) > -1) {
19593                 return;
19594             }
19595             this.cwhite.push(tag);
19596             
19597         }, this);
19598         
19599         
19600         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19601             if (w.indexOf(tag) > -1) {
19602                 return;
19603             }
19604             this.cblack.push(tag);
19605             
19606         }, this);
19607         
19608         Roo.each(b, function(tag) {
19609             if (w.indexOf(tag) > -1) {
19610                 return;
19611             }
19612             if (this.cblack.indexOf(tag) > -1) {
19613                 return;
19614             }
19615             this.cblack.push(tag);
19616             
19617         }, this);
19618     },
19619     
19620     setStylesheets : function(stylesheets)
19621     {
19622         if(typeof(stylesheets) == 'string'){
19623             Roo.get(this.iframe.contentDocument.head).createChild({
19624                 tag : 'link',
19625                 rel : 'stylesheet',
19626                 type : 'text/css',
19627                 href : stylesheets
19628             });
19629             
19630             return;
19631         }
19632         var _this = this;
19633      
19634         Roo.each(stylesheets, function(s) {
19635             if(!s.length){
19636                 return;
19637             }
19638             
19639             Roo.get(_this.iframe.contentDocument.head).createChild({
19640                 tag : 'link',
19641                 rel : 'stylesheet',
19642                 type : 'text/css',
19643                 href : s
19644             });
19645         });
19646
19647         
19648     },
19649     
19650     removeStylesheets : function()
19651     {
19652         var _this = this;
19653         
19654         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19655             s.remove();
19656         });
19657     }
19658     
19659     // hide stuff that is not compatible
19660     /**
19661      * @event blur
19662      * @hide
19663      */
19664     /**
19665      * @event change
19666      * @hide
19667      */
19668     /**
19669      * @event focus
19670      * @hide
19671      */
19672     /**
19673      * @event specialkey
19674      * @hide
19675      */
19676     /**
19677      * @cfg {String} fieldClass @hide
19678      */
19679     /**
19680      * @cfg {String} focusClass @hide
19681      */
19682     /**
19683      * @cfg {String} autoCreate @hide
19684      */
19685     /**
19686      * @cfg {String} inputType @hide
19687      */
19688     /**
19689      * @cfg {String} invalidClass @hide
19690      */
19691     /**
19692      * @cfg {String} invalidText @hide
19693      */
19694     /**
19695      * @cfg {String} msgFx @hide
19696      */
19697     /**
19698      * @cfg {String} validateOnBlur @hide
19699      */
19700 });
19701
19702 Roo.HtmlEditorCore.white = [
19703         'area', 'br', 'img', 'input', 'hr', 'wbr',
19704         
19705        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19706        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19707        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19708        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19709        'table',   'ul',         'xmp', 
19710        
19711        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19712       'thead',   'tr', 
19713      
19714       'dir', 'menu', 'ol', 'ul', 'dl',
19715        
19716       'embed',  'object'
19717 ];
19718
19719
19720 Roo.HtmlEditorCore.black = [
19721     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19722         'applet', // 
19723         'base',   'basefont', 'bgsound', 'blink',  'body', 
19724         'frame',  'frameset', 'head',    'html',   'ilayer', 
19725         'iframe', 'layer',  'link',     'meta',    'object',   
19726         'script', 'style' ,'title',  'xml' // clean later..
19727 ];
19728 Roo.HtmlEditorCore.clean = [
19729     'script', 'style', 'title', 'xml'
19730 ];
19731 Roo.HtmlEditorCore.remove = [
19732     'font'
19733 ];
19734 // attributes..
19735
19736 Roo.HtmlEditorCore.ablack = [
19737     'on'
19738 ];
19739     
19740 Roo.HtmlEditorCore.aclean = [ 
19741     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19742 ];
19743
19744 // protocols..
19745 Roo.HtmlEditorCore.pwhite= [
19746         'http',  'https',  'mailto'
19747 ];
19748
19749 // white listed style attributes.
19750 Roo.HtmlEditorCore.cwhite= [
19751       //  'text-align', /// default is to allow most things..
19752       
19753          
19754 //        'font-size'//??
19755 ];
19756
19757 // black listed style attributes.
19758 Roo.HtmlEditorCore.cblack= [
19759       //  'font-size' -- this can be set by the project 
19760 ];
19761
19762
19763 Roo.HtmlEditorCore.swapCodes   =[ 
19764     [    8211, "--" ], 
19765     [    8212, "--" ], 
19766     [    8216,  "'" ],  
19767     [    8217, "'" ],  
19768     [    8220, '"' ],  
19769     [    8221, '"' ],  
19770     [    8226, "*" ],  
19771     [    8230, "..." ]
19772 ]; 
19773
19774     /*
19775  * - LGPL
19776  *
19777  * HtmlEditor
19778  * 
19779  */
19780
19781 /**
19782  * @class Roo.bootstrap.HtmlEditor
19783  * @extends Roo.bootstrap.TextArea
19784  * Bootstrap HtmlEditor class
19785
19786  * @constructor
19787  * Create a new HtmlEditor
19788  * @param {Object} config The config object
19789  */
19790
19791 Roo.bootstrap.HtmlEditor = function(config){
19792     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19793     if (!this.toolbars) {
19794         this.toolbars = [];
19795     }
19796     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19797     this.addEvents({
19798             /**
19799              * @event initialize
19800              * Fires when the editor is fully initialized (including the iframe)
19801              * @param {HtmlEditor} this
19802              */
19803             initialize: true,
19804             /**
19805              * @event activate
19806              * Fires when the editor is first receives the focus. Any insertion must wait
19807              * until after this event.
19808              * @param {HtmlEditor} this
19809              */
19810             activate: true,
19811              /**
19812              * @event beforesync
19813              * Fires before the textarea is updated with content from the editor iframe. Return false
19814              * to cancel the sync.
19815              * @param {HtmlEditor} this
19816              * @param {String} html
19817              */
19818             beforesync: true,
19819              /**
19820              * @event beforepush
19821              * Fires before the iframe editor is updated with content from the textarea. Return false
19822              * to cancel the push.
19823              * @param {HtmlEditor} this
19824              * @param {String} html
19825              */
19826             beforepush: true,
19827              /**
19828              * @event sync
19829              * Fires when the textarea is updated with content from the editor iframe.
19830              * @param {HtmlEditor} this
19831              * @param {String} html
19832              */
19833             sync: true,
19834              /**
19835              * @event push
19836              * Fires when the iframe editor is updated with content from the textarea.
19837              * @param {HtmlEditor} this
19838              * @param {String} html
19839              */
19840             push: true,
19841              /**
19842              * @event editmodechange
19843              * Fires when the editor switches edit modes
19844              * @param {HtmlEditor} this
19845              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19846              */
19847             editmodechange: true,
19848             /**
19849              * @event editorevent
19850              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19851              * @param {HtmlEditor} this
19852              */
19853             editorevent: true,
19854             /**
19855              * @event firstfocus
19856              * Fires when on first focus - needed by toolbars..
19857              * @param {HtmlEditor} this
19858              */
19859             firstfocus: true,
19860             /**
19861              * @event autosave
19862              * Auto save the htmlEditor value as a file into Events
19863              * @param {HtmlEditor} this
19864              */
19865             autosave: true,
19866             /**
19867              * @event savedpreview
19868              * preview the saved version of htmlEditor
19869              * @param {HtmlEditor} this
19870              */
19871             savedpreview: true
19872         });
19873 };
19874
19875
19876 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19877     
19878     
19879       /**
19880      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19881      */
19882     toolbars : false,
19883    
19884      /**
19885      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19886      *                        Roo.resizable.
19887      */
19888     resizable : false,
19889      /**
19890      * @cfg {Number} height (in pixels)
19891      */   
19892     height: 300,
19893    /**
19894      * @cfg {Number} width (in pixels)
19895      */   
19896     width: false,
19897     
19898     /**
19899      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19900      * 
19901      */
19902     stylesheets: false,
19903     
19904     // id of frame..
19905     frameId: false,
19906     
19907     // private properties
19908     validationEvent : false,
19909     deferHeight: true,
19910     initialized : false,
19911     activated : false,
19912     
19913     onFocus : Roo.emptyFn,
19914     iframePad:3,
19915     hideMode:'offsets',
19916     
19917     
19918     tbContainer : false,
19919     
19920     toolbarContainer :function() {
19921         return this.wrap.select('.x-html-editor-tb',true).first();
19922     },
19923
19924     /**
19925      * Protected method that will not generally be called directly. It
19926      * is called when the editor creates its toolbar. Override this method if you need to
19927      * add custom toolbar buttons.
19928      * @param {HtmlEditor} editor
19929      */
19930     createToolbar : function(){
19931         
19932         Roo.log("create toolbars");
19933         
19934         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19935         this.toolbars[0].render(this.toolbarContainer());
19936         
19937         return;
19938         
19939 //        if (!editor.toolbars || !editor.toolbars.length) {
19940 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19941 //        }
19942 //        
19943 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19944 //            editor.toolbars[i] = Roo.factory(
19945 //                    typeof(editor.toolbars[i]) == 'string' ?
19946 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19947 //                Roo.bootstrap.HtmlEditor);
19948 //            editor.toolbars[i].init(editor);
19949 //        }
19950     },
19951
19952      
19953     // private
19954     onRender : function(ct, position)
19955     {
19956        // Roo.log("Call onRender: " + this.xtype);
19957         var _t = this;
19958         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19959       
19960         this.wrap = this.inputEl().wrap({
19961             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19962         });
19963         
19964         this.editorcore.onRender(ct, position);
19965          
19966         if (this.resizable) {
19967             this.resizeEl = new Roo.Resizable(this.wrap, {
19968                 pinned : true,
19969                 wrap: true,
19970                 dynamic : true,
19971                 minHeight : this.height,
19972                 height: this.height,
19973                 handles : this.resizable,
19974                 width: this.width,
19975                 listeners : {
19976                     resize : function(r, w, h) {
19977                         _t.onResize(w,h); // -something
19978                     }
19979                 }
19980             });
19981             
19982         }
19983         this.createToolbar(this);
19984        
19985         
19986         if(!this.width && this.resizable){
19987             this.setSize(this.wrap.getSize());
19988         }
19989         if (this.resizeEl) {
19990             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19991             // should trigger onReize..
19992         }
19993         
19994     },
19995
19996     // private
19997     onResize : function(w, h)
19998     {
19999         Roo.log('resize: ' +w + ',' + h );
20000         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20001         var ew = false;
20002         var eh = false;
20003         
20004         if(this.inputEl() ){
20005             if(typeof w == 'number'){
20006                 var aw = w - this.wrap.getFrameWidth('lr');
20007                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20008                 ew = aw;
20009             }
20010             if(typeof h == 'number'){
20011                  var tbh = -11;  // fixme it needs to tool bar size!
20012                 for (var i =0; i < this.toolbars.length;i++) {
20013                     // fixme - ask toolbars for heights?
20014                     tbh += this.toolbars[i].el.getHeight();
20015                     //if (this.toolbars[i].footer) {
20016                     //    tbh += this.toolbars[i].footer.el.getHeight();
20017                     //}
20018                 }
20019               
20020                 
20021                 
20022                 
20023                 
20024                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20025                 ah -= 5; // knock a few pixes off for look..
20026                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20027                 var eh = ah;
20028             }
20029         }
20030         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20031         this.editorcore.onResize(ew,eh);
20032         
20033     },
20034
20035     /**
20036      * Toggles the editor between standard and source edit mode.
20037      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20038      */
20039     toggleSourceEdit : function(sourceEditMode)
20040     {
20041         this.editorcore.toggleSourceEdit(sourceEditMode);
20042         
20043         if(this.editorcore.sourceEditMode){
20044             Roo.log('editor - showing textarea');
20045             
20046 //            Roo.log('in');
20047 //            Roo.log(this.syncValue());
20048             this.syncValue();
20049             this.inputEl().removeClass(['hide', 'x-hidden']);
20050             this.inputEl().dom.removeAttribute('tabIndex');
20051             this.inputEl().focus();
20052         }else{
20053             Roo.log('editor - hiding textarea');
20054 //            Roo.log('out')
20055 //            Roo.log(this.pushValue()); 
20056             this.pushValue();
20057             
20058             this.inputEl().addClass(['hide', 'x-hidden']);
20059             this.inputEl().dom.setAttribute('tabIndex', -1);
20060             //this.deferFocus();
20061         }
20062          
20063         if(this.resizable){
20064             this.setSize(this.wrap.getSize());
20065         }
20066         
20067         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20068     },
20069  
20070     // private (for BoxComponent)
20071     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20072
20073     // private (for BoxComponent)
20074     getResizeEl : function(){
20075         return this.wrap;
20076     },
20077
20078     // private (for BoxComponent)
20079     getPositionEl : function(){
20080         return this.wrap;
20081     },
20082
20083     // private
20084     initEvents : function(){
20085         this.originalValue = this.getValue();
20086     },
20087
20088 //    /**
20089 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20090 //     * @method
20091 //     */
20092 //    markInvalid : Roo.emptyFn,
20093 //    /**
20094 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20095 //     * @method
20096 //     */
20097 //    clearInvalid : Roo.emptyFn,
20098
20099     setValue : function(v){
20100         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20101         this.editorcore.pushValue();
20102     },
20103
20104      
20105     // private
20106     deferFocus : function(){
20107         this.focus.defer(10, this);
20108     },
20109
20110     // doc'ed in Field
20111     focus : function(){
20112         this.editorcore.focus();
20113         
20114     },
20115       
20116
20117     // private
20118     onDestroy : function(){
20119         
20120         
20121         
20122         if(this.rendered){
20123             
20124             for (var i =0; i < this.toolbars.length;i++) {
20125                 // fixme - ask toolbars for heights?
20126                 this.toolbars[i].onDestroy();
20127             }
20128             
20129             this.wrap.dom.innerHTML = '';
20130             this.wrap.remove();
20131         }
20132     },
20133
20134     // private
20135     onFirstFocus : function(){
20136         //Roo.log("onFirstFocus");
20137         this.editorcore.onFirstFocus();
20138          for (var i =0; i < this.toolbars.length;i++) {
20139             this.toolbars[i].onFirstFocus();
20140         }
20141         
20142     },
20143     
20144     // private
20145     syncValue : function()
20146     {   
20147         this.editorcore.syncValue();
20148     },
20149     
20150     pushValue : function()
20151     {   
20152         this.editorcore.pushValue();
20153     }
20154      
20155     
20156     // hide stuff that is not compatible
20157     /**
20158      * @event blur
20159      * @hide
20160      */
20161     /**
20162      * @event change
20163      * @hide
20164      */
20165     /**
20166      * @event focus
20167      * @hide
20168      */
20169     /**
20170      * @event specialkey
20171      * @hide
20172      */
20173     /**
20174      * @cfg {String} fieldClass @hide
20175      */
20176     /**
20177      * @cfg {String} focusClass @hide
20178      */
20179     /**
20180      * @cfg {String} autoCreate @hide
20181      */
20182     /**
20183      * @cfg {String} inputType @hide
20184      */
20185     /**
20186      * @cfg {String} invalidClass @hide
20187      */
20188     /**
20189      * @cfg {String} invalidText @hide
20190      */
20191     /**
20192      * @cfg {String} msgFx @hide
20193      */
20194     /**
20195      * @cfg {String} validateOnBlur @hide
20196      */
20197 });
20198  
20199     
20200    
20201    
20202    
20203       
20204 Roo.namespace('Roo.bootstrap.htmleditor');
20205 /**
20206  * @class Roo.bootstrap.HtmlEditorToolbar1
20207  * Basic Toolbar
20208  * 
20209  * Usage:
20210  *
20211  new Roo.bootstrap.HtmlEditor({
20212     ....
20213     toolbars : [
20214         new Roo.bootstrap.HtmlEditorToolbar1({
20215             disable : { fonts: 1 , format: 1, ..., ... , ...],
20216             btns : [ .... ]
20217         })
20218     }
20219      
20220  * 
20221  * @cfg {Object} disable List of elements to disable..
20222  * @cfg {Array} btns List of additional buttons.
20223  * 
20224  * 
20225  * NEEDS Extra CSS? 
20226  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20227  */
20228  
20229 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20230 {
20231     
20232     Roo.apply(this, config);
20233     
20234     // default disabled, based on 'good practice'..
20235     this.disable = this.disable || {};
20236     Roo.applyIf(this.disable, {
20237         fontSize : true,
20238         colors : true,
20239         specialElements : true
20240     });
20241     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20242     
20243     this.editor = config.editor;
20244     this.editorcore = config.editor.editorcore;
20245     
20246     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20247     
20248     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20249     // dont call parent... till later.
20250 }
20251 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20252      
20253     bar : true,
20254     
20255     editor : false,
20256     editorcore : false,
20257     
20258     
20259     formats : [
20260         "p" ,  
20261         "h1","h2","h3","h4","h5","h6", 
20262         "pre", "code", 
20263         "abbr", "acronym", "address", "cite", "samp", "var",
20264         'div','span'
20265     ],
20266     
20267     onRender : function(ct, position)
20268     {
20269        // Roo.log("Call onRender: " + this.xtype);
20270         
20271        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20272        Roo.log(this.el);
20273        this.el.dom.style.marginBottom = '0';
20274        var _this = this;
20275        var editorcore = this.editorcore;
20276        var editor= this.editor;
20277        
20278        var children = [];
20279        var btn = function(id,cmd , toggle, handler){
20280        
20281             var  event = toggle ? 'toggle' : 'click';
20282        
20283             var a = {
20284                 size : 'sm',
20285                 xtype: 'Button',
20286                 xns: Roo.bootstrap,
20287                 glyphicon : id,
20288                 cmd : id || cmd,
20289                 enableToggle:toggle !== false,
20290                 //html : 'submit'
20291                 pressed : toggle ? false : null,
20292                 listeners : {}
20293             }
20294             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20295                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20296             }
20297             children.push(a);
20298             return a;
20299        }
20300         
20301         var style = {
20302                 xtype: 'Button',
20303                 size : 'sm',
20304                 xns: Roo.bootstrap,
20305                 glyphicon : 'font',
20306                 //html : 'submit'
20307                 menu : {
20308                     xtype: 'Menu',
20309                     xns: Roo.bootstrap,
20310                     items:  []
20311                 }
20312         };
20313         Roo.each(this.formats, function(f) {
20314             style.menu.items.push({
20315                 xtype :'MenuItem',
20316                 xns: Roo.bootstrap,
20317                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20318                 tagname : f,
20319                 listeners : {
20320                     click : function()
20321                     {
20322                         editorcore.insertTag(this.tagname);
20323                         editor.focus();
20324                     }
20325                 }
20326                 
20327             });
20328         });
20329          children.push(style);   
20330             
20331             
20332         btn('bold',false,true);
20333         btn('italic',false,true);
20334         btn('align-left', 'justifyleft',true);
20335         btn('align-center', 'justifycenter',true);
20336         btn('align-right' , 'justifyright',true);
20337         btn('link', false, false, function(btn) {
20338             //Roo.log("create link?");
20339             var url = prompt(this.createLinkText, this.defaultLinkValue);
20340             if(url && url != 'http:/'+'/'){
20341                 this.editorcore.relayCmd('createlink', url);
20342             }
20343         }),
20344         btn('list','insertunorderedlist',true);
20345         btn('pencil', false,true, function(btn){
20346                 Roo.log(this);
20347                 
20348                 this.toggleSourceEdit(btn.pressed);
20349         });
20350         /*
20351         var cog = {
20352                 xtype: 'Button',
20353                 size : 'sm',
20354                 xns: Roo.bootstrap,
20355                 glyphicon : 'cog',
20356                 //html : 'submit'
20357                 menu : {
20358                     xtype: 'Menu',
20359                     xns: Roo.bootstrap,
20360                     items:  []
20361                 }
20362         };
20363         
20364         cog.menu.items.push({
20365             xtype :'MenuItem',
20366             xns: Roo.bootstrap,
20367             html : Clean styles,
20368             tagname : f,
20369             listeners : {
20370                 click : function()
20371                 {
20372                     editorcore.insertTag(this.tagname);
20373                     editor.focus();
20374                 }
20375             }
20376             
20377         });
20378        */
20379         
20380          
20381        this.xtype = 'NavSimplebar';
20382         
20383         for(var i=0;i< children.length;i++) {
20384             
20385             this.buttons.add(this.addxtypeChild(children[i]));
20386             
20387         }
20388         
20389         editor.on('editorevent', this.updateToolbar, this);
20390     },
20391     onBtnClick : function(id)
20392     {
20393        this.editorcore.relayCmd(id);
20394        this.editorcore.focus();
20395     },
20396     
20397     /**
20398      * Protected method that will not generally be called directly. It triggers
20399      * a toolbar update by reading the markup state of the current selection in the editor.
20400      */
20401     updateToolbar: function(){
20402
20403         if(!this.editorcore.activated){
20404             this.editor.onFirstFocus(); // is this neeed?
20405             return;
20406         }
20407
20408         var btns = this.buttons; 
20409         var doc = this.editorcore.doc;
20410         btns.get('bold').setActive(doc.queryCommandState('bold'));
20411         btns.get('italic').setActive(doc.queryCommandState('italic'));
20412         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20413         
20414         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20415         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20416         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20417         
20418         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20419         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20420          /*
20421         
20422         var ans = this.editorcore.getAllAncestors();
20423         if (this.formatCombo) {
20424             
20425             
20426             var store = this.formatCombo.store;
20427             this.formatCombo.setValue("");
20428             for (var i =0; i < ans.length;i++) {
20429                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20430                     // select it..
20431                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20432                     break;
20433                 }
20434             }
20435         }
20436         
20437         
20438         
20439         // hides menus... - so this cant be on a menu...
20440         Roo.bootstrap.MenuMgr.hideAll();
20441         */
20442         Roo.bootstrap.MenuMgr.hideAll();
20443         //this.editorsyncValue();
20444     },
20445     onFirstFocus: function() {
20446         this.buttons.each(function(item){
20447            item.enable();
20448         });
20449     },
20450     toggleSourceEdit : function(sourceEditMode){
20451         
20452           
20453         if(sourceEditMode){
20454             Roo.log("disabling buttons");
20455            this.buttons.each( function(item){
20456                 if(item.cmd != 'pencil'){
20457                     item.disable();
20458                 }
20459             });
20460           
20461         }else{
20462             Roo.log("enabling buttons");
20463             if(this.editorcore.initialized){
20464                 this.buttons.each( function(item){
20465                     item.enable();
20466                 });
20467             }
20468             
20469         }
20470         Roo.log("calling toggole on editor");
20471         // tell the editor that it's been pressed..
20472         this.editor.toggleSourceEdit(sourceEditMode);
20473        
20474     }
20475 });
20476
20477
20478
20479
20480
20481 /**
20482  * @class Roo.bootstrap.Table.AbstractSelectionModel
20483  * @extends Roo.util.Observable
20484  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20485  * implemented by descendant classes.  This class should not be directly instantiated.
20486  * @constructor
20487  */
20488 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20489     this.locked = false;
20490     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20491 };
20492
20493
20494 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20495     /** @ignore Called by the grid automatically. Do not call directly. */
20496     init : function(grid){
20497         this.grid = grid;
20498         this.initEvents();
20499     },
20500
20501     /**
20502      * Locks the selections.
20503      */
20504     lock : function(){
20505         this.locked = true;
20506     },
20507
20508     /**
20509      * Unlocks the selections.
20510      */
20511     unlock : function(){
20512         this.locked = false;
20513     },
20514
20515     /**
20516      * Returns true if the selections are locked.
20517      * @return {Boolean}
20518      */
20519     isLocked : function(){
20520         return this.locked;
20521     }
20522 });
20523 /**
20524  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20525  * @class Roo.bootstrap.Table.RowSelectionModel
20526  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20527  * It supports multiple selections and keyboard selection/navigation. 
20528  * @constructor
20529  * @param {Object} config
20530  */
20531
20532 Roo.bootstrap.Table.RowSelectionModel = function(config){
20533     Roo.apply(this, config);
20534     this.selections = new Roo.util.MixedCollection(false, function(o){
20535         return o.id;
20536     });
20537
20538     this.last = false;
20539     this.lastActive = false;
20540
20541     this.addEvents({
20542         /**
20543              * @event selectionchange
20544              * Fires when the selection changes
20545              * @param {SelectionModel} this
20546              */
20547             "selectionchange" : true,
20548         /**
20549              * @event afterselectionchange
20550              * Fires after the selection changes (eg. by key press or clicking)
20551              * @param {SelectionModel} this
20552              */
20553             "afterselectionchange" : true,
20554         /**
20555              * @event beforerowselect
20556              * Fires when a row is selected being selected, return false to cancel.
20557              * @param {SelectionModel} this
20558              * @param {Number} rowIndex The selected index
20559              * @param {Boolean} keepExisting False if other selections will be cleared
20560              */
20561             "beforerowselect" : true,
20562         /**
20563              * @event rowselect
20564              * Fires when a row is selected.
20565              * @param {SelectionModel} this
20566              * @param {Number} rowIndex The selected index
20567              * @param {Roo.data.Record} r The record
20568              */
20569             "rowselect" : true,
20570         /**
20571              * @event rowdeselect
20572              * Fires when a row is deselected.
20573              * @param {SelectionModel} this
20574              * @param {Number} rowIndex The selected index
20575              */
20576         "rowdeselect" : true
20577     });
20578     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20579     this.locked = false;
20580 };
20581
20582 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20583     /**
20584      * @cfg {Boolean} singleSelect
20585      * True to allow selection of only one row at a time (defaults to false)
20586      */
20587     singleSelect : false,
20588
20589     // private
20590     initEvents : function(){
20591
20592         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20593             this.grid.on("mousedown", this.handleMouseDown, this);
20594         }else{ // allow click to work like normal
20595             this.grid.on("rowclick", this.handleDragableRowClick, this);
20596         }
20597
20598         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20599             "up" : function(e){
20600                 if(!e.shiftKey){
20601                     this.selectPrevious(e.shiftKey);
20602                 }else if(this.last !== false && this.lastActive !== false){
20603                     var last = this.last;
20604                     this.selectRange(this.last,  this.lastActive-1);
20605                     this.grid.getView().focusRow(this.lastActive);
20606                     if(last !== false){
20607                         this.last = last;
20608                     }
20609                 }else{
20610                     this.selectFirstRow();
20611                 }
20612                 this.fireEvent("afterselectionchange", this);
20613             },
20614             "down" : function(e){
20615                 if(!e.shiftKey){
20616                     this.selectNext(e.shiftKey);
20617                 }else if(this.last !== false && this.lastActive !== false){
20618                     var last = this.last;
20619                     this.selectRange(this.last,  this.lastActive+1);
20620                     this.grid.getView().focusRow(this.lastActive);
20621                     if(last !== false){
20622                         this.last = last;
20623                     }
20624                 }else{
20625                     this.selectFirstRow();
20626                 }
20627                 this.fireEvent("afterselectionchange", this);
20628             },
20629             scope: this
20630         });
20631
20632         var view = this.grid.view;
20633         view.on("refresh", this.onRefresh, this);
20634         view.on("rowupdated", this.onRowUpdated, this);
20635         view.on("rowremoved", this.onRemove, this);
20636     },
20637
20638     // private
20639     onRefresh : function(){
20640         var ds = this.grid.dataSource, i, v = this.grid.view;
20641         var s = this.selections;
20642         s.each(function(r){
20643             if((i = ds.indexOfId(r.id)) != -1){
20644                 v.onRowSelect(i);
20645             }else{
20646                 s.remove(r);
20647             }
20648         });
20649     },
20650
20651     // private
20652     onRemove : function(v, index, r){
20653         this.selections.remove(r);
20654     },
20655
20656     // private
20657     onRowUpdated : function(v, index, r){
20658         if(this.isSelected(r)){
20659             v.onRowSelect(index);
20660         }
20661     },
20662
20663     /**
20664      * Select records.
20665      * @param {Array} records The records to select
20666      * @param {Boolean} keepExisting (optional) True to keep existing selections
20667      */
20668     selectRecords : function(records, keepExisting){
20669         if(!keepExisting){
20670             this.clearSelections();
20671         }
20672         var ds = this.grid.dataSource;
20673         for(var i = 0, len = records.length; i < len; i++){
20674             this.selectRow(ds.indexOf(records[i]), true);
20675         }
20676     },
20677
20678     /**
20679      * Gets the number of selected rows.
20680      * @return {Number}
20681      */
20682     getCount : function(){
20683         return this.selections.length;
20684     },
20685
20686     /**
20687      * Selects the first row in the grid.
20688      */
20689     selectFirstRow : function(){
20690         this.selectRow(0);
20691     },
20692
20693     /**
20694      * Select the last row.
20695      * @param {Boolean} keepExisting (optional) True to keep existing selections
20696      */
20697     selectLastRow : function(keepExisting){
20698         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20699     },
20700
20701     /**
20702      * Selects the row immediately following the last selected row.
20703      * @param {Boolean} keepExisting (optional) True to keep existing selections
20704      */
20705     selectNext : function(keepExisting){
20706         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20707             this.selectRow(this.last+1, keepExisting);
20708             this.grid.getView().focusRow(this.last);
20709         }
20710     },
20711
20712     /**
20713      * Selects the row that precedes the last selected row.
20714      * @param {Boolean} keepExisting (optional) True to keep existing selections
20715      */
20716     selectPrevious : function(keepExisting){
20717         if(this.last){
20718             this.selectRow(this.last-1, keepExisting);
20719             this.grid.getView().focusRow(this.last);
20720         }
20721     },
20722
20723     /**
20724      * Returns the selected records
20725      * @return {Array} Array of selected records
20726      */
20727     getSelections : function(){
20728         return [].concat(this.selections.items);
20729     },
20730
20731     /**
20732      * Returns the first selected record.
20733      * @return {Record}
20734      */
20735     getSelected : function(){
20736         return this.selections.itemAt(0);
20737     },
20738
20739
20740     /**
20741      * Clears all selections.
20742      */
20743     clearSelections : function(fast){
20744         if(this.locked) return;
20745         if(fast !== true){
20746             var ds = this.grid.dataSource;
20747             var s = this.selections;
20748             s.each(function(r){
20749                 this.deselectRow(ds.indexOfId(r.id));
20750             }, this);
20751             s.clear();
20752         }else{
20753             this.selections.clear();
20754         }
20755         this.last = false;
20756     },
20757
20758
20759     /**
20760      * Selects all rows.
20761      */
20762     selectAll : function(){
20763         if(this.locked) return;
20764         this.selections.clear();
20765         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20766             this.selectRow(i, true);
20767         }
20768     },
20769
20770     /**
20771      * Returns True if there is a selection.
20772      * @return {Boolean}
20773      */
20774     hasSelection : function(){
20775         return this.selections.length > 0;
20776     },
20777
20778     /**
20779      * Returns True if the specified row is selected.
20780      * @param {Number/Record} record The record or index of the record to check
20781      * @return {Boolean}
20782      */
20783     isSelected : function(index){
20784         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20785         return (r && this.selections.key(r.id) ? true : false);
20786     },
20787
20788     /**
20789      * Returns True if the specified record id is selected.
20790      * @param {String} id The id of record to check
20791      * @return {Boolean}
20792      */
20793     isIdSelected : function(id){
20794         return (this.selections.key(id) ? true : false);
20795     },
20796
20797     // private
20798     handleMouseDown : function(e, t){
20799         var view = this.grid.getView(), rowIndex;
20800         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20801             return;
20802         };
20803         if(e.shiftKey && this.last !== false){
20804             var last = this.last;
20805             this.selectRange(last, rowIndex, e.ctrlKey);
20806             this.last = last; // reset the last
20807             view.focusRow(rowIndex);
20808         }else{
20809             var isSelected = this.isSelected(rowIndex);
20810             if(e.button !== 0 && isSelected){
20811                 view.focusRow(rowIndex);
20812             }else if(e.ctrlKey && isSelected){
20813                 this.deselectRow(rowIndex);
20814             }else if(!isSelected){
20815                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20816                 view.focusRow(rowIndex);
20817             }
20818         }
20819         this.fireEvent("afterselectionchange", this);
20820     },
20821     // private
20822     handleDragableRowClick :  function(grid, rowIndex, e) 
20823     {
20824         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20825             this.selectRow(rowIndex, false);
20826             grid.view.focusRow(rowIndex);
20827              this.fireEvent("afterselectionchange", this);
20828         }
20829     },
20830     
20831     /**
20832      * Selects multiple rows.
20833      * @param {Array} rows Array of the indexes of the row to select
20834      * @param {Boolean} keepExisting (optional) True to keep existing selections
20835      */
20836     selectRows : function(rows, keepExisting){
20837         if(!keepExisting){
20838             this.clearSelections();
20839         }
20840         for(var i = 0, len = rows.length; i < len; i++){
20841             this.selectRow(rows[i], true);
20842         }
20843     },
20844
20845     /**
20846      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20847      * @param {Number} startRow The index of the first row in the range
20848      * @param {Number} endRow The index of the last row in the range
20849      * @param {Boolean} keepExisting (optional) True to retain existing selections
20850      */
20851     selectRange : function(startRow, endRow, keepExisting){
20852         if(this.locked) return;
20853         if(!keepExisting){
20854             this.clearSelections();
20855         }
20856         if(startRow <= endRow){
20857             for(var i = startRow; i <= endRow; i++){
20858                 this.selectRow(i, true);
20859             }
20860         }else{
20861             for(var i = startRow; i >= endRow; i--){
20862                 this.selectRow(i, true);
20863             }
20864         }
20865     },
20866
20867     /**
20868      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20869      * @param {Number} startRow The index of the first row in the range
20870      * @param {Number} endRow The index of the last row in the range
20871      */
20872     deselectRange : function(startRow, endRow, preventViewNotify){
20873         if(this.locked) return;
20874         for(var i = startRow; i <= endRow; i++){
20875             this.deselectRow(i, preventViewNotify);
20876         }
20877     },
20878
20879     /**
20880      * Selects a row.
20881      * @param {Number} row The index of the row to select
20882      * @param {Boolean} keepExisting (optional) True to keep existing selections
20883      */
20884     selectRow : function(index, keepExisting, preventViewNotify){
20885         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20886         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20887             if(!keepExisting || this.singleSelect){
20888                 this.clearSelections();
20889             }
20890             var r = this.grid.dataSource.getAt(index);
20891             this.selections.add(r);
20892             this.last = this.lastActive = index;
20893             if(!preventViewNotify){
20894                 this.grid.getView().onRowSelect(index);
20895             }
20896             this.fireEvent("rowselect", this, index, r);
20897             this.fireEvent("selectionchange", this);
20898         }
20899     },
20900
20901     /**
20902      * Deselects a row.
20903      * @param {Number} row The index of the row to deselect
20904      */
20905     deselectRow : function(index, preventViewNotify){
20906         if(this.locked) return;
20907         if(this.last == index){
20908             this.last = false;
20909         }
20910         if(this.lastActive == index){
20911             this.lastActive = false;
20912         }
20913         var r = this.grid.dataSource.getAt(index);
20914         this.selections.remove(r);
20915         if(!preventViewNotify){
20916             this.grid.getView().onRowDeselect(index);
20917         }
20918         this.fireEvent("rowdeselect", this, index);
20919         this.fireEvent("selectionchange", this);
20920     },
20921
20922     // private
20923     restoreLast : function(){
20924         if(this._last){
20925             this.last = this._last;
20926         }
20927     },
20928
20929     // private
20930     acceptsNav : function(row, col, cm){
20931         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20932     },
20933
20934     // private
20935     onEditorKey : function(field, e){
20936         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20937         if(k == e.TAB){
20938             e.stopEvent();
20939             ed.completeEdit();
20940             if(e.shiftKey){
20941                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20942             }else{
20943                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20944             }
20945         }else if(k == e.ENTER && !e.ctrlKey){
20946             e.stopEvent();
20947             ed.completeEdit();
20948             if(e.shiftKey){
20949                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20950             }else{
20951                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20952             }
20953         }else if(k == e.ESC){
20954             ed.cancelEdit();
20955         }
20956         if(newCell){
20957             g.startEditing(newCell[0], newCell[1]);
20958         }
20959     }
20960 });/*
20961  * Based on:
20962  * Ext JS Library 1.1.1
20963  * Copyright(c) 2006-2007, Ext JS, LLC.
20964  *
20965  * Originally Released Under LGPL - original licence link has changed is not relivant.
20966  *
20967  * Fork - LGPL
20968  * <script type="text/javascript">
20969  */
20970  
20971 /**
20972  * @class Roo.bootstrap.PagingToolbar
20973  * @extends Roo.Row
20974  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20975  * @constructor
20976  * Create a new PagingToolbar
20977  * @param {Object} config The config object
20978  */
20979 Roo.bootstrap.PagingToolbar = function(config)
20980 {
20981     // old args format still supported... - xtype is prefered..
20982         // created from xtype...
20983     var ds = config.dataSource;
20984     this.toolbarItems = [];
20985     if (config.items) {
20986         this.toolbarItems = config.items;
20987 //        config.items = [];
20988     }
20989     
20990     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20991     this.ds = ds;
20992     this.cursor = 0;
20993     if (ds) { 
20994         this.bind(ds);
20995     }
20996     
20997     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20998     
20999 };
21000
21001 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21002     /**
21003      * @cfg {Roo.data.Store} dataSource
21004      * The underlying data store providing the paged data
21005      */
21006     /**
21007      * @cfg {String/HTMLElement/Element} container
21008      * container The id or element that will contain the toolbar
21009      */
21010     /**
21011      * @cfg {Boolean} displayInfo
21012      * True to display the displayMsg (defaults to false)
21013      */
21014     /**
21015      * @cfg {Number} pageSize
21016      * The number of records to display per page (defaults to 20)
21017      */
21018     pageSize: 20,
21019     /**
21020      * @cfg {String} displayMsg
21021      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21022      */
21023     displayMsg : 'Displaying {0} - {1} of {2}',
21024     /**
21025      * @cfg {String} emptyMsg
21026      * The message to display when no records are found (defaults to "No data to display")
21027      */
21028     emptyMsg : 'No data to display',
21029     /**
21030      * Customizable piece of the default paging text (defaults to "Page")
21031      * @type String
21032      */
21033     beforePageText : "Page",
21034     /**
21035      * Customizable piece of the default paging text (defaults to "of %0")
21036      * @type String
21037      */
21038     afterPageText : "of {0}",
21039     /**
21040      * Customizable piece of the default paging text (defaults to "First Page")
21041      * @type String
21042      */
21043     firstText : "First Page",
21044     /**
21045      * Customizable piece of the default paging text (defaults to "Previous Page")
21046      * @type String
21047      */
21048     prevText : "Previous Page",
21049     /**
21050      * Customizable piece of the default paging text (defaults to "Next Page")
21051      * @type String
21052      */
21053     nextText : "Next Page",
21054     /**
21055      * Customizable piece of the default paging text (defaults to "Last Page")
21056      * @type String
21057      */
21058     lastText : "Last Page",
21059     /**
21060      * Customizable piece of the default paging text (defaults to "Refresh")
21061      * @type String
21062      */
21063     refreshText : "Refresh",
21064
21065     buttons : false,
21066     // private
21067     onRender : function(ct, position) 
21068     {
21069         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21070         this.navgroup.parentId = this.id;
21071         this.navgroup.onRender(this.el, null);
21072         // add the buttons to the navgroup
21073         
21074         if(this.displayInfo){
21075             Roo.log(this.el.select('ul.navbar-nav',true).first());
21076             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21077             this.displayEl = this.el.select('.x-paging-info', true).first();
21078 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21079 //            this.displayEl = navel.el.select('span',true).first();
21080         }
21081         
21082         var _this = this;
21083         
21084         if(this.buttons){
21085             Roo.each(_this.buttons, function(e){
21086                Roo.factory(e).onRender(_this.el, null);
21087             });
21088         }
21089             
21090         Roo.each(_this.toolbarItems, function(e) {
21091             _this.navgroup.addItem(e);
21092         });
21093         
21094         
21095         this.first = this.navgroup.addItem({
21096             tooltip: this.firstText,
21097             cls: "prev",
21098             icon : 'fa fa-backward',
21099             disabled: true,
21100             preventDefault: true,
21101             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21102         });
21103         
21104         this.prev =  this.navgroup.addItem({
21105             tooltip: this.prevText,
21106             cls: "prev",
21107             icon : 'fa fa-step-backward',
21108             disabled: true,
21109             preventDefault: true,
21110             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21111         });
21112     //this.addSeparator();
21113         
21114         
21115         var field = this.navgroup.addItem( {
21116             tagtype : 'span',
21117             cls : 'x-paging-position',
21118             
21119             html : this.beforePageText  +
21120                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21121                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21122          } ); //?? escaped?
21123         
21124         this.field = field.el.select('input', true).first();
21125         this.field.on("keydown", this.onPagingKeydown, this);
21126         this.field.on("focus", function(){this.dom.select();});
21127     
21128     
21129         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21130         //this.field.setHeight(18);
21131         //this.addSeparator();
21132         this.next = this.navgroup.addItem({
21133             tooltip: this.nextText,
21134             cls: "next",
21135             html : ' <i class="fa fa-step-forward">',
21136             disabled: true,
21137             preventDefault: true,
21138             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21139         });
21140         this.last = this.navgroup.addItem({
21141             tooltip: this.lastText,
21142             icon : 'fa fa-forward',
21143             cls: "next",
21144             disabled: true,
21145             preventDefault: true,
21146             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21147         });
21148     //this.addSeparator();
21149         this.loading = this.navgroup.addItem({
21150             tooltip: this.refreshText,
21151             icon: 'fa fa-refresh',
21152             preventDefault: true,
21153             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21154         });
21155
21156     },
21157
21158     // private
21159     updateInfo : function(){
21160         if(this.displayEl){
21161             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21162             var msg = count == 0 ?
21163                 this.emptyMsg :
21164                 String.format(
21165                     this.displayMsg,
21166                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21167                 );
21168             this.displayEl.update(msg);
21169         }
21170     },
21171
21172     // private
21173     onLoad : function(ds, r, o){
21174        this.cursor = o.params ? o.params.start : 0;
21175        var d = this.getPageData(),
21176             ap = d.activePage,
21177             ps = d.pages;
21178         
21179        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21180        this.field.dom.value = ap;
21181        this.first.setDisabled(ap == 1);
21182        this.prev.setDisabled(ap == 1);
21183        this.next.setDisabled(ap == ps);
21184        this.last.setDisabled(ap == ps);
21185        this.loading.enable();
21186        this.updateInfo();
21187     },
21188
21189     // private
21190     getPageData : function(){
21191         var total = this.ds.getTotalCount();
21192         return {
21193             total : total,
21194             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21195             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21196         };
21197     },
21198
21199     // private
21200     onLoadError : function(){
21201         this.loading.enable();
21202     },
21203
21204     // private
21205     onPagingKeydown : function(e){
21206         var k = e.getKey();
21207         var d = this.getPageData();
21208         if(k == e.RETURN){
21209             var v = this.field.dom.value, pageNum;
21210             if(!v || isNaN(pageNum = parseInt(v, 10))){
21211                 this.field.dom.value = d.activePage;
21212                 return;
21213             }
21214             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21215             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21216             e.stopEvent();
21217         }
21218         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))
21219         {
21220           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21221           this.field.dom.value = pageNum;
21222           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21223           e.stopEvent();
21224         }
21225         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21226         {
21227           var v = this.field.dom.value, pageNum; 
21228           var increment = (e.shiftKey) ? 10 : 1;
21229           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21230             increment *= -1;
21231           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21232             this.field.dom.value = d.activePage;
21233             return;
21234           }
21235           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21236           {
21237             this.field.dom.value = parseInt(v, 10) + increment;
21238             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21239             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21240           }
21241           e.stopEvent();
21242         }
21243     },
21244
21245     // private
21246     beforeLoad : function(){
21247         if(this.loading){
21248             this.loading.disable();
21249         }
21250     },
21251
21252     // private
21253     onClick : function(which){
21254         
21255         var ds = this.ds;
21256         if (!ds) {
21257             return;
21258         }
21259         
21260         switch(which){
21261             case "first":
21262                 ds.load({params:{start: 0, limit: this.pageSize}});
21263             break;
21264             case "prev":
21265                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21266             break;
21267             case "next":
21268                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21269             break;
21270             case "last":
21271                 var total = ds.getTotalCount();
21272                 var extra = total % this.pageSize;
21273                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21274                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21275             break;
21276             case "refresh":
21277                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21278             break;
21279         }
21280     },
21281
21282     /**
21283      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21284      * @param {Roo.data.Store} store The data store to unbind
21285      */
21286     unbind : function(ds){
21287         ds.un("beforeload", this.beforeLoad, this);
21288         ds.un("load", this.onLoad, this);
21289         ds.un("loadexception", this.onLoadError, this);
21290         ds.un("remove", this.updateInfo, this);
21291         ds.un("add", this.updateInfo, this);
21292         this.ds = undefined;
21293     },
21294
21295     /**
21296      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21297      * @param {Roo.data.Store} store The data store to bind
21298      */
21299     bind : function(ds){
21300         ds.on("beforeload", this.beforeLoad, this);
21301         ds.on("load", this.onLoad, this);
21302         ds.on("loadexception", this.onLoadError, this);
21303         ds.on("remove", this.updateInfo, this);
21304         ds.on("add", this.updateInfo, this);
21305         this.ds = ds;
21306     }
21307 });/*
21308  * - LGPL
21309  *
21310  * element
21311  * 
21312  */
21313
21314 /**
21315  * @class Roo.bootstrap.MessageBar
21316  * @extends Roo.bootstrap.Component
21317  * Bootstrap MessageBar class
21318  * @cfg {String} html contents of the MessageBar
21319  * @cfg {String} weight (info | success | warning | danger) default info
21320  * @cfg {String} beforeClass insert the bar before the given class
21321  * @cfg {Boolean} closable (true | false) default false
21322  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21323  * 
21324  * @constructor
21325  * Create a new Element
21326  * @param {Object} config The config object
21327  */
21328
21329 Roo.bootstrap.MessageBar = function(config){
21330     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21331 };
21332
21333 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21334     
21335     html: '',
21336     weight: 'info',
21337     closable: false,
21338     fixed: false,
21339     beforeClass: 'bootstrap-sticky-wrap',
21340     
21341     getAutoCreate : function(){
21342         
21343         var cfg = {
21344             tag: 'div',
21345             cls: 'alert alert-dismissable alert-' + this.weight,
21346             cn: [
21347                 {
21348                     tag: 'span',
21349                     cls: 'message',
21350                     html: this.html || ''
21351                 }
21352             ]
21353         }
21354         
21355         if(this.fixed){
21356             cfg.cls += ' alert-messages-fixed';
21357         }
21358         
21359         if(this.closable){
21360             cfg.cn.push({
21361                 tag: 'button',
21362                 cls: 'close',
21363                 html: 'x'
21364             });
21365         }
21366         
21367         return cfg;
21368     },
21369     
21370     onRender : function(ct, position)
21371     {
21372         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21373         
21374         if(!this.el){
21375             var cfg = Roo.apply({},  this.getAutoCreate());
21376             cfg.id = Roo.id();
21377             
21378             if (this.cls) {
21379                 cfg.cls += ' ' + this.cls;
21380             }
21381             if (this.style) {
21382                 cfg.style = this.style;
21383             }
21384             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21385             
21386             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21387         }
21388         
21389         this.el.select('>button.close').on('click', this.hide, this);
21390         
21391     },
21392     
21393     show : function()
21394     {
21395         if (!this.rendered) {
21396             this.render();
21397         }
21398         
21399         this.el.show();
21400         
21401         this.fireEvent('show', this);
21402         
21403     },
21404     
21405     hide : function()
21406     {
21407         if (!this.rendered) {
21408             this.render();
21409         }
21410         
21411         this.el.hide();
21412         
21413         this.fireEvent('hide', this);
21414     },
21415     
21416     update : function()
21417     {
21418 //        var e = this.el.dom.firstChild;
21419 //        
21420 //        if(this.closable){
21421 //            e = e.nextSibling;
21422 //        }
21423 //        
21424 //        e.data = this.html || '';
21425
21426         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21427     }
21428    
21429 });
21430
21431  
21432
21433      /*
21434  * - LGPL
21435  *
21436  * Graph
21437  * 
21438  */
21439
21440
21441 /**
21442  * @class Roo.bootstrap.Graph
21443  * @extends Roo.bootstrap.Component
21444  * Bootstrap Graph class
21445 > Prameters
21446  -sm {number} sm 4
21447  -md {number} md 5
21448  @cfg {String} graphtype  bar | vbar | pie
21449  @cfg {number} g_x coodinator | centre x (pie)
21450  @cfg {number} g_y coodinator | centre y (pie)
21451  @cfg {number} g_r radius (pie)
21452  @cfg {number} g_height height of the chart (respected by all elements in the set)
21453  @cfg {number} g_width width of the chart (respected by all elements in the set)
21454  @cfg {Object} title The title of the chart
21455     
21456  -{Array}  values
21457  -opts (object) options for the chart 
21458      o {
21459      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21460      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21461      o vgutter (number)
21462      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.
21463      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21464      o to
21465      o stretch (boolean)
21466      o }
21467  -opts (object) options for the pie
21468      o{
21469      o cut
21470      o startAngle (number)
21471      o endAngle (number)
21472      } 
21473  *
21474  * @constructor
21475  * Create a new Input
21476  * @param {Object} config The config object
21477  */
21478
21479 Roo.bootstrap.Graph = function(config){
21480     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21481     
21482     this.addEvents({
21483         // img events
21484         /**
21485          * @event click
21486          * The img click event for the img.
21487          * @param {Roo.EventObject} e
21488          */
21489         "click" : true
21490     });
21491 };
21492
21493 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21494     
21495     sm: 4,
21496     md: 5,
21497     graphtype: 'bar',
21498     g_height: 250,
21499     g_width: 400,
21500     g_x: 50,
21501     g_y: 50,
21502     g_r: 30,
21503     opts:{
21504         //g_colors: this.colors,
21505         g_type: 'soft',
21506         g_gutter: '20%'
21507
21508     },
21509     title : false,
21510
21511     getAutoCreate : function(){
21512         
21513         var cfg = {
21514             tag: 'div',
21515             html : null
21516         }
21517         
21518         
21519         return  cfg;
21520     },
21521
21522     onRender : function(ct,position){
21523         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21524         this.raphael = Raphael(this.el.dom);
21525         
21526                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21527                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21528                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21529                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21530                 /*
21531                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21532                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21533                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21534                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21535                 
21536                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21537                 r.barchart(330, 10, 300, 220, data1);
21538                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21539                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21540                 */
21541                 
21542                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21543                 // r.barchart(30, 30, 560, 250,  xdata, {
21544                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21545                 //     axis : "0 0 1 1",
21546                 //     axisxlabels :  xdata
21547                 //     //yvalues : cols,
21548                    
21549                 // });
21550 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21551 //        
21552 //        this.load(null,xdata,{
21553 //                axis : "0 0 1 1",
21554 //                axisxlabels :  xdata
21555 //                });
21556
21557     },
21558
21559     load : function(graphtype,xdata,opts){
21560         this.raphael.clear();
21561         if(!graphtype) {
21562             graphtype = this.graphtype;
21563         }
21564         if(!opts){
21565             opts = this.opts;
21566         }
21567         var r = this.raphael,
21568             fin = function () {
21569                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21570             },
21571             fout = function () {
21572                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21573             },
21574             pfin = function() {
21575                 this.sector.stop();
21576                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21577
21578                 if (this.label) {
21579                     this.label[0].stop();
21580                     this.label[0].attr({ r: 7.5 });
21581                     this.label[1].attr({ "font-weight": 800 });
21582                 }
21583             },
21584             pfout = function() {
21585                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21586
21587                 if (this.label) {
21588                     this.label[0].animate({ r: 5 }, 500, "bounce");
21589                     this.label[1].attr({ "font-weight": 400 });
21590                 }
21591             };
21592
21593         switch(graphtype){
21594             case 'bar':
21595                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21596                 break;
21597             case 'hbar':
21598                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21599                 break;
21600             case 'pie':
21601 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21602 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21603 //            
21604                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21605                 
21606                 break;
21607
21608         }
21609         
21610         if(this.title){
21611             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21612         }
21613         
21614     },
21615     
21616     setTitle: function(o)
21617     {
21618         this.title = o;
21619     },
21620     
21621     initEvents: function() {
21622         
21623         if(!this.href){
21624             this.el.on('click', this.onClick, this);
21625         }
21626     },
21627     
21628     onClick : function(e)
21629     {
21630         Roo.log('img onclick');
21631         this.fireEvent('click', this, e);
21632     }
21633    
21634 });
21635
21636  
21637 /*
21638  * - LGPL
21639  *
21640  * numberBox
21641  * 
21642  */
21643 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21644
21645 /**
21646  * @class Roo.bootstrap.dash.NumberBox
21647  * @extends Roo.bootstrap.Component
21648  * Bootstrap NumberBox class
21649  * @cfg {String} headline Box headline
21650  * @cfg {String} content Box content
21651  * @cfg {String} icon Box icon
21652  * @cfg {String} footer Footer text
21653  * @cfg {String} fhref Footer href
21654  * 
21655  * @constructor
21656  * Create a new NumberBox
21657  * @param {Object} config The config object
21658  */
21659
21660
21661 Roo.bootstrap.dash.NumberBox = function(config){
21662     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21663     
21664 };
21665
21666 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21667     
21668     headline : '',
21669     content : '',
21670     icon : '',
21671     footer : '',
21672     fhref : '',
21673     ficon : '',
21674     
21675     getAutoCreate : function(){
21676         
21677         var cfg = {
21678             tag : 'div',
21679             cls : 'small-box ',
21680             cn : [
21681                 {
21682                     tag : 'div',
21683                     cls : 'inner',
21684                     cn :[
21685                         {
21686                             tag : 'h3',
21687                             cls : 'roo-headline',
21688                             html : this.headline
21689                         },
21690                         {
21691                             tag : 'p',
21692                             cls : 'roo-content',
21693                             html : this.content
21694                         }
21695                     ]
21696                 }
21697             ]
21698         }
21699         
21700         if(this.icon){
21701             cfg.cn.push({
21702                 tag : 'div',
21703                 cls : 'icon',
21704                 cn :[
21705                     {
21706                         tag : 'i',
21707                         cls : 'ion ' + this.icon
21708                     }
21709                 ]
21710             });
21711         }
21712         
21713         if(this.footer){
21714             var footer = {
21715                 tag : 'a',
21716                 cls : 'small-box-footer',
21717                 href : this.fhref || '#',
21718                 html : this.footer
21719             };
21720             
21721             cfg.cn.push(footer);
21722             
21723         }
21724         
21725         return  cfg;
21726     },
21727
21728     onRender : function(ct,position){
21729         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21730
21731
21732        
21733                 
21734     },
21735
21736     setHeadline: function (value)
21737     {
21738         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21739     },
21740     
21741     setFooter: function (value, href)
21742     {
21743         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21744         
21745         if(href){
21746             this.el.select('a.small-box-footer',true).first().attr('href', href);
21747         }
21748         
21749     },
21750
21751     setContent: function (value)
21752     {
21753         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21754     },
21755
21756     initEvents: function() 
21757     {   
21758         
21759     }
21760     
21761 });
21762
21763  
21764 /*
21765  * - LGPL
21766  *
21767  * TabBox
21768  * 
21769  */
21770 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21771
21772 /**
21773  * @class Roo.bootstrap.dash.TabBox
21774  * @extends Roo.bootstrap.Component
21775  * Bootstrap TabBox class
21776  * @cfg {String} title Title of the TabBox
21777  * @cfg {String} icon Icon of the TabBox
21778  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21779  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21780  * 
21781  * @constructor
21782  * Create a new TabBox
21783  * @param {Object} config The config object
21784  */
21785
21786
21787 Roo.bootstrap.dash.TabBox = function(config){
21788     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21789     this.addEvents({
21790         // raw events
21791         /**
21792          * @event addpane
21793          * When a pane is added
21794          * @param {Roo.bootstrap.dash.TabPane} pane
21795          */
21796         "addpane" : true,
21797         /**
21798          * @event activatepane
21799          * When a pane is activated
21800          * @param {Roo.bootstrap.dash.TabPane} pane
21801          */
21802         "activatepane" : true
21803         
21804          
21805     });
21806     
21807     this.panes = [];
21808 };
21809
21810 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21811
21812     title : '',
21813     icon : false,
21814     showtabs : true,
21815     tabScrollable : false,
21816     
21817     getChildContainer : function()
21818     {
21819         return this.el.select('.tab-content', true).first();
21820     },
21821     
21822     getAutoCreate : function(){
21823         
21824         var header = {
21825             tag: 'li',
21826             cls: 'pull-left header',
21827             html: this.title,
21828             cn : []
21829         };
21830         
21831         if(this.icon){
21832             header.cn.push({
21833                 tag: 'i',
21834                 cls: 'fa ' + this.icon
21835             });
21836         }
21837         
21838         var h = {
21839             tag: 'ul',
21840             cls: 'nav nav-tabs pull-right',
21841             cn: [
21842                 header
21843             ]
21844         };
21845         
21846         if(this.tabScrollable){
21847             h = {
21848                 tag: 'div',
21849                 cls: 'tab-header',
21850                 cn: [
21851                     {
21852                         tag: 'ul',
21853                         cls: 'nav nav-tabs pull-right',
21854                         cn: [
21855                             header
21856                         ]
21857                     }
21858                 ]
21859             }
21860         }
21861         
21862         var cfg = {
21863             tag: 'div',
21864             cls: 'nav-tabs-custom',
21865             cn: [
21866                 h,
21867                 {
21868                     tag: 'div',
21869                     cls: 'tab-content no-padding',
21870                     cn: []
21871                 }
21872             ]
21873         }
21874
21875         return  cfg;
21876     },
21877     initEvents : function()
21878     {
21879         //Roo.log('add add pane handler');
21880         this.on('addpane', this.onAddPane, this);
21881     },
21882      /**
21883      * Updates the box title
21884      * @param {String} html to set the title to.
21885      */
21886     setTitle : function(value)
21887     {
21888         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21889     },
21890     onAddPane : function(pane)
21891     {
21892         this.panes.push(pane);
21893         //Roo.log('addpane');
21894         //Roo.log(pane);
21895         // tabs are rendere left to right..
21896         if(!this.showtabs){
21897             return;
21898         }
21899         
21900         var ctr = this.el.select('.nav-tabs', true).first();
21901          
21902          
21903         var existing = ctr.select('.nav-tab',true);
21904         var qty = existing.getCount();;
21905         
21906         
21907         var tab = ctr.createChild({
21908             tag : 'li',
21909             cls : 'nav-tab' + (qty ? '' : ' active'),
21910             cn : [
21911                 {
21912                     tag : 'a',
21913                     href:'#',
21914                     html : pane.title
21915                 }
21916             ]
21917         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21918         pane.tab = tab;
21919         
21920         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21921         if (!qty) {
21922             pane.el.addClass('active');
21923         }
21924         
21925                 
21926     },
21927     onTabClick : function(ev,un,ob,pane)
21928     {
21929         //Roo.log('tab - prev default');
21930         ev.preventDefault();
21931         
21932         
21933         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21934         pane.tab.addClass('active');
21935         //Roo.log(pane.title);
21936         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21937         // technically we should have a deactivate event.. but maybe add later.
21938         // and it should not de-activate the selected tab...
21939         this.fireEvent('activatepane', pane);
21940         pane.el.addClass('active');
21941         pane.fireEvent('activate');
21942         
21943         
21944     },
21945     
21946     getActivePane : function()
21947     {
21948         var r = false;
21949         Roo.each(this.panes, function(p) {
21950             if(p.el.hasClass('active')){
21951                 r = p;
21952                 return false;
21953             }
21954             
21955             return;
21956         });
21957         
21958         return r;
21959     }
21960     
21961     
21962 });
21963
21964  
21965 /*
21966  * - LGPL
21967  *
21968  * Tab pane
21969  * 
21970  */
21971 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21972 /**
21973  * @class Roo.bootstrap.TabPane
21974  * @extends Roo.bootstrap.Component
21975  * Bootstrap TabPane class
21976  * @cfg {Boolean} active (false | true) Default false
21977  * @cfg {String} title title of panel
21978
21979  * 
21980  * @constructor
21981  * Create a new TabPane
21982  * @param {Object} config The config object
21983  */
21984
21985 Roo.bootstrap.dash.TabPane = function(config){
21986     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21987     
21988     this.addEvents({
21989         // raw events
21990         /**
21991          * @event activate
21992          * When a pane is activated
21993          * @param {Roo.bootstrap.dash.TabPane} pane
21994          */
21995         "activate" : true
21996          
21997     });
21998 };
21999
22000 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22001     
22002     active : false,
22003     title : '',
22004     
22005     // the tabBox that this is attached to.
22006     tab : false,
22007      
22008     getAutoCreate : function() 
22009     {
22010         var cfg = {
22011             tag: 'div',
22012             cls: 'tab-pane'
22013         }
22014         
22015         if(this.active){
22016             cfg.cls += ' active';
22017         }
22018         
22019         return cfg;
22020     },
22021     initEvents  : function()
22022     {
22023         //Roo.log('trigger add pane handler');
22024         this.parent().fireEvent('addpane', this)
22025     },
22026     
22027      /**
22028      * Updates the tab title 
22029      * @param {String} html to set the title to.
22030      */
22031     setTitle: function(str)
22032     {
22033         if (!this.tab) {
22034             return;
22035         }
22036         this.title = str;
22037         this.tab.select('a', true).first().dom.innerHTML = str;
22038         
22039     }
22040     
22041     
22042     
22043 });
22044
22045  
22046
22047
22048  /*
22049  * - LGPL
22050  *
22051  * menu
22052  * 
22053  */
22054 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22055
22056 /**
22057  * @class Roo.bootstrap.menu.Menu
22058  * @extends Roo.bootstrap.Component
22059  * Bootstrap Menu class - container for Menu
22060  * @cfg {String} html Text of the menu
22061  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22062  * @cfg {String} icon Font awesome icon
22063  * @cfg {String} pos Menu align to (top | bottom) default bottom
22064  * 
22065  * 
22066  * @constructor
22067  * Create a new Menu
22068  * @param {Object} config The config object
22069  */
22070
22071
22072 Roo.bootstrap.menu.Menu = function(config){
22073     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22074     
22075     this.addEvents({
22076         /**
22077          * @event beforeshow
22078          * Fires before this menu is displayed
22079          * @param {Roo.bootstrap.menu.Menu} this
22080          */
22081         beforeshow : true,
22082         /**
22083          * @event beforehide
22084          * Fires before this menu is hidden
22085          * @param {Roo.bootstrap.menu.Menu} this
22086          */
22087         beforehide : true,
22088         /**
22089          * @event show
22090          * Fires after this menu is displayed
22091          * @param {Roo.bootstrap.menu.Menu} this
22092          */
22093         show : true,
22094         /**
22095          * @event hide
22096          * Fires after this menu is hidden
22097          * @param {Roo.bootstrap.menu.Menu} this
22098          */
22099         hide : true,
22100         /**
22101          * @event click
22102          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22103          * @param {Roo.bootstrap.menu.Menu} this
22104          * @param {Roo.EventObject} e
22105          */
22106         click : true
22107     });
22108     
22109 };
22110
22111 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22112     
22113     submenu : false,
22114     html : '',
22115     weight : 'default',
22116     icon : false,
22117     pos : 'bottom',
22118     
22119     
22120     getChildContainer : function() {
22121         if(this.isSubMenu){
22122             return this.el;
22123         }
22124         
22125         return this.el.select('ul.dropdown-menu', true).first();  
22126     },
22127     
22128     getAutoCreate : function()
22129     {
22130         var text = [
22131             {
22132                 tag : 'span',
22133                 cls : 'roo-menu-text',
22134                 html : this.html
22135             }
22136         ];
22137         
22138         if(this.icon){
22139             text.unshift({
22140                 tag : 'i',
22141                 cls : 'fa ' + this.icon
22142             })
22143         }
22144         
22145         
22146         var cfg = {
22147             tag : 'div',
22148             cls : 'btn-group',
22149             cn : [
22150                 {
22151                     tag : 'button',
22152                     cls : 'dropdown-button btn btn-' + this.weight,
22153                     cn : text
22154                 },
22155                 {
22156                     tag : 'button',
22157                     cls : 'dropdown-toggle btn btn-' + this.weight,
22158                     cn : [
22159                         {
22160                             tag : 'span',
22161                             cls : 'caret'
22162                         }
22163                     ]
22164                 },
22165                 {
22166                     tag : 'ul',
22167                     cls : 'dropdown-menu'
22168                 }
22169             ]
22170             
22171         };
22172         
22173         if(this.pos == 'top'){
22174             cfg.cls += ' dropup';
22175         }
22176         
22177         if(this.isSubMenu){
22178             cfg = {
22179                 tag : 'ul',
22180                 cls : 'dropdown-menu'
22181             }
22182         }
22183         
22184         return cfg;
22185     },
22186     
22187     onRender : function(ct, position)
22188     {
22189         this.isSubMenu = ct.hasClass('dropdown-submenu');
22190         
22191         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22192     },
22193     
22194     initEvents : function() 
22195     {
22196         if(this.isSubMenu){
22197             return;
22198         }
22199         
22200         this.hidden = true;
22201         
22202         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22203         this.triggerEl.on('click', this.onTriggerPress, this);
22204         
22205         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22206         this.buttonEl.on('click', this.onClick, this);
22207         
22208     },
22209     
22210     list : function()
22211     {
22212         if(this.isSubMenu){
22213             return this.el;
22214         }
22215         
22216         return this.el.select('ul.dropdown-menu', true).first();
22217     },
22218     
22219     onClick : function(e)
22220     {
22221         this.fireEvent("click", this, e);
22222     },
22223     
22224     onTriggerPress  : function(e)
22225     {   
22226         if (this.isVisible()) {
22227             this.hide();
22228         } else {
22229             this.show();
22230         }
22231     },
22232     
22233     isVisible : function(){
22234         return !this.hidden;
22235     },
22236     
22237     show : function()
22238     {
22239         this.fireEvent("beforeshow", this);
22240         
22241         this.hidden = false;
22242         this.el.addClass('open');
22243         
22244         Roo.get(document).on("mouseup", this.onMouseUp, this);
22245         
22246         this.fireEvent("show", this);
22247         
22248         
22249     },
22250     
22251     hide : function()
22252     {
22253         this.fireEvent("beforehide", this);
22254         
22255         this.hidden = true;
22256         this.el.removeClass('open');
22257         
22258         Roo.get(document).un("mouseup", this.onMouseUp);
22259         
22260         this.fireEvent("hide", this);
22261     },
22262     
22263     onMouseUp : function()
22264     {
22265         this.hide();
22266     }
22267     
22268 });
22269
22270  
22271  /*
22272  * - LGPL
22273  *
22274  * menu item
22275  * 
22276  */
22277 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22278
22279 /**
22280  * @class Roo.bootstrap.menu.Item
22281  * @extends Roo.bootstrap.Component
22282  * Bootstrap MenuItem class
22283  * @cfg {Boolean} submenu (true | false) default false
22284  * @cfg {String} html text of the item
22285  * @cfg {String} href the link
22286  * @cfg {Boolean} disable (true | false) default false
22287  * @cfg {Boolean} preventDefault (true | false) default true
22288  * @cfg {String} icon Font awesome icon
22289  * @cfg {String} pos Submenu align to (left | right) default right 
22290  * 
22291  * 
22292  * @constructor
22293  * Create a new Item
22294  * @param {Object} config The config object
22295  */
22296
22297
22298 Roo.bootstrap.menu.Item = function(config){
22299     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22300     this.addEvents({
22301         /**
22302          * @event mouseover
22303          * Fires when the mouse is hovering over this menu
22304          * @param {Roo.bootstrap.menu.Item} this
22305          * @param {Roo.EventObject} e
22306          */
22307         mouseover : true,
22308         /**
22309          * @event mouseout
22310          * Fires when the mouse exits this menu
22311          * @param {Roo.bootstrap.menu.Item} this
22312          * @param {Roo.EventObject} e
22313          */
22314         mouseout : true,
22315         // raw events
22316         /**
22317          * @event click
22318          * The raw click event for the entire grid.
22319          * @param {Roo.EventObject} e
22320          */
22321         click : true
22322     });
22323 };
22324
22325 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22326     
22327     submenu : false,
22328     href : '',
22329     html : '',
22330     preventDefault: true,
22331     disable : false,
22332     icon : false,
22333     pos : 'right',
22334     
22335     getAutoCreate : function()
22336     {
22337         var text = [
22338             {
22339                 tag : 'span',
22340                 cls : 'roo-menu-item-text',
22341                 html : this.html
22342             }
22343         ];
22344         
22345         if(this.icon){
22346             text.unshift({
22347                 tag : 'i',
22348                 cls : 'fa ' + this.icon
22349             })
22350         }
22351         
22352         var cfg = {
22353             tag : 'li',
22354             cn : [
22355                 {
22356                     tag : 'a',
22357                     href : this.href || '#',
22358                     cn : text
22359                 }
22360             ]
22361         };
22362         
22363         if(this.disable){
22364             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22365         }
22366         
22367         if(this.submenu){
22368             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22369             
22370             if(this.pos == 'left'){
22371                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22372             }
22373         }
22374         
22375         return cfg;
22376     },
22377     
22378     initEvents : function() 
22379     {
22380         this.el.on('mouseover', this.onMouseOver, this);
22381         this.el.on('mouseout', this.onMouseOut, this);
22382         
22383         this.el.select('a', true).first().on('click', this.onClick, this);
22384         
22385     },
22386     
22387     onClick : function(e)
22388     {
22389         if(this.preventDefault){
22390             e.preventDefault();
22391         }
22392         
22393         this.fireEvent("click", this, e);
22394     },
22395     
22396     onMouseOver : function(e)
22397     {
22398         if(this.submenu && this.pos == 'left'){
22399             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22400         }
22401         
22402         this.fireEvent("mouseover", this, e);
22403     },
22404     
22405     onMouseOut : function(e)
22406     {
22407         this.fireEvent("mouseout", this, e);
22408     }
22409 });
22410
22411  
22412
22413  /*
22414  * - LGPL
22415  *
22416  * menu separator
22417  * 
22418  */
22419 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22420
22421 /**
22422  * @class Roo.bootstrap.menu.Separator
22423  * @extends Roo.bootstrap.Component
22424  * Bootstrap Separator class
22425  * 
22426  * @constructor
22427  * Create a new Separator
22428  * @param {Object} config The config object
22429  */
22430
22431
22432 Roo.bootstrap.menu.Separator = function(config){
22433     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22434 };
22435
22436 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22437     
22438     getAutoCreate : function(){
22439         var cfg = {
22440             tag : 'li',
22441             cls: 'divider'
22442         };
22443         
22444         return cfg;
22445     }
22446    
22447 });
22448
22449  
22450
22451  /*
22452  * - LGPL
22453  *
22454  * Tooltip
22455  * 
22456  */
22457
22458 /**
22459  * @class Roo.bootstrap.Tooltip
22460  * Bootstrap Tooltip class
22461  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22462  * to determine which dom element triggers the tooltip.
22463  * 
22464  * It needs to add support for additional attributes like tooltip-position
22465  * 
22466  * @constructor
22467  * Create a new Toolti
22468  * @param {Object} config The config object
22469  */
22470
22471 Roo.bootstrap.Tooltip = function(config){
22472     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22473 };
22474
22475 Roo.apply(Roo.bootstrap.Tooltip, {
22476     /**
22477      * @function init initialize tooltip monitoring.
22478      * @static
22479      */
22480     currentEl : false,
22481     currentTip : false,
22482     currentRegion : false,
22483     
22484     //  init : delay?
22485     
22486     init : function()
22487     {
22488         Roo.get(document).on('mouseover', this.enter ,this);
22489         Roo.get(document).on('mouseout', this.leave, this);
22490          
22491         
22492         this.currentTip = new Roo.bootstrap.Tooltip();
22493     },
22494     
22495     enter : function(ev)
22496     {
22497         var dom = ev.getTarget();
22498         
22499         //Roo.log(['enter',dom]);
22500         var el = Roo.fly(dom);
22501         if (this.currentEl) {
22502             //Roo.log(dom);
22503             //Roo.log(this.currentEl);
22504             //Roo.log(this.currentEl.contains(dom));
22505             if (this.currentEl == el) {
22506                 return;
22507             }
22508             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22509                 return;
22510             }
22511
22512         }
22513         
22514         
22515         
22516         if (this.currentTip.el) {
22517             this.currentTip.el.hide(); // force hiding...
22518         }    
22519         //Roo.log(ev);
22520         var bindEl = el;
22521         
22522         // you can not look for children, as if el is the body.. then everythign is the child..
22523         if (!el.attr('tooltip')) { //
22524             if (!el.select("[tooltip]").elements.length) {
22525                 return;
22526             }
22527             // is the mouse over this child...?
22528             bindEl = el.select("[tooltip]").first();
22529             var xy = ev.getXY();
22530             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22531                 //Roo.log("not in region.");
22532                 return;
22533             }
22534             //Roo.log("child element over..");
22535             
22536         }
22537         this.currentEl = bindEl;
22538         this.currentTip.bind(bindEl);
22539         this.currentRegion = Roo.lib.Region.getRegion(dom);
22540         this.currentTip.enter();
22541         
22542     },
22543     leave : function(ev)
22544     {
22545         var dom = ev.getTarget();
22546         //Roo.log(['leave',dom]);
22547         if (!this.currentEl) {
22548             return;
22549         }
22550         
22551         
22552         if (dom != this.currentEl.dom) {
22553             return;
22554         }
22555         var xy = ev.getXY();
22556         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22557             return;
22558         }
22559         // only activate leave if mouse cursor is outside... bounding box..
22560         
22561         
22562         
22563         
22564         if (this.currentTip) {
22565             this.currentTip.leave();
22566         }
22567         //Roo.log('clear currentEl');
22568         this.currentEl = false;
22569         
22570         
22571     },
22572     alignment : {
22573         'left' : ['r-l', [-2,0], 'right'],
22574         'right' : ['l-r', [2,0], 'left'],
22575         'bottom' : ['t-b', [0,2], 'top'],
22576         'top' : [ 'b-t', [0,-2], 'bottom']
22577     }
22578     
22579 });
22580
22581
22582 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22583     
22584     
22585     bindEl : false,
22586     
22587     delay : null, // can be { show : 300 , hide: 500}
22588     
22589     timeout : null,
22590     
22591     hoverState : null, //???
22592     
22593     placement : 'bottom', 
22594     
22595     getAutoCreate : function(){
22596     
22597         var cfg = {
22598            cls : 'tooltip',
22599            role : 'tooltip',
22600            cn : [
22601                 {
22602                     cls : 'tooltip-arrow'
22603                 },
22604                 {
22605                     cls : 'tooltip-inner'
22606                 }
22607            ]
22608         };
22609         
22610         return cfg;
22611     },
22612     bind : function(el)
22613     {
22614         this.bindEl = el;
22615     },
22616       
22617     
22618     enter : function () {
22619        
22620         if (this.timeout != null) {
22621             clearTimeout(this.timeout);
22622         }
22623         
22624         this.hoverState = 'in';
22625          //Roo.log("enter - show");
22626         if (!this.delay || !this.delay.show) {
22627             this.show();
22628             return;
22629         }
22630         var _t = this;
22631         this.timeout = setTimeout(function () {
22632             if (_t.hoverState == 'in') {
22633                 _t.show();
22634             }
22635         }, this.delay.show);
22636     },
22637     leave : function()
22638     {
22639         clearTimeout(this.timeout);
22640     
22641         this.hoverState = 'out';
22642          if (!this.delay || !this.delay.hide) {
22643             this.hide();
22644             return;
22645         }
22646        
22647         var _t = this;
22648         this.timeout = setTimeout(function () {
22649             //Roo.log("leave - timeout");
22650             
22651             if (_t.hoverState == 'out') {
22652                 _t.hide();
22653                 Roo.bootstrap.Tooltip.currentEl = false;
22654             }
22655         }, delay);
22656     },
22657     
22658     show : function ()
22659     {
22660         if (!this.el) {
22661             this.render(document.body);
22662         }
22663         // set content.
22664         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22665         
22666         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22667         
22668         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22669         
22670         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22671         
22672         var placement = typeof this.placement == 'function' ?
22673             this.placement.call(this, this.el, on_el) :
22674             this.placement;
22675             
22676         var autoToken = /\s?auto?\s?/i;
22677         var autoPlace = autoToken.test(placement);
22678         if (autoPlace) {
22679             placement = placement.replace(autoToken, '') || 'top';
22680         }
22681         
22682         //this.el.detach()
22683         //this.el.setXY([0,0]);
22684         this.el.show();
22685         //this.el.dom.style.display='block';
22686         this.el.addClass(placement);
22687         
22688         //this.el.appendTo(on_el);
22689         
22690         var p = this.getPosition();
22691         var box = this.el.getBox();
22692         
22693         if (autoPlace) {
22694             // fixme..
22695         }
22696         var align = Roo.bootstrap.Tooltip.alignment[placement];
22697         this.el.alignTo(this.bindEl, align[0],align[1]);
22698         //var arrow = this.el.select('.arrow',true).first();
22699         //arrow.set(align[2], 
22700         
22701         this.el.addClass('in fade');
22702         this.hoverState = null;
22703         
22704         if (this.el.hasClass('fade')) {
22705             // fade it?
22706         }
22707         
22708     },
22709     hide : function()
22710     {
22711          
22712         if (!this.el) {
22713             return;
22714         }
22715         //this.el.setXY([0,0]);
22716         this.el.removeClass('in');
22717         //this.el.hide();
22718         
22719     }
22720     
22721 });
22722  
22723
22724  /*
22725  * - LGPL
22726  *
22727  * Location Picker
22728  * 
22729  */
22730
22731 /**
22732  * @class Roo.bootstrap.LocationPicker
22733  * @extends Roo.bootstrap.Component
22734  * Bootstrap LocationPicker class
22735  * @cfg {Number} latitude Position when init default 0
22736  * @cfg {Number} longitude Position when init default 0
22737  * @cfg {Number} zoom default 15
22738  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22739  * @cfg {Boolean} mapTypeControl default false
22740  * @cfg {Boolean} disableDoubleClickZoom default false
22741  * @cfg {Boolean} scrollwheel default true
22742  * @cfg {Boolean} streetViewControl default false
22743  * @cfg {Number} radius default 0
22744  * @cfg {String} locationName
22745  * @cfg {Boolean} draggable default true
22746  * @cfg {Boolean} enableAutocomplete default false
22747  * @cfg {Boolean} enableReverseGeocode default true
22748  * @cfg {String} markerTitle
22749  * 
22750  * @constructor
22751  * Create a new LocationPicker
22752  * @param {Object} config The config object
22753  */
22754
22755
22756 Roo.bootstrap.LocationPicker = function(config){
22757     
22758     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22759     
22760     this.addEvents({
22761         /**
22762          * @event initial
22763          * Fires when the picker initialized.
22764          * @param {Roo.bootstrap.LocationPicker} this
22765          * @param {Google Location} location
22766          */
22767         initial : true,
22768         /**
22769          * @event positionchanged
22770          * Fires when the picker position changed.
22771          * @param {Roo.bootstrap.LocationPicker} this
22772          * @param {Google Location} location
22773          */
22774         positionchanged : true,
22775         /**
22776          * @event resize
22777          * Fires when the map resize.
22778          * @param {Roo.bootstrap.LocationPicker} this
22779          */
22780         resize : true,
22781         /**
22782          * @event show
22783          * Fires when the map show.
22784          * @param {Roo.bootstrap.LocationPicker} this
22785          */
22786         show : true,
22787         /**
22788          * @event hide
22789          * Fires when the map hide.
22790          * @param {Roo.bootstrap.LocationPicker} this
22791          */
22792         hide : true,
22793         /**
22794          * @event mapClick
22795          * Fires when click the map.
22796          * @param {Roo.bootstrap.LocationPicker} this
22797          * @param {Map event} e
22798          */
22799         mapClick : true,
22800         /**
22801          * @event mapRightClick
22802          * Fires when right click the map.
22803          * @param {Roo.bootstrap.LocationPicker} this
22804          * @param {Map event} e
22805          */
22806         mapRightClick : true,
22807         /**
22808          * @event markerClick
22809          * Fires when click the marker.
22810          * @param {Roo.bootstrap.LocationPicker} this
22811          * @param {Map event} e
22812          */
22813         markerClick : true,
22814         /**
22815          * @event markerRightClick
22816          * Fires when right click the marker.
22817          * @param {Roo.bootstrap.LocationPicker} this
22818          * @param {Map event} e
22819          */
22820         markerRightClick : true,
22821         /**
22822          * @event OverlayViewDraw
22823          * Fires when OverlayView Draw
22824          * @param {Roo.bootstrap.LocationPicker} this
22825          */
22826         OverlayViewDraw : true,
22827         /**
22828          * @event OverlayViewOnAdd
22829          * Fires when OverlayView Draw
22830          * @param {Roo.bootstrap.LocationPicker} this
22831          */
22832         OverlayViewOnAdd : true,
22833         /**
22834          * @event OverlayViewOnRemove
22835          * Fires when OverlayView Draw
22836          * @param {Roo.bootstrap.LocationPicker} this
22837          */
22838         OverlayViewOnRemove : true,
22839         /**
22840          * @event OverlayViewShow
22841          * Fires when OverlayView Draw
22842          * @param {Roo.bootstrap.LocationPicker} this
22843          * @param {Pixel} cpx
22844          */
22845         OverlayViewShow : true,
22846         /**
22847          * @event OverlayViewHide
22848          * Fires when OverlayView Draw
22849          * @param {Roo.bootstrap.LocationPicker} this
22850          */
22851         OverlayViewHide : true
22852     });
22853         
22854 };
22855
22856 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22857     
22858     gMapContext: false,
22859     
22860     latitude: 0,
22861     longitude: 0,
22862     zoom: 15,
22863     mapTypeId: false,
22864     mapTypeControl: false,
22865     disableDoubleClickZoom: false,
22866     scrollwheel: true,
22867     streetViewControl: false,
22868     radius: 0,
22869     locationName: '',
22870     draggable: true,
22871     enableAutocomplete: false,
22872     enableReverseGeocode: true,
22873     markerTitle: '',
22874     
22875     getAutoCreate: function()
22876     {
22877
22878         var cfg = {
22879             tag: 'div',
22880             cls: 'roo-location-picker'
22881         };
22882         
22883         return cfg
22884     },
22885     
22886     initEvents: function(ct, position)
22887     {       
22888         if(!this.el.getWidth() || this.isApplied()){
22889             return;
22890         }
22891         
22892         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22893         
22894         this.initial();
22895     },
22896     
22897     initial: function()
22898     {
22899         if(!this.mapTypeId){
22900             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22901         }
22902         
22903         this.gMapContext = this.GMapContext();
22904         
22905         this.initOverlayView();
22906         
22907         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22908         
22909         var _this = this;
22910                 
22911         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22912             _this.setPosition(_this.gMapContext.marker.position);
22913         });
22914         
22915         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22916             _this.fireEvent('mapClick', this, event);
22917             
22918         });
22919
22920         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22921             _this.fireEvent('mapRightClick', this, event);
22922             
22923         });
22924         
22925         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22926             _this.fireEvent('markerClick', this, event);
22927             
22928         });
22929
22930         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22931             _this.fireEvent('markerRightClick', this, event);
22932             
22933         });
22934         
22935         this.setPosition(this.gMapContext.location);
22936         
22937         this.fireEvent('initial', this, this.gMapContext.location);
22938     },
22939     
22940     initOverlayView: function()
22941     {
22942         var _this = this;
22943         
22944         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22945             
22946             draw: function()
22947             {
22948                 _this.fireEvent('OverlayViewDraw', _this);
22949             },
22950             
22951             onAdd: function()
22952             {
22953                 _this.fireEvent('OverlayViewOnAdd', _this);
22954             },
22955             
22956             onRemove: function()
22957             {
22958                 _this.fireEvent('OverlayViewOnRemove', _this);
22959             },
22960             
22961             show: function(cpx)
22962             {
22963                 _this.fireEvent('OverlayViewShow', _this, cpx);
22964             },
22965             
22966             hide: function()
22967             {
22968                 _this.fireEvent('OverlayViewHide', _this);
22969             }
22970             
22971         });
22972     },
22973     
22974     fromLatLngToContainerPixel: function(event)
22975     {
22976         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22977     },
22978     
22979     isApplied: function() 
22980     {
22981         return this.getGmapContext() == false ? false : true;
22982     },
22983     
22984     getGmapContext: function() 
22985     {
22986         return this.gMapContext
22987     },
22988     
22989     GMapContext: function() 
22990     {
22991         var position = new google.maps.LatLng(this.latitude, this.longitude);
22992         
22993         var _map = new google.maps.Map(this.el.dom, {
22994             center: position,
22995             zoom: this.zoom,
22996             mapTypeId: this.mapTypeId,
22997             mapTypeControl: this.mapTypeControl,
22998             disableDoubleClickZoom: this.disableDoubleClickZoom,
22999             scrollwheel: this.scrollwheel,
23000             streetViewControl: this.streetViewControl,
23001             locationName: this.locationName,
23002             draggable: this.draggable,
23003             enableAutocomplete: this.enableAutocomplete,
23004             enableReverseGeocode: this.enableReverseGeocode
23005         });
23006         
23007         var _marker = new google.maps.Marker({
23008             position: position,
23009             map: _map,
23010             title: this.markerTitle,
23011             draggable: this.draggable
23012         });
23013         
23014         return {
23015             map: _map,
23016             marker: _marker,
23017             circle: null,
23018             location: position,
23019             radius: this.radius,
23020             locationName: this.locationName,
23021             addressComponents: {
23022                 formatted_address: null,
23023                 addressLine1: null,
23024                 addressLine2: null,
23025                 streetName: null,
23026                 streetNumber: null,
23027                 city: null,
23028                 district: null,
23029                 state: null,
23030                 stateOrProvince: null
23031             },
23032             settings: this,
23033             domContainer: this.el.dom,
23034             geodecoder: new google.maps.Geocoder()
23035         };
23036     },
23037     
23038     drawCircle: function(center, radius, options) 
23039     {
23040         if (this.gMapContext.circle != null) {
23041             this.gMapContext.circle.setMap(null);
23042         }
23043         if (radius > 0) {
23044             radius *= 1;
23045             options = Roo.apply({}, options, {
23046                 strokeColor: "#0000FF",
23047                 strokeOpacity: .35,
23048                 strokeWeight: 2,
23049                 fillColor: "#0000FF",
23050                 fillOpacity: .2
23051             });
23052             
23053             options.map = this.gMapContext.map;
23054             options.radius = radius;
23055             options.center = center;
23056             this.gMapContext.circle = new google.maps.Circle(options);
23057             return this.gMapContext.circle;
23058         }
23059         
23060         return null;
23061     },
23062     
23063     setPosition: function(location) 
23064     {
23065         this.gMapContext.location = location;
23066         this.gMapContext.marker.setPosition(location);
23067         this.gMapContext.map.panTo(location);
23068         this.drawCircle(location, this.gMapContext.radius, {});
23069         
23070         var _this = this;
23071         
23072         if (this.gMapContext.settings.enableReverseGeocode) {
23073             this.gMapContext.geodecoder.geocode({
23074                 latLng: this.gMapContext.location
23075             }, function(results, status) {
23076                 
23077                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23078                     _this.gMapContext.locationName = results[0].formatted_address;
23079                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23080                     
23081                     _this.fireEvent('positionchanged', this, location);
23082                 }
23083             });
23084             
23085             return;
23086         }
23087         
23088         this.fireEvent('positionchanged', this, location);
23089     },
23090     
23091     resize: function()
23092     {
23093         google.maps.event.trigger(this.gMapContext.map, "resize");
23094         
23095         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23096         
23097         this.fireEvent('resize', this);
23098     },
23099     
23100     setPositionByLatLng: function(latitude, longitude)
23101     {
23102         this.setPosition(new google.maps.LatLng(latitude, longitude));
23103     },
23104     
23105     getCurrentPosition: function() 
23106     {
23107         return {
23108             latitude: this.gMapContext.location.lat(),
23109             longitude: this.gMapContext.location.lng()
23110         };
23111     },
23112     
23113     getAddressName: function() 
23114     {
23115         return this.gMapContext.locationName;
23116     },
23117     
23118     getAddressComponents: function() 
23119     {
23120         return this.gMapContext.addressComponents;
23121     },
23122     
23123     address_component_from_google_geocode: function(address_components) 
23124     {
23125         var result = {};
23126         
23127         for (var i = 0; i < address_components.length; i++) {
23128             var component = address_components[i];
23129             if (component.types.indexOf("postal_code") >= 0) {
23130                 result.postalCode = component.short_name;
23131             } else if (component.types.indexOf("street_number") >= 0) {
23132                 result.streetNumber = component.short_name;
23133             } else if (component.types.indexOf("route") >= 0) {
23134                 result.streetName = component.short_name;
23135             } else if (component.types.indexOf("neighborhood") >= 0) {
23136                 result.city = component.short_name;
23137             } else if (component.types.indexOf("locality") >= 0) {
23138                 result.city = component.short_name;
23139             } else if (component.types.indexOf("sublocality") >= 0) {
23140                 result.district = component.short_name;
23141             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23142                 result.stateOrProvince = component.short_name;
23143             } else if (component.types.indexOf("country") >= 0) {
23144                 result.country = component.short_name;
23145             }
23146         }
23147         
23148         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23149         result.addressLine2 = "";
23150         return result;
23151     },
23152     
23153     setZoomLevel: function(zoom)
23154     {
23155         this.gMapContext.map.setZoom(zoom);
23156     },
23157     
23158     show: function()
23159     {
23160         if(!this.el){
23161             return;
23162         }
23163         
23164         this.el.show();
23165         
23166         this.resize();
23167         
23168         this.fireEvent('show', this);
23169     },
23170     
23171     hide: function()
23172     {
23173         if(!this.el){
23174             return;
23175         }
23176         
23177         this.el.hide();
23178         
23179         this.fireEvent('hide', this);
23180     }
23181     
23182 });
23183
23184 Roo.apply(Roo.bootstrap.LocationPicker, {
23185     
23186     OverlayView : function(map, options)
23187     {
23188         options = options || {};
23189         
23190         this.setMap(map);
23191     }
23192     
23193     
23194 });/*
23195  * - LGPL
23196  *
23197  * Alert
23198  * 
23199  */
23200
23201 /**
23202  * @class Roo.bootstrap.Alert
23203  * @extends Roo.bootstrap.Component
23204  * Bootstrap Alert class
23205  * @cfg {String} title The title of alert
23206  * @cfg {String} html The content of alert
23207  * @cfg {String} weight (  success | info | warning | danger )
23208  * @cfg {String} faicon font-awesomeicon
23209  * 
23210  * @constructor
23211  * Create a new alert
23212  * @param {Object} config The config object
23213  */
23214
23215
23216 Roo.bootstrap.Alert = function(config){
23217     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23218     
23219 };
23220
23221 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23222     
23223     title: '',
23224     html: '',
23225     weight: false,
23226     faicon: false,
23227     
23228     getAutoCreate : function()
23229     {
23230         
23231         var cfg = {
23232             tag : 'div',
23233             cls : 'alert',
23234             cn : [
23235                 {
23236                     tag : 'i',
23237                     cls : 'roo-alert-icon'
23238                     
23239                 },
23240                 {
23241                     tag : 'b',
23242                     cls : 'roo-alert-title',
23243                     html : this.title
23244                 },
23245                 {
23246                     tag : 'span',
23247                     cls : 'roo-alert-text',
23248                     html : this.html
23249                 }
23250             ]
23251         };
23252         
23253         if(this.faicon){
23254             cfg.cn[0].cls += ' fa ' + this.faicon;
23255         }
23256         
23257         if(this.weight){
23258             cfg.cls += ' alert-' + this.weight;
23259         }
23260         
23261         return cfg;
23262     },
23263     
23264     initEvents: function() 
23265     {
23266         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23267     },
23268     
23269     setTitle : function(str)
23270     {
23271         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23272     },
23273     
23274     setText : function(str)
23275     {
23276         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23277     },
23278     
23279     setWeight : function(weight)
23280     {
23281         if(this.weight){
23282             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23283         }
23284         
23285         this.weight = weight;
23286         
23287         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23288     },
23289     
23290     setIcon : function(icon)
23291     {
23292         if(this.faicon){
23293             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23294         }
23295         
23296         this.faicon = icon
23297         
23298         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23299     },
23300     
23301     hide: function() 
23302     {
23303         this.el.hide();   
23304     },
23305     
23306     show: function() 
23307     {  
23308         this.el.show();   
23309     }
23310     
23311 });
23312
23313