b217ea4582f5bbce14ea346df8d320156097961a
[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(['xsUrl', 'smUrl', 'mdUrl', 'lgUrl'], function(size){
1379             if(!_this[size]){
1380                 return;
1381             }
1382             
1383             var img = {
1384                 tag: 'img',
1385                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1386                 html: _this.html || cfg.html,
1387                 src: _this[size]
1388             }
1389             
1390             img.cls += ' roo-image-responsive-' + size;
1391             
1392             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1393                 cfg.cls += ' img-' + _this.border;
1394             }
1395             
1396             if(_this.alt){
1397                 cfg.alt = _this.alt;
1398             }
1399             
1400             if(_this.href){
1401                 var a = {
1402                     tag: 'a',
1403                     href: _this.href,
1404                     cn: [
1405                         img
1406                     ]
1407                 }
1408
1409                 if(this.target){
1410                     a.target = _this.target;
1411                 }
1412             }
1413             
1414             cfg.cn.push((_this.href) ? a : img);
1415             
1416         });
1417         
1418         return cfg;
1419     },
1420     
1421     createSingleImg : function()
1422     {
1423         var cfg = {
1424             tag: 'img',
1425             cls: (this.imgResponsive) ? 'img-responsive' : '',
1426             html : null
1427         }
1428         
1429         cfg.html = this.html || cfg.html;
1430         
1431         cfg.src = this.src || cfg.src;
1432         
1433         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1434             cfg.cls += ' img-' + this.border;
1435         }
1436         
1437         if(this.alt){
1438             cfg.alt = this.alt;
1439         }
1440         
1441         if(this.href){
1442             var a = {
1443                 tag: 'a',
1444                 href: this.href,
1445                 cn: [
1446                     cfg
1447                 ]
1448             }
1449             
1450             if(this.target){
1451                 a.target = this.target;
1452             }
1453             
1454         }
1455         
1456         return (this.href) ? a : cfg;
1457     },
1458     
1459     initEvents: function() {
1460         
1461         if(!this.href){
1462             this.el.on('click', this.onClick, this);
1463         }
1464     },
1465     
1466     onClick : function(e)
1467     {
1468         Roo.log('img onclick');
1469         this.fireEvent('click', this, e);
1470     }
1471    
1472 });
1473
1474  /*
1475  * - LGPL
1476  *
1477  * image
1478  * 
1479  */
1480
1481
1482 /**
1483  * @class Roo.bootstrap.Link
1484  * @extends Roo.bootstrap.Component
1485  * Bootstrap Link Class
1486  * @cfg {String} alt image alternative text
1487  * @cfg {String} href a tag href
1488  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1489  * @cfg {String} html the content of the link.
1490  * @cfg {String} anchor name for the anchor link
1491
1492  * @cfg {Boolean} preventDefault (true | false) default false
1493
1494  * 
1495  * @constructor
1496  * Create a new Input
1497  * @param {Object} config The config object
1498  */
1499
1500 Roo.bootstrap.Link = function(config){
1501     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1502     
1503     this.addEvents({
1504         // img events
1505         /**
1506          * @event click
1507          * The img click event for the img.
1508          * @param {Roo.EventObject} e
1509          */
1510         "click" : true
1511     });
1512 };
1513
1514 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1515     
1516     href: false,
1517     target: false,
1518     preventDefault: false,
1519     anchor : false,
1520     alt : false,
1521
1522     getAutoCreate : function()
1523     {
1524         
1525         var cfg = {
1526             tag: 'a'
1527         };
1528         // anchor's do not require html/href...
1529         if (this.anchor === false) {
1530             cfg.html = this.html || '';
1531             cfg.href = this.href || '#';
1532         } else {
1533             cfg.name = this.anchor;
1534             if (this.html !== false) {
1535                 cfg.html = this.html;
1536             }
1537             if (this.href !== false) {
1538                 cfg.href = this.href;
1539             }
1540         }
1541         
1542         if(this.alt !== false){
1543             cfg.alt = this.alt;
1544         }
1545         
1546         
1547         if(this.target !== false) {
1548             cfg.target = this.target;
1549         }
1550         
1551         return cfg;
1552     },
1553     
1554     initEvents: function() {
1555         
1556         if(!this.href || this.preventDefault){
1557             this.el.on('click', this.onClick, this);
1558         }
1559     },
1560     
1561     onClick : function(e)
1562     {
1563         if(this.preventDefault){
1564             e.preventDefault();
1565         }
1566         //Roo.log('img onclick');
1567         this.fireEvent('click', this, e);
1568     }
1569    
1570 });
1571
1572  /*
1573  * - LGPL
1574  *
1575  * header
1576  * 
1577  */
1578
1579 /**
1580  * @class Roo.bootstrap.Header
1581  * @extends Roo.bootstrap.Component
1582  * Bootstrap Header class
1583  * @cfg {String} html content of header
1584  * @cfg {Number} level (1|2|3|4|5|6) default 1
1585  * 
1586  * @constructor
1587  * Create a new Header
1588  * @param {Object} config The config object
1589  */
1590
1591
1592 Roo.bootstrap.Header  = function(config){
1593     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1594 };
1595
1596 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1597     
1598     //href : false,
1599     html : false,
1600     level : 1,
1601     
1602     
1603     
1604     getAutoCreate : function(){
1605         
1606         
1607         
1608         var cfg = {
1609             tag: 'h' + (1 *this.level),
1610             html: this.html || ''
1611         } ;
1612         
1613         return cfg;
1614     }
1615    
1616 });
1617
1618  
1619
1620  /*
1621  * Based on:
1622  * Ext JS Library 1.1.1
1623  * Copyright(c) 2006-2007, Ext JS, LLC.
1624  *
1625  * Originally Released Under LGPL - original licence link has changed is not relivant.
1626  *
1627  * Fork - LGPL
1628  * <script type="text/javascript">
1629  */
1630  
1631 /**
1632  * @class Roo.bootstrap.MenuMgr
1633  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1634  * @singleton
1635  */
1636 Roo.bootstrap.MenuMgr = function(){
1637    var menus, active, groups = {}, attached = false, lastShow = new Date();
1638
1639    // private - called when first menu is created
1640    function init(){
1641        menus = {};
1642        active = new Roo.util.MixedCollection();
1643        Roo.get(document).addKeyListener(27, function(){
1644            if(active.length > 0){
1645                hideAll();
1646            }
1647        });
1648    }
1649
1650    // private
1651    function hideAll(){
1652        if(active && active.length > 0){
1653            var c = active.clone();
1654            c.each(function(m){
1655                m.hide();
1656            });
1657        }
1658    }
1659
1660    // private
1661    function onHide(m){
1662        active.remove(m);
1663        if(active.length < 1){
1664            Roo.get(document).un("mouseup", onMouseDown);
1665             
1666            attached = false;
1667        }
1668    }
1669
1670    // private
1671    function onShow(m){
1672        var last = active.last();
1673        lastShow = new Date();
1674        active.add(m);
1675        if(!attached){
1676           Roo.get(document).on("mouseup", onMouseDown);
1677            
1678            attached = true;
1679        }
1680        if(m.parentMenu){
1681           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1682           m.parentMenu.activeChild = m;
1683        }else if(last && last.isVisible()){
1684           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1685        }
1686    }
1687
1688    // private
1689    function onBeforeHide(m){
1690        if(m.activeChild){
1691            m.activeChild.hide();
1692        }
1693        if(m.autoHideTimer){
1694            clearTimeout(m.autoHideTimer);
1695            delete m.autoHideTimer;
1696        }
1697    }
1698
1699    // private
1700    function onBeforeShow(m){
1701        var pm = m.parentMenu;
1702        if(!pm && !m.allowOtherMenus){
1703            hideAll();
1704        }else if(pm && pm.activeChild && active != m){
1705            pm.activeChild.hide();
1706        }
1707    }
1708
1709    // private this should really trigger on mouseup..
1710    function onMouseDown(e){
1711         Roo.log("on Mouse Up");
1712         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1713             Roo.log("hideAll");
1714             hideAll();
1715             e.stopEvent();
1716         }
1717         
1718         
1719    }
1720
1721    // private
1722    function onBeforeCheck(mi, state){
1723        if(state){
1724            var g = groups[mi.group];
1725            for(var i = 0, l = g.length; i < l; i++){
1726                if(g[i] != mi){
1727                    g[i].setChecked(false);
1728                }
1729            }
1730        }
1731    }
1732
1733    return {
1734
1735        /**
1736         * Hides all menus that are currently visible
1737         */
1738        hideAll : function(){
1739             hideAll();  
1740        },
1741
1742        // private
1743        register : function(menu){
1744            if(!menus){
1745                init();
1746            }
1747            menus[menu.id] = menu;
1748            menu.on("beforehide", onBeforeHide);
1749            menu.on("hide", onHide);
1750            menu.on("beforeshow", onBeforeShow);
1751            menu.on("show", onShow);
1752            var g = menu.group;
1753            if(g && menu.events["checkchange"]){
1754                if(!groups[g]){
1755                    groups[g] = [];
1756                }
1757                groups[g].push(menu);
1758                menu.on("checkchange", onCheck);
1759            }
1760        },
1761
1762         /**
1763          * Returns a {@link Roo.menu.Menu} object
1764          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1765          * be used to generate and return a new Menu instance.
1766          */
1767        get : function(menu){
1768            if(typeof menu == "string"){ // menu id
1769                return menus[menu];
1770            }else if(menu.events){  // menu instance
1771                return menu;
1772            }
1773            /*else if(typeof menu.length == 'number'){ // array of menu items?
1774                return new Roo.bootstrap.Menu({items:menu});
1775            }else{ // otherwise, must be a config
1776                return new Roo.bootstrap.Menu(menu);
1777            }
1778            */
1779            return false;
1780        },
1781
1782        // private
1783        unregister : function(menu){
1784            delete menus[menu.id];
1785            menu.un("beforehide", onBeforeHide);
1786            menu.un("hide", onHide);
1787            menu.un("beforeshow", onBeforeShow);
1788            menu.un("show", onShow);
1789            var g = menu.group;
1790            if(g && menu.events["checkchange"]){
1791                groups[g].remove(menu);
1792                menu.un("checkchange", onCheck);
1793            }
1794        },
1795
1796        // private
1797        registerCheckable : function(menuItem){
1798            var g = menuItem.group;
1799            if(g){
1800                if(!groups[g]){
1801                    groups[g] = [];
1802                }
1803                groups[g].push(menuItem);
1804                menuItem.on("beforecheckchange", onBeforeCheck);
1805            }
1806        },
1807
1808        // private
1809        unregisterCheckable : function(menuItem){
1810            var g = menuItem.group;
1811            if(g){
1812                groups[g].remove(menuItem);
1813                menuItem.un("beforecheckchange", onBeforeCheck);
1814            }
1815        }
1816    };
1817 }();/*
1818  * - LGPL
1819  *
1820  * menu
1821  * 
1822  */
1823
1824 /**
1825  * @class Roo.bootstrap.Menu
1826  * @extends Roo.bootstrap.Component
1827  * Bootstrap Menu class - container for MenuItems
1828  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1829  * 
1830  * @constructor
1831  * Create a new Menu
1832  * @param {Object} config The config object
1833  */
1834
1835
1836 Roo.bootstrap.Menu = function(config){
1837     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1838     if (this.registerMenu) {
1839         Roo.bootstrap.MenuMgr.register(this);
1840     }
1841     this.addEvents({
1842         /**
1843          * @event beforeshow
1844          * Fires before this menu is displayed
1845          * @param {Roo.menu.Menu} this
1846          */
1847         beforeshow : true,
1848         /**
1849          * @event beforehide
1850          * Fires before this menu is hidden
1851          * @param {Roo.menu.Menu} this
1852          */
1853         beforehide : true,
1854         /**
1855          * @event show
1856          * Fires after this menu is displayed
1857          * @param {Roo.menu.Menu} this
1858          */
1859         show : true,
1860         /**
1861          * @event hide
1862          * Fires after this menu is hidden
1863          * @param {Roo.menu.Menu} this
1864          */
1865         hide : true,
1866         /**
1867          * @event click
1868          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1869          * @param {Roo.menu.Menu} this
1870          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1871          * @param {Roo.EventObject} e
1872          */
1873         click : true,
1874         /**
1875          * @event mouseover
1876          * Fires when the mouse is hovering over this menu
1877          * @param {Roo.menu.Menu} this
1878          * @param {Roo.EventObject} e
1879          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1880          */
1881         mouseover : true,
1882         /**
1883          * @event mouseout
1884          * Fires when the mouse exits this menu
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.EventObject} e
1887          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1888          */
1889         mouseout : true,
1890         /**
1891          * @event itemclick
1892          * Fires when a menu item contained in this menu is clicked
1893          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1894          * @param {Roo.EventObject} e
1895          */
1896         itemclick: true
1897     });
1898     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1899 };
1900
1901 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1902     
1903    /// html : false,
1904     //align : '',
1905     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1906     type: false,
1907     /**
1908      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1909      */
1910     registerMenu : true,
1911     
1912     menuItems :false, // stores the menu items..
1913     
1914     hidden:true,
1915     
1916     parentMenu : false,
1917     
1918     getChildContainer : function() {
1919         return this.el;  
1920     },
1921     
1922     getAutoCreate : function(){
1923          
1924         //if (['right'].indexOf(this.align)!==-1) {
1925         //    cfg.cn[1].cls += ' pull-right'
1926         //}
1927         
1928         
1929         var cfg = {
1930             tag : 'ul',
1931             cls : 'dropdown-menu' ,
1932             style : 'z-index:1000'
1933             
1934         }
1935         
1936         if (this.type === 'submenu') {
1937             cfg.cls = 'submenu active';
1938         }
1939         if (this.type === 'treeview') {
1940             cfg.cls = 'treeview-menu';
1941         }
1942         
1943         return cfg;
1944     },
1945     initEvents : function() {
1946         
1947        // Roo.log("ADD event");
1948        // Roo.log(this.triggerEl.dom);
1949         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1950         
1951         this.triggerEl.addClass('dropdown-toggle');
1952         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1953
1954         this.el.on("mouseover", this.onMouseOver, this);
1955         this.el.on("mouseout", this.onMouseOut, this);
1956         
1957         
1958     },
1959     findTargetItem : function(e){
1960         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1961         if(!t){
1962             return false;
1963         }
1964         //Roo.log(t);         Roo.log(t.id);
1965         if(t && t.id){
1966             //Roo.log(this.menuitems);
1967             return this.menuitems.get(t.id);
1968             
1969             //return this.items.get(t.menuItemId);
1970         }
1971         
1972         return false;
1973     },
1974     onClick : function(e){
1975         Roo.log("menu.onClick");
1976         var t = this.findTargetItem(e);
1977         if(!t || t.isContainer){
1978             return;
1979         }
1980         Roo.log(e);
1981         /*
1982         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1983             if(t == this.activeItem && t.shouldDeactivate(e)){
1984                 this.activeItem.deactivate();
1985                 delete this.activeItem;
1986                 return;
1987             }
1988             if(t.canActivate){
1989                 this.setActiveItem(t, true);
1990             }
1991             return;
1992             
1993             
1994         }
1995         */
1996        
1997         Roo.log('pass click event');
1998         
1999         t.onClick(e);
2000         
2001         this.fireEvent("click", this, t, e);
2002         
2003         this.hide();
2004     },
2005      onMouseOver : function(e){
2006         var t  = this.findTargetItem(e);
2007         //Roo.log(t);
2008         //if(t){
2009         //    if(t.canActivate && !t.disabled){
2010         //        this.setActiveItem(t, true);
2011         //    }
2012         //}
2013         
2014         this.fireEvent("mouseover", this, e, t);
2015     },
2016     isVisible : function(){
2017         return !this.hidden;
2018     },
2019      onMouseOut : function(e){
2020         var t  = this.findTargetItem(e);
2021         
2022         //if(t ){
2023         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2024         //        this.activeItem.deactivate();
2025         //        delete this.activeItem;
2026         //    }
2027         //}
2028         this.fireEvent("mouseout", this, e, t);
2029     },
2030     
2031     
2032     /**
2033      * Displays this menu relative to another element
2034      * @param {String/HTMLElement/Roo.Element} element The element to align to
2035      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2036      * the element (defaults to this.defaultAlign)
2037      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2038      */
2039     show : function(el, pos, parentMenu){
2040         this.parentMenu = parentMenu;
2041         if(!this.el){
2042             this.render();
2043         }
2044         this.fireEvent("beforeshow", this);
2045         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2046     },
2047      /**
2048      * Displays this menu at a specific xy position
2049      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2050      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2051      */
2052     showAt : function(xy, parentMenu, /* private: */_e){
2053         this.parentMenu = parentMenu;
2054         if(!this.el){
2055             this.render();
2056         }
2057         if(_e !== false){
2058             this.fireEvent("beforeshow", this);
2059             //xy = this.el.adjustForConstraints(xy);
2060         }
2061         
2062         //this.el.show();
2063         this.hideMenuItems();
2064         this.hidden = false;
2065         this.triggerEl.addClass('open');
2066         
2067         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2068             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2069         }
2070         
2071         this.el.setXY(xy);
2072         this.focus();
2073         this.fireEvent("show", this);
2074     },
2075     
2076     focus : function(){
2077         return;
2078         if(!this.hidden){
2079             this.doFocus.defer(50, this);
2080         }
2081     },
2082
2083     doFocus : function(){
2084         if(!this.hidden){
2085             this.focusEl.focus();
2086         }
2087     },
2088
2089     /**
2090      * Hides this menu and optionally all parent menus
2091      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2092      */
2093     hide : function(deep){
2094         
2095         this.hideMenuItems();
2096         if(this.el && this.isVisible()){
2097             this.fireEvent("beforehide", this);
2098             if(this.activeItem){
2099                 this.activeItem.deactivate();
2100                 this.activeItem = null;
2101             }
2102             this.triggerEl.removeClass('open');;
2103             this.hidden = true;
2104             this.fireEvent("hide", this);
2105         }
2106         if(deep === true && this.parentMenu){
2107             this.parentMenu.hide(true);
2108         }
2109     },
2110     
2111     onTriggerPress  : function(e)
2112     {
2113         
2114         Roo.log('trigger press');
2115         //Roo.log(e.getTarget());
2116        // Roo.log(this.triggerEl.dom);
2117         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2118             return;
2119         }
2120         
2121         if (this.isVisible()) {
2122             Roo.log('hide');
2123             this.hide();
2124         } else {
2125             Roo.log('show');
2126             this.show(this.triggerEl, false, false);
2127         }
2128         
2129         e.stopEvent();
2130     },
2131     
2132          
2133        
2134     
2135     hideMenuItems : function()
2136     {
2137         //$(backdrop).remove()
2138         Roo.select('.open',true).each(function(aa) {
2139             
2140             aa.removeClass('open');
2141           //var parent = getParent($(this))
2142           //var relatedTarget = { relatedTarget: this }
2143           
2144            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2145           //if (e.isDefaultPrevented()) return
2146            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2147         })
2148     },
2149     addxtypeChild : function (tree, cntr) {
2150         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2151           
2152         this.menuitems.add(comp);
2153         return comp;
2154
2155     },
2156     getEl : function()
2157     {
2158         Roo.log(this.el);
2159         return this.el;
2160     }
2161 });
2162
2163  
2164  /*
2165  * - LGPL
2166  *
2167  * menu item
2168  * 
2169  */
2170
2171
2172 /**
2173  * @class Roo.bootstrap.MenuItem
2174  * @extends Roo.bootstrap.Component
2175  * Bootstrap MenuItem class
2176  * @cfg {String} html the menu label
2177  * @cfg {String} href the link
2178  * @cfg {Boolean} preventDefault (true | false) default true
2179  * @cfg {Boolean} isContainer (true | false) default false
2180  * 
2181  * 
2182  * @constructor
2183  * Create a new MenuItem
2184  * @param {Object} config The config object
2185  */
2186
2187
2188 Roo.bootstrap.MenuItem = function(config){
2189     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2190     this.addEvents({
2191         // raw events
2192         /**
2193          * @event click
2194          * The raw click event for the entire grid.
2195          * @param {Roo.bootstrap.MenuItem} this
2196          * @param {Roo.EventObject} e
2197          */
2198         "click" : true
2199     });
2200 };
2201
2202 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2203     
2204     href : false,
2205     html : false,
2206     preventDefault: true,
2207     isContainer : false,
2208     
2209     getAutoCreate : function(){
2210         
2211         if(this.isContainer){
2212             return {
2213                 tag: 'li',
2214                 cls: 'dropdown-menu-item'
2215             };
2216         }
2217         
2218         var cfg= {
2219             tag: 'li',
2220             cls: 'dropdown-menu-item',
2221             cn: [
2222                     {
2223                         tag : 'a',
2224                         href : '#',
2225                         html : 'Link'
2226                     }
2227                 ]
2228         };
2229         if (this.parent().type == 'treeview') {
2230             cfg.cls = 'treeview-menu';
2231         }
2232         
2233         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2234         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2235         return cfg;
2236     },
2237     
2238     initEvents: function() {
2239         
2240         //this.el.select('a').on('click', this.onClick, this);
2241         
2242     },
2243     onClick : function(e)
2244     {
2245         Roo.log('item on click ');
2246         //if(this.preventDefault){
2247         //    e.preventDefault();
2248         //}
2249         //this.parent().hideMenuItems();
2250         
2251         this.fireEvent('click', this, e);
2252     },
2253     getEl : function()
2254     {
2255         return this.el;
2256     }
2257 });
2258
2259  
2260
2261  /*
2262  * - LGPL
2263  *
2264  * menu separator
2265  * 
2266  */
2267
2268
2269 /**
2270  * @class Roo.bootstrap.MenuSeparator
2271  * @extends Roo.bootstrap.Component
2272  * Bootstrap MenuSeparator class
2273  * 
2274  * @constructor
2275  * Create a new MenuItem
2276  * @param {Object} config The config object
2277  */
2278
2279
2280 Roo.bootstrap.MenuSeparator = function(config){
2281     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2282 };
2283
2284 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2285     
2286     getAutoCreate : function(){
2287         var cfg = {
2288             cls: 'divider',
2289             tag : 'li'
2290         };
2291         
2292         return cfg;
2293     }
2294    
2295 });
2296
2297  
2298
2299  
2300 /*
2301 * Licence: LGPL
2302 */
2303
2304 /**
2305  * @class Roo.bootstrap.Modal
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap Modal class
2308  * @cfg {String} title Title of dialog
2309  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2310  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2311  * @cfg {Boolean} specificTitle default false
2312  * @cfg {Array} buttons Array of buttons or standard button set..
2313  * @cfg {String} buttonPosition (left|right|center) default right
2314  * @cfg {Boolean} animate default true
2315  * @cfg {Boolean} allow_close default true
2316  * 
2317  * @constructor
2318  * Create a new Modal Dialog
2319  * @param {Object} config The config object
2320  */
2321
2322 Roo.bootstrap.Modal = function(config){
2323     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2324     this.addEvents({
2325         // raw events
2326         /**
2327          * @event btnclick
2328          * The raw btnclick event for the button
2329          * @param {Roo.EventObject} e
2330          */
2331         "btnclick" : true
2332     });
2333     this.buttons = this.buttons || [];
2334      
2335     if (this.tmpl) {
2336         this.tmpl = Roo.factory(this.tmpl);
2337     }
2338     
2339 };
2340
2341 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2342     
2343     title : 'test dialog',
2344    
2345     buttons : false,
2346     
2347     // set on load...
2348      
2349     html: false,
2350     
2351     tmp: false,
2352     
2353     specificTitle: false,
2354     
2355     buttonPosition: 'right',
2356     
2357     allow_close : true,
2358     
2359     animate : true,
2360     
2361     
2362      // private
2363     bodyEl:  false,
2364     footerEl:  false,
2365     titleEl:  false,
2366     closeEl:  false,
2367     
2368     
2369     onRender : function(ct, position)
2370     {
2371         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2372      
2373         if(!this.el){
2374             var cfg = Roo.apply({},  this.getAutoCreate());
2375             cfg.id = Roo.id();
2376             //if(!cfg.name){
2377             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2378             //}
2379             //if (!cfg.name.length) {
2380             //    delete cfg.name;
2381            // }
2382             if (this.cls) {
2383                 cfg.cls += ' ' + this.cls;
2384             }
2385             if (this.style) {
2386                 cfg.style = this.style;
2387             }
2388             this.el = Roo.get(document.body).createChild(cfg, position);
2389         }
2390         //var type = this.el.dom.type;
2391         
2392         
2393         
2394         
2395         if(this.tabIndex !== undefined){
2396             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2397         }
2398         
2399         
2400         this.bodyEl = this.el.select('.modal-body',true).first();
2401         this.closeEl = this.el.select('.modal-header .close', true).first();
2402         this.footerEl = this.el.select('.modal-footer',true).first();
2403         this.titleEl = this.el.select('.modal-title',true).first();
2404         
2405         
2406          
2407         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2408         this.maskEl.enableDisplayMode("block");
2409         this.maskEl.hide();
2410         //this.el.addClass("x-dlg-modal");
2411     
2412         if (this.buttons.length) {
2413             Roo.each(this.buttons, function(bb) {
2414                 b = Roo.apply({}, bb);
2415                 b.xns = b.xns || Roo.bootstrap;
2416                 b.xtype = b.xtype || 'Button';
2417                 if (typeof(b.listeners) == 'undefined') {
2418                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2419                 }
2420                 
2421                 var btn = Roo.factory(b);
2422                 
2423                 btn.onRender(this.el.select('.modal-footer div').first());
2424                 
2425             },this);
2426         }
2427         // render the children.
2428         var nitems = [];
2429         
2430         if(typeof(this.items) != 'undefined'){
2431             var items = this.items;
2432             delete this.items;
2433
2434             for(var i =0;i < items.length;i++) {
2435                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2436             }
2437         }
2438         
2439         this.items = nitems;
2440         
2441         // where are these used - they used to be body/close/footer
2442         
2443        
2444         this.initEvents();
2445         //this.el.addClass([this.fieldClass, this.cls]);
2446         
2447     },
2448     getAutoCreate : function(){
2449         
2450         
2451         var bdy = {
2452                 cls : 'modal-body',
2453                 html : this.html || ''
2454         };
2455         
2456         var title = {
2457             tag: 'h4',
2458             cls : 'modal-title',
2459             html : this.title
2460         };
2461         
2462         if(this.specificTitle){
2463             title = this.title;
2464             
2465         };
2466         
2467         var header = [];
2468         if (this.allow_close) {
2469             header.push({
2470                 tag: 'button',
2471                 cls : 'close',
2472                 html : '&times'
2473             });
2474         }
2475         header.push(title);
2476         
2477         var modal = {
2478             cls: "modal",
2479             style : 'display: none',
2480             cn : [
2481                 {
2482                     cls: "modal-dialog",
2483                     cn : [
2484                         {
2485                             cls : "modal-content",
2486                             cn : [
2487                                 {
2488                                     cls : 'modal-header',
2489                                     cn : header
2490                                 },
2491                                 bdy,
2492                                 {
2493                                     cls : 'modal-footer',
2494                                     cn : [
2495                                         {
2496                                             tag: 'div',
2497                                             cls: 'btn-' + this.buttonPosition
2498                                         }
2499                                     ]
2500                                     
2501                                 }
2502                                 
2503                                 
2504                             ]
2505                             
2506                         }
2507                     ]
2508                         
2509                 }
2510             ]
2511         };
2512         
2513         if(this.animate){
2514             modal.cls += ' fade';
2515         }
2516         
2517         return modal;
2518           
2519     },
2520     getChildContainer : function() {
2521          
2522          return this.bodyEl;
2523         
2524     },
2525     getButtonContainer : function() {
2526          return this.el.select('.modal-footer div',true).first();
2527         
2528     },
2529     initEvents : function()
2530     {
2531         if (this.allow_close) {
2532             this.closeEl.on('click', this.hide, this);
2533         }
2534
2535     },
2536     show : function() {
2537         
2538         if (!this.rendered) {
2539             this.render();
2540         }
2541         
2542         this.el.setStyle('display', 'block');
2543         
2544         if(this.animate){
2545             var _this = this;
2546             (function(){ _this.el.addClass('in'); }).defer(50);
2547         }else{
2548             this.el.addClass('in');
2549         }
2550         
2551         // not sure how we can show data in here.. 
2552         //if (this.tmpl) {
2553         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2554         //}
2555         
2556         Roo.get(document.body).addClass("x-body-masked");
2557         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2558         this.maskEl.show();
2559         this.el.setStyle('zIndex', '10001');
2560        
2561         this.fireEvent('show', this);
2562         
2563         
2564     },
2565     hide : function()
2566     {
2567         this.maskEl.hide();
2568         Roo.get(document.body).removeClass("x-body-masked");
2569         this.el.removeClass('in');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2574         }else{
2575             this.el.setStyle('display', 'none');
2576         }
2577         
2578         this.fireEvent('hide', this);
2579     },
2580     
2581     addButton : function(str, cb)
2582     {
2583          
2584         
2585         var b = Roo.apply({}, { html : str } );
2586         b.xns = b.xns || Roo.bootstrap;
2587         b.xtype = b.xtype || 'Button';
2588         if (typeof(b.listeners) == 'undefined') {
2589             b.listeners = { click : cb.createDelegate(this)  };
2590         }
2591         
2592         var btn = Roo.factory(b);
2593            
2594         btn.onRender(this.el.select('.modal-footer div').first());
2595         
2596         return btn;   
2597        
2598     },
2599     
2600     setDefaultButton : function(btn)
2601     {
2602         //this.el.select('.modal-footer').()
2603     },
2604     resizeTo: function(w,h)
2605     {
2606         // skip..
2607     },
2608     setContentSize  : function(w, h)
2609     {
2610         
2611     },
2612     onButtonClick: function(btn,e)
2613     {
2614         //Roo.log([a,b,c]);
2615         this.fireEvent('btnclick', btn.name, e);
2616     },
2617      /**
2618      * Set the title of the Dialog
2619      * @param {String} str new Title
2620      */
2621     setTitle: function(str) {
2622         this.titleEl.dom.innerHTML = str;    
2623     },
2624     /**
2625      * Set the body of the Dialog
2626      * @param {String} str new Title
2627      */
2628     setBody: function(str) {
2629         this.bodyEl.dom.innerHTML = str;    
2630     },
2631     /**
2632      * Set the body of the Dialog using the template
2633      * @param {Obj} data - apply this data to the template and replace the body contents.
2634      */
2635     applyBody: function(obj)
2636     {
2637         if (!this.tmpl) {
2638             Roo.log("Error - using apply Body without a template");
2639             //code
2640         }
2641         this.tmpl.overwrite(this.bodyEl, obj);
2642     }
2643     
2644 });
2645
2646
2647 Roo.apply(Roo.bootstrap.Modal,  {
2648     /**
2649          * Button config that displays a single OK button
2650          * @type Object
2651          */
2652         OK :  [{
2653             name : 'ok',
2654             weight : 'primary',
2655             html : 'OK'
2656         }], 
2657         /**
2658          * Button config that displays Yes and No buttons
2659          * @type Object
2660          */
2661         YESNO : [
2662             {
2663                 name  : 'no',
2664                 html : 'No'
2665             },
2666             {
2667                 name  :'yes',
2668                 weight : 'primary',
2669                 html : 'Yes'
2670             }
2671         ],
2672         
2673         /**
2674          * Button config that displays OK and Cancel buttons
2675          * @type Object
2676          */
2677         OKCANCEL : [
2678             {
2679                name : 'cancel',
2680                 html : 'Cancel'
2681             },
2682             {
2683                 name : 'ok',
2684                 weight : 'primary',
2685                 html : 'OK'
2686             }
2687         ],
2688         /**
2689          * Button config that displays Yes, No and Cancel buttons
2690          * @type Object
2691          */
2692         YESNOCANCEL : [
2693             {
2694                 name : 'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             },
2698             {
2699                 name : 'no',
2700                 html : 'No'
2701             },
2702             {
2703                 name : 'cancel',
2704                 html : 'Cancel'
2705             }
2706         ]
2707 });
2708  
2709  /*
2710  * - LGPL
2711  *
2712  * messagebox - can be used as a replace
2713  * 
2714  */
2715 /**
2716  * @class Roo.MessageBox
2717  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2718  * Example usage:
2719  *<pre><code>
2720 // Basic alert:
2721 Roo.Msg.alert('Status', 'Changes saved successfully.');
2722
2723 // Prompt for user data:
2724 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2725     if (btn == 'ok'){
2726         // process text value...
2727     }
2728 });
2729
2730 // Show a dialog using config options:
2731 Roo.Msg.show({
2732    title:'Save Changes?',
2733    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2734    buttons: Roo.Msg.YESNOCANCEL,
2735    fn: processResult,
2736    animEl: 'elId'
2737 });
2738 </code></pre>
2739  * @singleton
2740  */
2741 Roo.bootstrap.MessageBox = function(){
2742     var dlg, opt, mask, waitTimer;
2743     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2744     var buttons, activeTextEl, bwidth;
2745
2746     
2747     // private
2748     var handleButton = function(button){
2749         dlg.hide();
2750         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2751     };
2752
2753     // private
2754     var handleHide = function(){
2755         if(opt && opt.cls){
2756             dlg.el.removeClass(opt.cls);
2757         }
2758         //if(waitTimer){
2759         //    Roo.TaskMgr.stop(waitTimer);
2760         //    waitTimer = null;
2761         //}
2762     };
2763
2764     // private
2765     var updateButtons = function(b){
2766         var width = 0;
2767         if(!b){
2768             buttons["ok"].hide();
2769             buttons["cancel"].hide();
2770             buttons["yes"].hide();
2771             buttons["no"].hide();
2772             //dlg.footer.dom.style.display = 'none';
2773             return width;
2774         }
2775         dlg.footerEl.dom.style.display = '';
2776         for(var k in buttons){
2777             if(typeof buttons[k] != "function"){
2778                 if(b[k]){
2779                     buttons[k].show();
2780                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2781                     width += buttons[k].el.getWidth()+15;
2782                 }else{
2783                     buttons[k].hide();
2784                 }
2785             }
2786         }
2787         return width;
2788     };
2789
2790     // private
2791     var handleEsc = function(d, k, e){
2792         if(opt && opt.closable !== false){
2793             dlg.hide();
2794         }
2795         if(e){
2796             e.stopEvent();
2797         }
2798     };
2799
2800     return {
2801         /**
2802          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2803          * @return {Roo.BasicDialog} The BasicDialog element
2804          */
2805         getDialog : function(){
2806            if(!dlg){
2807                 dlg = new Roo.bootstrap.Modal( {
2808                     //draggable: true,
2809                     //resizable:false,
2810                     //constraintoviewport:false,
2811                     //fixedcenter:true,
2812                     //collapsible : false,
2813                     //shim:true,
2814                     //modal: true,
2815                   //  width:400,
2816                   //  height:100,
2817                     //buttonAlign:"center",
2818                     closeClick : function(){
2819                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2820                             handleButton("no");
2821                         }else{
2822                             handleButton("cancel");
2823                         }
2824                     }
2825                 });
2826                 dlg.render();
2827                 dlg.on("hide", handleHide);
2828                 mask = dlg.mask;
2829                 //dlg.addKeyListener(27, handleEsc);
2830                 buttons = {};
2831                 this.buttons = buttons;
2832                 var bt = this.buttonText;
2833                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2834                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2835                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2836                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2837                 Roo.log(buttons)
2838                 bodyEl = dlg.bodyEl.createChild({
2839
2840                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2841                         '<textarea class="roo-mb-textarea"></textarea>' +
2842                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2843                 });
2844                 msgEl = bodyEl.dom.firstChild;
2845                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2846                 textboxEl.enableDisplayMode();
2847                 textboxEl.addKeyListener([10,13], function(){
2848                     if(dlg.isVisible() && opt && opt.buttons){
2849                         if(opt.buttons.ok){
2850                             handleButton("ok");
2851                         }else if(opt.buttons.yes){
2852                             handleButton("yes");
2853                         }
2854                     }
2855                 });
2856                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2857                 textareaEl.enableDisplayMode();
2858                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2859                 progressEl.enableDisplayMode();
2860                 var pf = progressEl.dom.firstChild;
2861                 if (pf) {
2862                     pp = Roo.get(pf.firstChild);
2863                     pp.setHeight(pf.offsetHeight);
2864                 }
2865                 
2866             }
2867             return dlg;
2868         },
2869
2870         /**
2871          * Updates the message box body text
2872          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2873          * the XHTML-compliant non-breaking space character '&amp;#160;')
2874          * @return {Roo.MessageBox} This message box
2875          */
2876         updateText : function(text){
2877             if(!dlg.isVisible() && !opt.width){
2878                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2879             }
2880             msgEl.innerHTML = text || '&#160;';
2881       
2882             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2883             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2884             var w = Math.max(
2885                     Math.min(opt.width || cw , this.maxWidth), 
2886                     Math.max(opt.minWidth || this.minWidth, bwidth)
2887             );
2888             if(opt.prompt){
2889                 activeTextEl.setWidth(w);
2890             }
2891             if(dlg.isVisible()){
2892                 dlg.fixedcenter = false;
2893             }
2894             // to big, make it scroll. = But as usual stupid IE does not support
2895             // !important..
2896             
2897             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2898                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2899                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2900             } else {
2901                 bodyEl.dom.style.height = '';
2902                 bodyEl.dom.style.overflowY = '';
2903             }
2904             if (cw > w) {
2905                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2906             } else {
2907                 bodyEl.dom.style.overflowX = '';
2908             }
2909             
2910             dlg.setContentSize(w, bodyEl.getHeight());
2911             if(dlg.isVisible()){
2912                 dlg.fixedcenter = true;
2913             }
2914             return this;
2915         },
2916
2917         /**
2918          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2919          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2920          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2921          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2922          * @return {Roo.MessageBox} This message box
2923          */
2924         updateProgress : function(value, text){
2925             if(text){
2926                 this.updateText(text);
2927             }
2928             if (pp) { // weird bug on my firefox - for some reason this is not defined
2929                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2930             }
2931             return this;
2932         },        
2933
2934         /**
2935          * Returns true if the message box is currently displayed
2936          * @return {Boolean} True if the message box is visible, else false
2937          */
2938         isVisible : function(){
2939             return dlg && dlg.isVisible();  
2940         },
2941
2942         /**
2943          * Hides the message box if it is displayed
2944          */
2945         hide : function(){
2946             if(this.isVisible()){
2947                 dlg.hide();
2948             }  
2949         },
2950
2951         /**
2952          * Displays a new message box, or reinitializes an existing message box, based on the config options
2953          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2954          * The following config object properties are supported:
2955          * <pre>
2956 Property    Type             Description
2957 ----------  ---------------  ------------------------------------------------------------------------------------
2958 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2959                                    closes (defaults to undefined)
2960 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2961                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2962 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2963                                    progress and wait dialogs will ignore this property and always hide the
2964                                    close button as they can only be closed programmatically.
2965 cls               String           A custom CSS class to apply to the message box element
2966 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2967                                    displayed (defaults to 75)
2968 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2969                                    function will be btn (the name of the button that was clicked, if applicable,
2970                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2971                                    Progress and wait dialogs will ignore this option since they do not respond to
2972                                    user actions and can only be closed programmatically, so any required function
2973                                    should be called by the same code after it closes the dialog.
2974 icon              String           A CSS class that provides a background image to be used as an icon for
2975                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2976 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2977 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2978 modal             Boolean          False to allow user interaction with the page while the message box is
2979                                    displayed (defaults to true)
2980 msg               String           A string that will replace the existing message box body text (defaults
2981                                    to the XHTML-compliant non-breaking space character '&#160;')
2982 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2983 progress          Boolean          True to display a progress bar (defaults to false)
2984 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2985 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2986 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2987 title             String           The title text
2988 value             String           The string value to set into the active textbox element if displayed
2989 wait              Boolean          True to display a progress bar (defaults to false)
2990 width             Number           The width of the dialog in pixels
2991 </pre>
2992          *
2993          * Example usage:
2994          * <pre><code>
2995 Roo.Msg.show({
2996    title: 'Address',
2997    msg: 'Please enter your address:',
2998    width: 300,
2999    buttons: Roo.MessageBox.OKCANCEL,
3000    multiline: true,
3001    fn: saveAddress,
3002    animEl: 'addAddressBtn'
3003 });
3004 </code></pre>
3005          * @param {Object} config Configuration options
3006          * @return {Roo.MessageBox} This message box
3007          */
3008         show : function(options)
3009         {
3010             
3011             // this causes nightmares if you show one dialog after another
3012             // especially on callbacks..
3013              
3014             if(this.isVisible()){
3015                 
3016                 this.hide();
3017                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3018                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3019                 Roo.log("New Dialog Message:" +  options.msg )
3020                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3021                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3022                 
3023             }
3024             var d = this.getDialog();
3025             opt = options;
3026             d.setTitle(opt.title || "&#160;");
3027             d.closeEl.setDisplayed(opt.closable !== false);
3028             activeTextEl = textboxEl;
3029             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3030             if(opt.prompt){
3031                 if(opt.multiline){
3032                     textboxEl.hide();
3033                     textareaEl.show();
3034                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3035                         opt.multiline : this.defaultTextHeight);
3036                     activeTextEl = textareaEl;
3037                 }else{
3038                     textboxEl.show();
3039                     textareaEl.hide();
3040                 }
3041             }else{
3042                 textboxEl.hide();
3043                 textareaEl.hide();
3044             }
3045             progressEl.setDisplayed(opt.progress === true);
3046             this.updateProgress(0);
3047             activeTextEl.dom.value = opt.value || "";
3048             if(opt.prompt){
3049                 dlg.setDefaultButton(activeTextEl);
3050             }else{
3051                 var bs = opt.buttons;
3052                 var db = null;
3053                 if(bs && bs.ok){
3054                     db = buttons["ok"];
3055                 }else if(bs && bs.yes){
3056                     db = buttons["yes"];
3057                 }
3058                 dlg.setDefaultButton(db);
3059             }
3060             bwidth = updateButtons(opt.buttons);
3061             this.updateText(opt.msg);
3062             if(opt.cls){
3063                 d.el.addClass(opt.cls);
3064             }
3065             d.proxyDrag = opt.proxyDrag === true;
3066             d.modal = opt.modal !== false;
3067             d.mask = opt.modal !== false ? mask : false;
3068             if(!d.isVisible()){
3069                 // force it to the end of the z-index stack so it gets a cursor in FF
3070                 document.body.appendChild(dlg.el.dom);
3071                 d.animateTarget = null;
3072                 d.show(options.animEl);
3073             }
3074             return this;
3075         },
3076
3077         /**
3078          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3079          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3080          * and closing the message box when the process is complete.
3081          * @param {String} title The title bar text
3082          * @param {String} msg The message box body text
3083          * @return {Roo.MessageBox} This message box
3084          */
3085         progress : function(title, msg){
3086             this.show({
3087                 title : title,
3088                 msg : msg,
3089                 buttons: false,
3090                 progress:true,
3091                 closable:false,
3092                 minWidth: this.minProgressWidth,
3093                 modal : true
3094             });
3095             return this;
3096         },
3097
3098         /**
3099          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3100          * If a callback function is passed it will be called after the user clicks the button, and the
3101          * id of the button that was clicked will be passed as the only parameter to the callback
3102          * (could also be the top-right close button).
3103          * @param {String} title The title bar text
3104          * @param {String} msg The message box body text
3105          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3106          * @param {Object} scope (optional) The scope of the callback function
3107          * @return {Roo.MessageBox} This message box
3108          */
3109         alert : function(title, msg, fn, scope){
3110             this.show({
3111                 title : title,
3112                 msg : msg,
3113                 buttons: this.OK,
3114                 fn: fn,
3115                 scope : scope,
3116                 modal : true
3117             });
3118             return this;
3119         },
3120
3121         /**
3122          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3123          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3124          * You are responsible for closing the message box when the process is complete.
3125          * @param {String} msg The message box body text
3126          * @param {String} title (optional) The title bar text
3127          * @return {Roo.MessageBox} This message box
3128          */
3129         wait : function(msg, title){
3130             this.show({
3131                 title : title,
3132                 msg : msg,
3133                 buttons: false,
3134                 closable:false,
3135                 progress:true,
3136                 modal:true,
3137                 width:300,
3138                 wait:true
3139             });
3140             waitTimer = Roo.TaskMgr.start({
3141                 run: function(i){
3142                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3143                 },
3144                 interval: 1000
3145             });
3146             return this;
3147         },
3148
3149         /**
3150          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3151          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3152          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3153          * @param {String} title The title bar text
3154          * @param {String} msg The message box body text
3155          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3156          * @param {Object} scope (optional) The scope of the callback function
3157          * @return {Roo.MessageBox} This message box
3158          */
3159         confirm : function(title, msg, fn, scope){
3160             this.show({
3161                 title : title,
3162                 msg : msg,
3163                 buttons: this.YESNO,
3164                 fn: fn,
3165                 scope : scope,
3166                 modal : true
3167             });
3168             return this;
3169         },
3170
3171         /**
3172          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3173          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3174          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3175          * (could also be the top-right close button) and the text that was entered will be passed as the two
3176          * parameters to the callback.
3177          * @param {String} title The title bar text
3178          * @param {String} msg The message box body text
3179          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3180          * @param {Object} scope (optional) The scope of the callback function
3181          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3182          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3183          * @return {Roo.MessageBox} This message box
3184          */
3185         prompt : function(title, msg, fn, scope, multiline){
3186             this.show({
3187                 title : title,
3188                 msg : msg,
3189                 buttons: this.OKCANCEL,
3190                 fn: fn,
3191                 minWidth:250,
3192                 scope : scope,
3193                 prompt:true,
3194                 multiline: multiline,
3195                 modal : true
3196             });
3197             return this;
3198         },
3199
3200         /**
3201          * Button config that displays a single OK button
3202          * @type Object
3203          */
3204         OK : {ok:true},
3205         /**
3206          * Button config that displays Yes and No buttons
3207          * @type Object
3208          */
3209         YESNO : {yes:true, no:true},
3210         /**
3211          * Button config that displays OK and Cancel buttons
3212          * @type Object
3213          */
3214         OKCANCEL : {ok:true, cancel:true},
3215         /**
3216          * Button config that displays Yes, No and Cancel buttons
3217          * @type Object
3218          */
3219         YESNOCANCEL : {yes:true, no:true, cancel:true},
3220
3221         /**
3222          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3223          * @type Number
3224          */
3225         defaultTextHeight : 75,
3226         /**
3227          * The maximum width in pixels of the message box (defaults to 600)
3228          * @type Number
3229          */
3230         maxWidth : 600,
3231         /**
3232          * The minimum width in pixels of the message box (defaults to 100)
3233          * @type Number
3234          */
3235         minWidth : 100,
3236         /**
3237          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3238          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3239          * @type Number
3240          */
3241         minProgressWidth : 250,
3242         /**
3243          * An object containing the default button text strings that can be overriden for localized language support.
3244          * Supported properties are: ok, cancel, yes and no.
3245          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3246          * @type Object
3247          */
3248         buttonText : {
3249             ok : "OK",
3250             cancel : "Cancel",
3251             yes : "Yes",
3252             no : "No"
3253         }
3254     };
3255 }();
3256
3257 /**
3258  * Shorthand for {@link Roo.MessageBox}
3259  */
3260 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3261 Roo.Msg = Roo.Msg || Roo.MessageBox;
3262 /*
3263  * - LGPL
3264  *
3265  * navbar
3266  * 
3267  */
3268
3269 /**
3270  * @class Roo.bootstrap.Navbar
3271  * @extends Roo.bootstrap.Component
3272  * Bootstrap Navbar class
3273
3274  * @constructor
3275  * Create a new Navbar
3276  * @param {Object} config The config object
3277  */
3278
3279
3280 Roo.bootstrap.Navbar = function(config){
3281     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3282     
3283 };
3284
3285 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3286     
3287     
3288    
3289     // private
3290     navItems : false,
3291     loadMask : false,
3292     
3293     
3294     getAutoCreate : function(){
3295         
3296         
3297         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3298         
3299     },
3300     
3301     initEvents :function ()
3302     {
3303         //Roo.log(this.el.select('.navbar-toggle',true));
3304         this.el.select('.navbar-toggle',true).on('click', function() {
3305            // Roo.log('click');
3306             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3307         }, this);
3308         
3309         var mark = {
3310             tag: "div",
3311             cls:"x-dlg-mask"
3312         }
3313         
3314         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3315         
3316         var size = this.el.getSize();
3317         this.maskEl.setSize(size.width, size.height);
3318         this.maskEl.enableDisplayMode("block");
3319         this.maskEl.hide();
3320         
3321         if(this.loadMask){
3322             this.maskEl.show();
3323         }
3324     },
3325     
3326     
3327     getChildContainer : function()
3328     {
3329         if (this.el.select('.collapse').getCount()) {
3330             return this.el.select('.collapse',true).first();
3331         }
3332         
3333         return this.el;
3334     },
3335     
3336     mask : function()
3337     {
3338         this.maskEl.show();
3339     },
3340     
3341     unmask : function()
3342     {
3343         this.maskEl.hide();
3344     } 
3345     
3346     
3347     
3348     
3349 });
3350
3351
3352
3353  
3354
3355  /*
3356  * - LGPL
3357  *
3358  * navbar
3359  * 
3360  */
3361
3362 /**
3363  * @class Roo.bootstrap.NavSimplebar
3364  * @extends Roo.bootstrap.Navbar
3365  * Bootstrap Sidebar class
3366  *
3367  * @cfg {Boolean} inverse is inverted color
3368  * 
3369  * @cfg {String} type (nav | pills | tabs)
3370  * @cfg {Boolean} arrangement stacked | justified
3371  * @cfg {String} align (left | right) alignment
3372  * 
3373  * @cfg {Boolean} main (true|false) main nav bar? default false
3374  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3375  * 
3376  * @cfg {String} tag (header|footer|nav|div) default is nav 
3377
3378  * 
3379  * 
3380  * 
3381  * @constructor
3382  * Create a new Sidebar
3383  * @param {Object} config The config object
3384  */
3385
3386
3387 Roo.bootstrap.NavSimplebar = function(config){
3388     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3389 };
3390
3391 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3392     
3393     inverse: false,
3394     
3395     type: false,
3396     arrangement: '',
3397     align : false,
3398     
3399     
3400     
3401     main : false,
3402     
3403     
3404     tag : false,
3405     
3406     
3407     getAutoCreate : function(){
3408         
3409         
3410         var cfg = {
3411             tag : this.tag || 'div',
3412             cls : 'navbar'
3413         };
3414           
3415         
3416         cfg.cn = [
3417             {
3418                 cls: 'nav',
3419                 tag : 'ul'
3420             }
3421         ];
3422         
3423          
3424         this.type = this.type || 'nav';
3425         if (['tabs','pills'].indexOf(this.type)!==-1) {
3426             cfg.cn[0].cls += ' nav-' + this.type
3427         
3428         
3429         } else {
3430             if (this.type!=='nav') {
3431                 Roo.log('nav type must be nav/tabs/pills')
3432             }
3433             cfg.cn[0].cls += ' navbar-nav'
3434         }
3435         
3436         
3437         
3438         
3439         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3440             cfg.cn[0].cls += ' nav-' + this.arrangement;
3441         }
3442         
3443         
3444         if (this.align === 'right') {
3445             cfg.cn[0].cls += ' navbar-right';
3446         }
3447         
3448         if (this.inverse) {
3449             cfg.cls += ' navbar-inverse';
3450             
3451         }
3452         
3453         
3454         return cfg;
3455     
3456         
3457     }
3458     
3459     
3460     
3461 });
3462
3463
3464
3465  
3466
3467  
3468        /*
3469  * - LGPL
3470  *
3471  * navbar
3472  * 
3473  */
3474
3475 /**
3476  * @class Roo.bootstrap.NavHeaderbar
3477  * @extends Roo.bootstrap.NavSimplebar
3478  * Bootstrap Sidebar class
3479  *
3480  * @cfg {String} brand what is brand
3481  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3482  * @cfg {String} brand_href href of the brand
3483  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3484  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3485  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3486  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3487  * 
3488  * @constructor
3489  * Create a new Sidebar
3490  * @param {Object} config The config object
3491  */
3492
3493
3494 Roo.bootstrap.NavHeaderbar = function(config){
3495     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3496       
3497 };
3498
3499 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3500     
3501     position: '',
3502     brand: '',
3503     brand_href: false,
3504     srButton : true,
3505     autohide : false,
3506     desktopCenter : false,
3507    
3508     
3509     getAutoCreate : function(){
3510         
3511         var   cfg = {
3512             tag: this.nav || 'nav',
3513             cls: 'navbar',
3514             role: 'navigation',
3515             cn: []
3516         };
3517         
3518         var cn = cfg.cn;
3519         if (this.desktopCenter) {
3520             cn.push({cls : 'container', cn : []});
3521             cn = cn[0].cn;
3522         }
3523         
3524         if(this.srButton){
3525             cn.push({
3526                 tag: 'div',
3527                 cls: 'navbar-header',
3528                 cn: [
3529                     {
3530                         tag: 'button',
3531                         type: 'button',
3532                         cls: 'navbar-toggle',
3533                         'data-toggle': 'collapse',
3534                         cn: [
3535                             {
3536                                 tag: 'span',
3537                                 cls: 'sr-only',
3538                                 html: 'Toggle navigation'
3539                             },
3540                             {
3541                                 tag: 'span',
3542                                 cls: 'icon-bar'
3543                             },
3544                             {
3545                                 tag: 'span',
3546                                 cls: 'icon-bar'
3547                             },
3548                             {
3549                                 tag: 'span',
3550                                 cls: 'icon-bar'
3551                             }
3552                         ]
3553                     }
3554                 ]
3555             });
3556         }
3557         
3558         cn.push({
3559             tag: 'div',
3560             cls: 'collapse navbar-collapse',
3561             cn : []
3562         });
3563         
3564         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3565         
3566         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3567             cfg.cls += ' navbar-' + this.position;
3568             
3569             // tag can override this..
3570             
3571             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3572         }
3573         
3574         if (this.brand !== '') {
3575             cn[0].cn.push({
3576                 tag: 'a',
3577                 href: this.brand_href ? this.brand_href : '#',
3578                 cls: 'navbar-brand',
3579                 cn: [
3580                 this.brand
3581                 ]
3582             });
3583         }
3584         
3585         if(this.main){
3586             cfg.cls += ' main-nav';
3587         }
3588         
3589         
3590         return cfg;
3591
3592         
3593     },
3594     getHeaderChildContainer : function()
3595     {
3596         if (this.el.select('.navbar-header').getCount()) {
3597             return this.el.select('.navbar-header',true).first();
3598         }
3599         
3600         return this.getChildContainer();
3601     },
3602     
3603     
3604     initEvents : function()
3605     {
3606         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3607         
3608         if (this.autohide) {
3609             
3610             var prevScroll = 0;
3611             var ft = this.el;
3612             
3613             Roo.get(document).on('scroll',function(e) {
3614                 var ns = Roo.get(document).getScroll().top;
3615                 var os = prevScroll;
3616                 prevScroll = ns;
3617                 
3618                 if(ns > os){
3619                     ft.removeClass('slideDown');
3620                     ft.addClass('slideUp');
3621                     return;
3622                 }
3623                 ft.removeClass('slideUp');
3624                 ft.addClass('slideDown');
3625                  
3626               
3627           },this);
3628         }
3629     }    
3630     
3631 });
3632
3633
3634
3635  
3636
3637  /*
3638  * - LGPL
3639  *
3640  * navbar
3641  * 
3642  */
3643
3644 /**
3645  * @class Roo.bootstrap.NavSidebar
3646  * @extends Roo.bootstrap.Navbar
3647  * Bootstrap Sidebar class
3648  * 
3649  * @constructor
3650  * Create a new Sidebar
3651  * @param {Object} config The config object
3652  */
3653
3654
3655 Roo.bootstrap.NavSidebar = function(config){
3656     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3657 };
3658
3659 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3660     
3661     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3662     
3663     getAutoCreate : function(){
3664         
3665         
3666         return  {
3667             tag: 'div',
3668             cls: 'sidebar sidebar-nav'
3669         };
3670     
3671         
3672     }
3673     
3674     
3675     
3676 });
3677
3678
3679
3680  
3681
3682  /*
3683  * - LGPL
3684  *
3685  * nav group
3686  * 
3687  */
3688
3689 /**
3690  * @class Roo.bootstrap.NavGroup
3691  * @extends Roo.bootstrap.Component
3692  * Bootstrap NavGroup class
3693  * @cfg {String} align (left|right)
3694  * @cfg {Boolean} inverse
3695  * @cfg {String} type (nav|pills|tab) default nav
3696  * @cfg {String} navId - reference Id for navbar.
3697
3698  * 
3699  * @constructor
3700  * Create a new nav group
3701  * @param {Object} config The config object
3702  */
3703
3704 Roo.bootstrap.NavGroup = function(config){
3705     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3706     this.navItems = [];
3707    
3708     Roo.bootstrap.NavGroup.register(this);
3709      this.addEvents({
3710         /**
3711              * @event changed
3712              * Fires when the active item changes
3713              * @param {Roo.bootstrap.NavGroup} this
3714              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3715              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3716          */
3717         'changed': true
3718      });
3719     
3720 };
3721
3722 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3723     
3724     align: '',
3725     inverse: false,
3726     form: false,
3727     type: 'nav',
3728     navId : '',
3729     // private
3730     
3731     navItems : false, 
3732     
3733     getAutoCreate : function()
3734     {
3735         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3736         
3737         cfg = {
3738             tag : 'ul',
3739             cls: 'nav' 
3740         }
3741         
3742         if (['tabs','pills'].indexOf(this.type)!==-1) {
3743             cfg.cls += ' nav-' + this.type
3744         } else {
3745             if (this.type!=='nav') {
3746                 Roo.log('nav type must be nav/tabs/pills')
3747             }
3748             cfg.cls += ' navbar-nav'
3749         }
3750         
3751         if (this.parent().sidebar) {
3752             cfg = {
3753                 tag: 'ul',
3754                 cls: 'dashboard-menu sidebar-menu'
3755             }
3756             
3757             return cfg;
3758         }
3759         
3760         if (this.form === true) {
3761             cfg = {
3762                 tag: 'form',
3763                 cls: 'navbar-form'
3764             }
3765             
3766             if (this.align === 'right') {
3767                 cfg.cls += ' navbar-right';
3768             } else {
3769                 cfg.cls += ' navbar-left';
3770             }
3771         }
3772         
3773         if (this.align === 'right') {
3774             cfg.cls += ' navbar-right';
3775         }
3776         
3777         if (this.inverse) {
3778             cfg.cls += ' navbar-inverse';
3779             
3780         }
3781         
3782         
3783         return cfg;
3784     },
3785     /**
3786     * sets the active Navigation item
3787     * @param {Roo.bootstrap.NavItem} the new current navitem
3788     */
3789     setActiveItem : function(item)
3790     {
3791         var prev = false;
3792         Roo.each(this.navItems, function(v){
3793             if (v == item) {
3794                 return ;
3795             }
3796             if (v.isActive()) {
3797                 v.setActive(false, true);
3798                 prev = v;
3799                 
3800             }
3801             
3802         });
3803
3804         item.setActive(true, true);
3805         this.fireEvent('changed', this, item, prev);
3806         
3807         
3808     },
3809     /**
3810     * gets the active Navigation item
3811     * @return {Roo.bootstrap.NavItem} the current navitem
3812     */
3813     getActive : function()
3814     {
3815         
3816         var prev = false;
3817         Roo.each(this.navItems, function(v){
3818             
3819             if (v.isActive()) {
3820                 prev = v;
3821                 
3822             }
3823             
3824         });
3825         return prev;
3826     },
3827     
3828     indexOfNav : function()
3829     {
3830         
3831         var prev = false;
3832         Roo.each(this.navItems, function(v,i){
3833             
3834             if (v.isActive()) {
3835                 prev = i;
3836                 
3837             }
3838             
3839         });
3840         return prev;
3841     },
3842     /**
3843     * adds a Navigation item
3844     * @param {Roo.bootstrap.NavItem} the navitem to add
3845     */
3846     addItem : function(cfg)
3847     {
3848         var cn = new Roo.bootstrap.NavItem(cfg);
3849         this.register(cn);
3850         cn.parentId = this.id;
3851         cn.onRender(this.el, null);
3852         return cn;
3853     },
3854     /**
3855     * register a Navigation item
3856     * @param {Roo.bootstrap.NavItem} the navitem to add
3857     */
3858     register : function(item)
3859     {
3860         this.navItems.push( item);
3861         item.navId = this.navId;
3862     
3863     },
3864     
3865     /**
3866     * clear all the Navigation item
3867     */
3868    
3869     clearAll : function()
3870     {
3871         this.navItems = [];
3872         this.el.dom.innerHTML = '';
3873     },
3874     
3875     getNavItem: function(tabId)
3876     {
3877         var ret = false;
3878         Roo.each(this.navItems, function(e) {
3879             if (e.tabId == tabId) {
3880                ret =  e;
3881                return false;
3882             }
3883             return true;
3884             
3885         });
3886         return ret;
3887     },
3888     
3889     setActiveNext : function()
3890     {
3891         var i = this.indexOfNav(this.getActive());
3892         if (i > this.navItems.length) {
3893             return;
3894         }
3895         this.setActiveItem(this.navItems[i+1]);
3896     },
3897     setActivePrev : function()
3898     {
3899         var i = this.indexOfNav(this.getActive());
3900         if (i  < 1) {
3901             return;
3902         }
3903         this.setActiveItem(this.navItems[i-1]);
3904     },
3905     clearWasActive : function(except) {
3906         Roo.each(this.navItems, function(e) {
3907             if (e.tabId != except.tabId && e.was_active) {
3908                e.was_active = false;
3909                return false;
3910             }
3911             return true;
3912             
3913         });
3914     },
3915     getWasActive : function ()
3916     {
3917         var r = false;
3918         Roo.each(this.navItems, function(e) {
3919             if (e.was_active) {
3920                r = e;
3921                return false;
3922             }
3923             return true;
3924             
3925         });
3926         return r;
3927     }
3928     
3929     
3930 });
3931
3932  
3933 Roo.apply(Roo.bootstrap.NavGroup, {
3934     
3935     groups: {},
3936      /**
3937     * register a Navigation Group
3938     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3939     */
3940     register : function(navgrp)
3941     {
3942         this.groups[navgrp.navId] = navgrp;
3943         
3944     },
3945     /**
3946     * fetch a Navigation Group based on the navigation ID
3947     * @param {string} the navgroup to add
3948     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3949     */
3950     get: function(navId) {
3951         if (typeof(this.groups[navId]) == 'undefined') {
3952             return false;
3953             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3954         }
3955         return this.groups[navId] ;
3956     }
3957     
3958     
3959     
3960 });
3961
3962  /*
3963  * - LGPL
3964  *
3965  * row
3966  * 
3967  */
3968
3969 /**
3970  * @class Roo.bootstrap.NavItem
3971  * @extends Roo.bootstrap.Component
3972  * Bootstrap Navbar.NavItem class
3973  * @cfg {String} href  link to
3974  * @cfg {String} html content of button
3975  * @cfg {String} badge text inside badge
3976  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3977  * @cfg {String} glyphicon name of glyphicon
3978  * @cfg {String} icon name of font awesome icon
3979  * @cfg {Boolean} active Is item active
3980  * @cfg {Boolean} disabled Is item disabled
3981  
3982  * @cfg {Boolean} preventDefault (true | false) default false
3983  * @cfg {String} tabId the tab that this item activates.
3984  * @cfg {String} tagtype (a|span) render as a href or span?
3985  * @cfg {Boolean} animateRef (true|false) link to element default false  
3986   
3987  * @constructor
3988  * Create a new Navbar Item
3989  * @param {Object} config The config object
3990  */
3991 Roo.bootstrap.NavItem = function(config){
3992     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3993     this.addEvents({
3994         // raw events
3995         /**
3996          * @event click
3997          * The raw click event for the entire grid.
3998          * @param {Roo.EventObject} e
3999          */
4000         "click" : true,
4001          /**
4002             * @event changed
4003             * Fires when the active item active state changes
4004             * @param {Roo.bootstrap.NavItem} this
4005             * @param {boolean} state the new state
4006              
4007          */
4008         'changed': true,
4009         /**
4010             * @event scrollto
4011             * Fires when scroll to element
4012             * @param {Roo.bootstrap.NavItem} this
4013             * @param {Object} options
4014             * @param {Roo.EventObject} e
4015              
4016          */
4017         'scrollto': true
4018     });
4019    
4020 };
4021
4022 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4023     
4024     href: false,
4025     html: '',
4026     badge: '',
4027     icon: false,
4028     glyphicon: false,
4029     active: false,
4030     preventDefault : false,
4031     tabId : false,
4032     tagtype : 'a',
4033     disabled : false,
4034     animateRef : false,
4035     was_active : false,
4036     
4037     getAutoCreate : function(){
4038          
4039         var cfg = {
4040             tag: 'li',
4041             cls: 'nav-item'
4042             
4043         }
4044         if (this.active) {
4045             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4046         }
4047         if (this.disabled) {
4048             cfg.cls += ' disabled';
4049         }
4050         
4051         if (this.href || this.html || this.glyphicon || this.icon) {
4052             cfg.cn = [
4053                 {
4054                     tag: this.tagtype,
4055                     href : this.href || "#",
4056                     html: this.html || ''
4057                 }
4058             ];
4059             
4060             if (this.icon) {
4061                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4062             }
4063
4064             if(this.glyphicon) {
4065                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4066             }
4067             
4068             if (this.menu) {
4069                 
4070                 cfg.cn[0].html += " <span class='caret'></span>";
4071              
4072             }
4073             
4074             if (this.badge !== '') {
4075                  
4076                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4077             }
4078         }
4079         
4080         
4081         
4082         return cfg;
4083     },
4084     initEvents: function() 
4085     {
4086         if (typeof (this.menu) != 'undefined') {
4087             this.menu.parentType = this.xtype;
4088             this.menu.triggerEl = this.el;
4089             this.menu = this.addxtype(Roo.apply({}, this.menu));
4090         }
4091         
4092         this.el.select('a',true).on('click', this.onClick, this);
4093         
4094         if(this.tagtype == 'span'){
4095             this.el.select('span',true).on('click', this.onClick, this);
4096         }
4097        
4098         // at this point parent should be available..
4099         this.parent().register(this);
4100     },
4101     
4102     onClick : function(e)
4103     {
4104         if(
4105                 this.preventDefault || 
4106                 this.href == '#' 
4107         ){
4108             
4109             e.preventDefault();
4110         }
4111         
4112         if (this.disabled) {
4113             return;
4114         }
4115         
4116         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4117         if (tg && tg.transition) {
4118             Roo.log("waiting for the transitionend");
4119             return;
4120         }
4121         
4122         
4123         
4124         //Roo.log("fire event clicked");
4125         if(this.fireEvent('click', this, e) === false){
4126             return;
4127         };
4128         
4129         if(this.tagtype == 'span'){
4130             return;
4131         }
4132         
4133         //Roo.log(this.href);
4134         var ael = this.el.select('a',true).first();
4135         //Roo.log(ael);
4136         
4137         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4138             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4139             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4140                 return; // ignore... - it's a 'hash' to another page.
4141             }
4142             
4143             e.preventDefault();
4144             this.scrollToElement(e);
4145         }
4146         
4147         
4148         var p =  this.parent();
4149    
4150         if (['tabs','pills'].indexOf(p.type)!==-1) {
4151             if (typeof(p.setActiveItem) !== 'undefined') {
4152                 p.setActiveItem(this);
4153             }
4154         }
4155         
4156         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4157         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4158             // remove the collapsed menu expand...
4159             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4160         }
4161     },
4162     
4163     isActive: function () {
4164         return this.active
4165     },
4166     setActive : function(state, fire, is_was_active)
4167     {
4168         if (this.active && !state & this.navId) {
4169             this.was_active = true;
4170             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4171             if (nv) {
4172                 nv.clearWasActive(this);
4173             }
4174             
4175         }
4176         this.active = state;
4177         
4178         if (!state ) {
4179             this.el.removeClass('active');
4180         } else if (!this.el.hasClass('active')) {
4181             this.el.addClass('active');
4182         }
4183         if (fire) {
4184             this.fireEvent('changed', this, state);
4185         }
4186         
4187         // show a panel if it's registered and related..
4188         
4189         if (!this.navId || !this.tabId || !state || is_was_active) {
4190             return;
4191         }
4192         
4193         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4194         if (!tg) {
4195             return;
4196         }
4197         var pan = tg.getPanelByName(this.tabId);
4198         if (!pan) {
4199             return;
4200         }
4201         // if we can not flip to new panel - go back to old nav highlight..
4202         if (false == tg.showPanel(pan)) {
4203             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4204             if (nv) {
4205                 var onav = nv.getWasActive();
4206                 if (onav) {
4207                     onav.setActive(true, false, true);
4208                 }
4209             }
4210             
4211         }
4212         
4213         
4214         
4215     },
4216      // this should not be here...
4217     setDisabled : function(state)
4218     {
4219         this.disabled = state;
4220         if (!state ) {
4221             this.el.removeClass('disabled');
4222         } else if (!this.el.hasClass('disabled')) {
4223             this.el.addClass('disabled');
4224         }
4225         
4226     },
4227     
4228     /**
4229      * Fetch the element to display the tooltip on.
4230      * @return {Roo.Element} defaults to this.el
4231      */
4232     tooltipEl : function()
4233     {
4234         return this.el.select('' + this.tagtype + '', true).first();
4235     },
4236     
4237     scrollToElement : function(e)
4238     {
4239         var c = document.body;
4240         
4241         /*
4242          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4243          */
4244         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4245             c = document.documentElement;
4246         }
4247         
4248         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4249         
4250         if(!target){
4251             return;
4252         }
4253
4254         var o = target.calcOffsetsTo(c);
4255         
4256         var options = {
4257             target : target,
4258             value : o[1]
4259         }
4260         
4261         this.fireEvent('scrollto', this, options, e);
4262         
4263         Roo.get(c).scrollTo('top', options.value, true);
4264         
4265         return;
4266     }
4267 });
4268  
4269
4270  /*
4271  * - LGPL
4272  *
4273  * sidebar item
4274  *
4275  *  li
4276  *    <span> icon </span>
4277  *    <span> text </span>
4278  *    <span>badge </span>
4279  */
4280
4281 /**
4282  * @class Roo.bootstrap.NavSidebarItem
4283  * @extends Roo.bootstrap.NavItem
4284  * Bootstrap Navbar.NavSidebarItem class
4285  * @constructor
4286  * Create a new Navbar Button
4287  * @param {Object} config The config object
4288  */
4289 Roo.bootstrap.NavSidebarItem = function(config){
4290     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event click
4295          * The raw click event for the entire grid.
4296          * @param {Roo.EventObject} e
4297          */
4298         "click" : true,
4299          /**
4300             * @event changed
4301             * Fires when the active item active state changes
4302             * @param {Roo.bootstrap.NavSidebarItem} this
4303             * @param {boolean} state the new state
4304              
4305          */
4306         'changed': true
4307     });
4308    
4309 };
4310
4311 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4312     
4313     
4314     getAutoCreate : function(){
4315         
4316         
4317         var a = {
4318                 tag: 'a',
4319                 href : this.href || '#',
4320                 cls: '',
4321                 html : '',
4322                 cn : []
4323         };
4324         var cfg = {
4325             tag: 'li',
4326             cls: '',
4327             cn: [ a ]
4328         }
4329         var span = {
4330             tag: 'span',
4331             html : this.html || ''
4332         }
4333         
4334         
4335         if (this.active) {
4336             cfg.cls += ' active';
4337         }
4338         
4339         // left icon..
4340         if (this.glyphicon || this.icon) {
4341             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4342             a.cn.push({ tag : 'i', cls : c }) ;
4343         }
4344         // html..
4345         a.cn.push(span);
4346         // then badge..
4347         if (this.badge !== '') {
4348             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4349         }
4350         // fi
4351         if (this.menu) {
4352             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4353             a.cls += 'dropdown-toggle treeview' ;
4354             
4355         }
4356         
4357         
4358         
4359         return cfg;
4360          
4361            
4362     }
4363    
4364      
4365  
4366 });
4367  
4368
4369  /*
4370  * - LGPL
4371  *
4372  * row
4373  * 
4374  */
4375
4376 /**
4377  * @class Roo.bootstrap.Row
4378  * @extends Roo.bootstrap.Component
4379  * Bootstrap Row class (contains columns...)
4380  * 
4381  * @constructor
4382  * Create a new Row
4383  * @param {Object} config The config object
4384  */
4385
4386 Roo.bootstrap.Row = function(config){
4387     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4388 };
4389
4390 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4391     
4392     getAutoCreate : function(){
4393        return {
4394             cls: 'row clearfix'
4395        };
4396     }
4397     
4398     
4399 });
4400
4401  
4402
4403  /*
4404  * - LGPL
4405  *
4406  * element
4407  * 
4408  */
4409
4410 /**
4411  * @class Roo.bootstrap.Element
4412  * @extends Roo.bootstrap.Component
4413  * Bootstrap Element class
4414  * @cfg {String} html contents of the element
4415  * @cfg {String} tag tag of the element
4416  * @cfg {String} cls class of the element
4417  * @cfg {Boolean} preventDefault (true|false) default false
4418  * @cfg {Boolean} clickable (true|false) default false
4419  * 
4420  * @constructor
4421  * Create a new Element
4422  * @param {Object} config The config object
4423  */
4424
4425 Roo.bootstrap.Element = function(config){
4426     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4427     
4428     this.addEvents({
4429         // raw events
4430         /**
4431          * @event click
4432          * When a element is chick
4433          * @param {Roo.bootstrap.Element} this
4434          * @param {Roo.EventObject} e
4435          */
4436         "click" : true
4437     });
4438 };
4439
4440 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4441     
4442     tag: 'div',
4443     cls: '',
4444     html: '',
4445     preventDefault: false, 
4446     clickable: false,
4447     
4448     getAutoCreate : function(){
4449         
4450         var cfg = {
4451             tag: this.tag,
4452             cls: this.cls,
4453             html: this.html
4454         }
4455         
4456         return cfg;
4457     },
4458     
4459     initEvents: function() 
4460     {
4461         Roo.bootstrap.Element.superclass.initEvents.call(this);
4462         
4463         if(this.clickable){
4464             this.el.on('click', this.onClick, this);
4465         }
4466         
4467     },
4468     
4469     onClick : function(e)
4470     {
4471         if(this.preventDefault){
4472             e.preventDefault();
4473         }
4474         
4475         this.fireEvent('click', this, e);
4476     },
4477     
4478     getValue : function()
4479     {
4480         return this.el.dom.innerHTML;
4481     },
4482     
4483     setValue : function(value)
4484     {
4485         this.el.dom.innerHTML = value;
4486     }
4487    
4488 });
4489
4490  
4491
4492  /*
4493  * - LGPL
4494  *
4495  * pagination
4496  * 
4497  */
4498
4499 /**
4500  * @class Roo.bootstrap.Pagination
4501  * @extends Roo.bootstrap.Component
4502  * Bootstrap Pagination class
4503  * @cfg {String} size xs | sm | md | lg
4504  * @cfg {Boolean} inverse false | true
4505  * 
4506  * @constructor
4507  * Create a new Pagination
4508  * @param {Object} config The config object
4509  */
4510
4511 Roo.bootstrap.Pagination = function(config){
4512     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4513 };
4514
4515 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4516     
4517     cls: false,
4518     size: false,
4519     inverse: false,
4520     
4521     getAutoCreate : function(){
4522         var cfg = {
4523             tag: 'ul',
4524                 cls: 'pagination'
4525         };
4526         if (this.inverse) {
4527             cfg.cls += ' inverse';
4528         }
4529         if (this.html) {
4530             cfg.html=this.html;
4531         }
4532         if (this.cls) {
4533             cfg.cls += " " + this.cls;
4534         }
4535         return cfg;
4536     }
4537    
4538 });
4539
4540  
4541
4542  /*
4543  * - LGPL
4544  *
4545  * Pagination item
4546  * 
4547  */
4548
4549
4550 /**
4551  * @class Roo.bootstrap.PaginationItem
4552  * @extends Roo.bootstrap.Component
4553  * Bootstrap PaginationItem class
4554  * @cfg {String} html text
4555  * @cfg {String} href the link
4556  * @cfg {Boolean} preventDefault (true | false) default true
4557  * @cfg {Boolean} active (true | false) default false
4558  * @cfg {Boolean} disabled default false
4559  * 
4560  * 
4561  * @constructor
4562  * Create a new PaginationItem
4563  * @param {Object} config The config object
4564  */
4565
4566
4567 Roo.bootstrap.PaginationItem = function(config){
4568     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4569     this.addEvents({
4570         // raw events
4571         /**
4572          * @event click
4573          * The raw click event for the entire grid.
4574          * @param {Roo.EventObject} e
4575          */
4576         "click" : true
4577     });
4578 };
4579
4580 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4581     
4582     href : false,
4583     html : false,
4584     preventDefault: true,
4585     active : false,
4586     cls : false,
4587     disabled: false,
4588     
4589     getAutoCreate : function(){
4590         var cfg= {
4591             tag: 'li',
4592             cn: [
4593                 {
4594                     tag : 'a',
4595                     href : this.href ? this.href : '#',
4596                     html : this.html ? this.html : ''
4597                 }
4598             ]
4599         };
4600         
4601         if(this.cls){
4602             cfg.cls = this.cls;
4603         }
4604         
4605         if(this.disabled){
4606             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4607         }
4608         
4609         if(this.active){
4610             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4611         }
4612         
4613         return cfg;
4614     },
4615     
4616     initEvents: function() {
4617         
4618         this.el.on('click', this.onClick, this);
4619         
4620     },
4621     onClick : function(e)
4622     {
4623         Roo.log('PaginationItem on click ');
4624         if(this.preventDefault){
4625             e.preventDefault();
4626         }
4627         
4628         if(this.disabled){
4629             return;
4630         }
4631         
4632         this.fireEvent('click', this, e);
4633     }
4634    
4635 });
4636
4637  
4638
4639  /*
4640  * - LGPL
4641  *
4642  * slider
4643  * 
4644  */
4645
4646
4647 /**
4648  * @class Roo.bootstrap.Slider
4649  * @extends Roo.bootstrap.Component
4650  * Bootstrap Slider class
4651  *    
4652  * @constructor
4653  * Create a new Slider
4654  * @param {Object} config The config object
4655  */
4656
4657 Roo.bootstrap.Slider = function(config){
4658     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4659 };
4660
4661 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4662     
4663     getAutoCreate : function(){
4664         
4665         var cfg = {
4666             tag: 'div',
4667             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4668             cn: [
4669                 {
4670                     tag: 'a',
4671                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4672                 }
4673             ]
4674         }
4675         
4676         return cfg;
4677     }
4678    
4679 });
4680
4681  /*
4682  * Based on:
4683  * Ext JS Library 1.1.1
4684  * Copyright(c) 2006-2007, Ext JS, LLC.
4685  *
4686  * Originally Released Under LGPL - original licence link has changed is not relivant.
4687  *
4688  * Fork - LGPL
4689  * <script type="text/javascript">
4690  */
4691  
4692
4693 /**
4694  * @class Roo.grid.ColumnModel
4695  * @extends Roo.util.Observable
4696  * This is the default implementation of a ColumnModel used by the Grid. It defines
4697  * the columns in the grid.
4698  * <br>Usage:<br>
4699  <pre><code>
4700  var colModel = new Roo.grid.ColumnModel([
4701         {header: "Ticker", width: 60, sortable: true, locked: true},
4702         {header: "Company Name", width: 150, sortable: true},
4703         {header: "Market Cap.", width: 100, sortable: true},
4704         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4705         {header: "Employees", width: 100, sortable: true, resizable: false}
4706  ]);
4707  </code></pre>
4708  * <p>
4709  
4710  * The config options listed for this class are options which may appear in each
4711  * individual column definition.
4712  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4713  * @constructor
4714  * @param {Object} config An Array of column config objects. See this class's
4715  * config objects for details.
4716 */
4717 Roo.grid.ColumnModel = function(config){
4718         /**
4719      * The config passed into the constructor
4720      */
4721     this.config = config;
4722     this.lookup = {};
4723
4724     // if no id, create one
4725     // if the column does not have a dataIndex mapping,
4726     // map it to the order it is in the config
4727     for(var i = 0, len = config.length; i < len; i++){
4728         var c = config[i];
4729         if(typeof c.dataIndex == "undefined"){
4730             c.dataIndex = i;
4731         }
4732         if(typeof c.renderer == "string"){
4733             c.renderer = Roo.util.Format[c.renderer];
4734         }
4735         if(typeof c.id == "undefined"){
4736             c.id = Roo.id();
4737         }
4738         if(c.editor && c.editor.xtype){
4739             c.editor  = Roo.factory(c.editor, Roo.grid);
4740         }
4741         if(c.editor && c.editor.isFormField){
4742             c.editor = new Roo.grid.GridEditor(c.editor);
4743         }
4744         this.lookup[c.id] = c;
4745     }
4746
4747     /**
4748      * The width of columns which have no width specified (defaults to 100)
4749      * @type Number
4750      */
4751     this.defaultWidth = 100;
4752
4753     /**
4754      * Default sortable of columns which have no sortable specified (defaults to false)
4755      * @type Boolean
4756      */
4757     this.defaultSortable = false;
4758
4759     this.addEvents({
4760         /**
4761              * @event widthchange
4762              * Fires when the width of a column changes.
4763              * @param {ColumnModel} this
4764              * @param {Number} columnIndex The column index
4765              * @param {Number} newWidth The new width
4766              */
4767             "widthchange": true,
4768         /**
4769              * @event headerchange
4770              * Fires when the text of a header changes.
4771              * @param {ColumnModel} this
4772              * @param {Number} columnIndex The column index
4773              * @param {Number} newText The new header text
4774              */
4775             "headerchange": true,
4776         /**
4777              * @event hiddenchange
4778              * Fires when a column is hidden or "unhidden".
4779              * @param {ColumnModel} this
4780              * @param {Number} columnIndex The column index
4781              * @param {Boolean} hidden true if hidden, false otherwise
4782              */
4783             "hiddenchange": true,
4784             /**
4785          * @event columnmoved
4786          * Fires when a column is moved.
4787          * @param {ColumnModel} this
4788          * @param {Number} oldIndex
4789          * @param {Number} newIndex
4790          */
4791         "columnmoved" : true,
4792         /**
4793          * @event columlockchange
4794          * Fires when a column's locked state is changed
4795          * @param {ColumnModel} this
4796          * @param {Number} colIndex
4797          * @param {Boolean} locked true if locked
4798          */
4799         "columnlockchange" : true
4800     });
4801     Roo.grid.ColumnModel.superclass.constructor.call(this);
4802 };
4803 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4804     /**
4805      * @cfg {String} header The header text to display in the Grid view.
4806      */
4807     /**
4808      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4809      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4810      * specified, the column's index is used as an index into the Record's data Array.
4811      */
4812     /**
4813      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4814      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4815      */
4816     /**
4817      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4818      * Defaults to the value of the {@link #defaultSortable} property.
4819      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4820      */
4821     /**
4822      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4823      */
4824     /**
4825      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4826      */
4827     /**
4828      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4829      */
4830     /**
4831      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4832      */
4833     /**
4834      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4835      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4836      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4837      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4838      */
4839        /**
4840      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4841      */
4842     /**
4843      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4844      */
4845     /**
4846      * @cfg {String} cursor (Optional)
4847      */
4848     /**
4849      * @cfg {String} tooltip (Optional)
4850      */
4851     /**
4852      * Returns the id of the column at the specified index.
4853      * @param {Number} index The column index
4854      * @return {String} the id
4855      */
4856     getColumnId : function(index){
4857         return this.config[index].id;
4858     },
4859
4860     /**
4861      * Returns the column for a specified id.
4862      * @param {String} id The column id
4863      * @return {Object} the column
4864      */
4865     getColumnById : function(id){
4866         return this.lookup[id];
4867     },
4868
4869     
4870     /**
4871      * Returns the column for a specified dataIndex.
4872      * @param {String} dataIndex The column dataIndex
4873      * @return {Object|Boolean} the column or false if not found
4874      */
4875     getColumnByDataIndex: function(dataIndex){
4876         var index = this.findColumnIndex(dataIndex);
4877         return index > -1 ? this.config[index] : false;
4878     },
4879     
4880     /**
4881      * Returns the index for a specified column id.
4882      * @param {String} id The column id
4883      * @return {Number} the index, or -1 if not found
4884      */
4885     getIndexById : function(id){
4886         for(var i = 0, len = this.config.length; i < len; i++){
4887             if(this.config[i].id == id){
4888                 return i;
4889             }
4890         }
4891         return -1;
4892     },
4893     
4894     /**
4895      * Returns the index for a specified column dataIndex.
4896      * @param {String} dataIndex The column dataIndex
4897      * @return {Number} the index, or -1 if not found
4898      */
4899     
4900     findColumnIndex : function(dataIndex){
4901         for(var i = 0, len = this.config.length; i < len; i++){
4902             if(this.config[i].dataIndex == dataIndex){
4903                 return i;
4904             }
4905         }
4906         return -1;
4907     },
4908     
4909     
4910     moveColumn : function(oldIndex, newIndex){
4911         var c = this.config[oldIndex];
4912         this.config.splice(oldIndex, 1);
4913         this.config.splice(newIndex, 0, c);
4914         this.dataMap = null;
4915         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4916     },
4917
4918     isLocked : function(colIndex){
4919         return this.config[colIndex].locked === true;
4920     },
4921
4922     setLocked : function(colIndex, value, suppressEvent){
4923         if(this.isLocked(colIndex) == value){
4924             return;
4925         }
4926         this.config[colIndex].locked = value;
4927         if(!suppressEvent){
4928             this.fireEvent("columnlockchange", this, colIndex, value);
4929         }
4930     },
4931
4932     getTotalLockedWidth : function(){
4933         var totalWidth = 0;
4934         for(var i = 0; i < this.config.length; i++){
4935             if(this.isLocked(i) && !this.isHidden(i)){
4936                 this.totalWidth += this.getColumnWidth(i);
4937             }
4938         }
4939         return totalWidth;
4940     },
4941
4942     getLockedCount : function(){
4943         for(var i = 0, len = this.config.length; i < len; i++){
4944             if(!this.isLocked(i)){
4945                 return i;
4946             }
4947         }
4948     },
4949
4950     /**
4951      * Returns the number of columns.
4952      * @return {Number}
4953      */
4954     getColumnCount : function(visibleOnly){
4955         if(visibleOnly === true){
4956             var c = 0;
4957             for(var i = 0, len = this.config.length; i < len; i++){
4958                 if(!this.isHidden(i)){
4959                     c++;
4960                 }
4961             }
4962             return c;
4963         }
4964         return this.config.length;
4965     },
4966
4967     /**
4968      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4969      * @param {Function} fn
4970      * @param {Object} scope (optional)
4971      * @return {Array} result
4972      */
4973     getColumnsBy : function(fn, scope){
4974         var r = [];
4975         for(var i = 0, len = this.config.length; i < len; i++){
4976             var c = this.config[i];
4977             if(fn.call(scope||this, c, i) === true){
4978                 r[r.length] = c;
4979             }
4980         }
4981         return r;
4982     },
4983
4984     /**
4985      * Returns true if the specified column is sortable.
4986      * @param {Number} col The column index
4987      * @return {Boolean}
4988      */
4989     isSortable : function(col){
4990         if(typeof this.config[col].sortable == "undefined"){
4991             return this.defaultSortable;
4992         }
4993         return this.config[col].sortable;
4994     },
4995
4996     /**
4997      * Returns the rendering (formatting) function defined for the column.
4998      * @param {Number} col The column index.
4999      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5000      */
5001     getRenderer : function(col){
5002         if(!this.config[col].renderer){
5003             return Roo.grid.ColumnModel.defaultRenderer;
5004         }
5005         return this.config[col].renderer;
5006     },
5007
5008     /**
5009      * Sets the rendering (formatting) function for a column.
5010      * @param {Number} col The column index
5011      * @param {Function} fn The function to use to process the cell's raw data
5012      * to return HTML markup for the grid view. The render function is called with
5013      * the following parameters:<ul>
5014      * <li>Data value.</li>
5015      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5016      * <li>css A CSS style string to apply to the table cell.</li>
5017      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5018      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5019      * <li>Row index</li>
5020      * <li>Column index</li>
5021      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5022      */
5023     setRenderer : function(col, fn){
5024         this.config[col].renderer = fn;
5025     },
5026
5027     /**
5028      * Returns the width for the specified column.
5029      * @param {Number} col The column index
5030      * @return {Number}
5031      */
5032     getColumnWidth : function(col){
5033         return this.config[col].width * 1 || this.defaultWidth;
5034     },
5035
5036     /**
5037      * Sets the width for a column.
5038      * @param {Number} col The column index
5039      * @param {Number} width The new width
5040      */
5041     setColumnWidth : function(col, width, suppressEvent){
5042         this.config[col].width = width;
5043         this.totalWidth = null;
5044         if(!suppressEvent){
5045              this.fireEvent("widthchange", this, col, width);
5046         }
5047     },
5048
5049     /**
5050      * Returns the total width of all columns.
5051      * @param {Boolean} includeHidden True to include hidden column widths
5052      * @return {Number}
5053      */
5054     getTotalWidth : function(includeHidden){
5055         if(!this.totalWidth){
5056             this.totalWidth = 0;
5057             for(var i = 0, len = this.config.length; i < len; i++){
5058                 if(includeHidden || !this.isHidden(i)){
5059                     this.totalWidth += this.getColumnWidth(i);
5060                 }
5061             }
5062         }
5063         return this.totalWidth;
5064     },
5065
5066     /**
5067      * Returns the header for the specified column.
5068      * @param {Number} col The column index
5069      * @return {String}
5070      */
5071     getColumnHeader : function(col){
5072         return this.config[col].header;
5073     },
5074
5075     /**
5076      * Sets the header for a column.
5077      * @param {Number} col The column index
5078      * @param {String} header The new header
5079      */
5080     setColumnHeader : function(col, header){
5081         this.config[col].header = header;
5082         this.fireEvent("headerchange", this, col, header);
5083     },
5084
5085     /**
5086      * Returns the tooltip for the specified column.
5087      * @param {Number} col The column index
5088      * @return {String}
5089      */
5090     getColumnTooltip : function(col){
5091             return this.config[col].tooltip;
5092     },
5093     /**
5094      * Sets the tooltip for a column.
5095      * @param {Number} col The column index
5096      * @param {String} tooltip The new tooltip
5097      */
5098     setColumnTooltip : function(col, tooltip){
5099             this.config[col].tooltip = tooltip;
5100     },
5101
5102     /**
5103      * Returns the dataIndex for the specified column.
5104      * @param {Number} col The column index
5105      * @return {Number}
5106      */
5107     getDataIndex : function(col){
5108         return this.config[col].dataIndex;
5109     },
5110
5111     /**
5112      * Sets the dataIndex for a column.
5113      * @param {Number} col The column index
5114      * @param {Number} dataIndex The new dataIndex
5115      */
5116     setDataIndex : function(col, dataIndex){
5117         this.config[col].dataIndex = dataIndex;
5118     },
5119
5120     
5121     
5122     /**
5123      * Returns true if the cell is editable.
5124      * @param {Number} colIndex The column index
5125      * @param {Number} rowIndex The row index
5126      * @return {Boolean}
5127      */
5128     isCellEditable : function(colIndex, rowIndex){
5129         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5130     },
5131
5132     /**
5133      * Returns the editor defined for the cell/column.
5134      * return false or null to disable editing.
5135      * @param {Number} colIndex The column index
5136      * @param {Number} rowIndex The row index
5137      * @return {Object}
5138      */
5139     getCellEditor : function(colIndex, rowIndex){
5140         return this.config[colIndex].editor;
5141     },
5142
5143     /**
5144      * Sets if a column is editable.
5145      * @param {Number} col The column index
5146      * @param {Boolean} editable True if the column is editable
5147      */
5148     setEditable : function(col, editable){
5149         this.config[col].editable = editable;
5150     },
5151
5152
5153     /**
5154      * Returns true if the column is hidden.
5155      * @param {Number} colIndex The column index
5156      * @return {Boolean}
5157      */
5158     isHidden : function(colIndex){
5159         return this.config[colIndex].hidden;
5160     },
5161
5162
5163     /**
5164      * Returns true if the column width cannot be changed
5165      */
5166     isFixed : function(colIndex){
5167         return this.config[colIndex].fixed;
5168     },
5169
5170     /**
5171      * Returns true if the column can be resized
5172      * @return {Boolean}
5173      */
5174     isResizable : function(colIndex){
5175         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5176     },
5177     /**
5178      * Sets if a column is hidden.
5179      * @param {Number} colIndex The column index
5180      * @param {Boolean} hidden True if the column is hidden
5181      */
5182     setHidden : function(colIndex, hidden){
5183         this.config[colIndex].hidden = hidden;
5184         this.totalWidth = null;
5185         this.fireEvent("hiddenchange", this, colIndex, hidden);
5186     },
5187
5188     /**
5189      * Sets the editor for a column.
5190      * @param {Number} col The column index
5191      * @param {Object} editor The editor object
5192      */
5193     setEditor : function(col, editor){
5194         this.config[col].editor = editor;
5195     }
5196 });
5197
5198 Roo.grid.ColumnModel.defaultRenderer = function(value){
5199         if(typeof value == "string" && value.length < 1){
5200             return "&#160;";
5201         }
5202         return value;
5203 };
5204
5205 // Alias for backwards compatibility
5206 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5207 /*
5208  * Based on:
5209  * Ext JS Library 1.1.1
5210  * Copyright(c) 2006-2007, Ext JS, LLC.
5211  *
5212  * Originally Released Under LGPL - original licence link has changed is not relivant.
5213  *
5214  * Fork - LGPL
5215  * <script type="text/javascript">
5216  */
5217  
5218 /**
5219  * @class Roo.LoadMask
5220  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5221  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5222  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5223  * element's UpdateManager load indicator and will be destroyed after the initial load.
5224  * @constructor
5225  * Create a new LoadMask
5226  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5227  * @param {Object} config The config object
5228  */
5229 Roo.LoadMask = function(el, config){
5230     this.el = Roo.get(el);
5231     Roo.apply(this, config);
5232     if(this.store){
5233         this.store.on('beforeload', this.onBeforeLoad, this);
5234         this.store.on('load', this.onLoad, this);
5235         this.store.on('loadexception', this.onLoadException, this);
5236         this.removeMask = false;
5237     }else{
5238         var um = this.el.getUpdateManager();
5239         um.showLoadIndicator = false; // disable the default indicator
5240         um.on('beforeupdate', this.onBeforeLoad, this);
5241         um.on('update', this.onLoad, this);
5242         um.on('failure', this.onLoad, this);
5243         this.removeMask = true;
5244     }
5245 };
5246
5247 Roo.LoadMask.prototype = {
5248     /**
5249      * @cfg {Boolean} removeMask
5250      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5251      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5252      */
5253     /**
5254      * @cfg {String} msg
5255      * The text to display in a centered loading message box (defaults to 'Loading...')
5256      */
5257     msg : 'Loading...',
5258     /**
5259      * @cfg {String} msgCls
5260      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5261      */
5262     msgCls : 'x-mask-loading',
5263
5264     /**
5265      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5266      * @type Boolean
5267      */
5268     disabled: false,
5269
5270     /**
5271      * Disables the mask to prevent it from being displayed
5272      */
5273     disable : function(){
5274        this.disabled = true;
5275     },
5276
5277     /**
5278      * Enables the mask so that it can be displayed
5279      */
5280     enable : function(){
5281         this.disabled = false;
5282     },
5283     
5284     onLoadException : function()
5285     {
5286         Roo.log(arguments);
5287         
5288         if (typeof(arguments[3]) != 'undefined') {
5289             Roo.MessageBox.alert("Error loading",arguments[3]);
5290         } 
5291         /*
5292         try {
5293             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5294                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5295             }   
5296         } catch(e) {
5297             
5298         }
5299         */
5300     
5301         
5302         
5303         this.el.unmask(this.removeMask);
5304     },
5305     // private
5306     onLoad : function()
5307     {
5308         this.el.unmask(this.removeMask);
5309     },
5310
5311     // private
5312     onBeforeLoad : function(){
5313         if(!this.disabled){
5314             this.el.mask(this.msg, this.msgCls);
5315         }
5316     },
5317
5318     // private
5319     destroy : function(){
5320         if(this.store){
5321             this.store.un('beforeload', this.onBeforeLoad, this);
5322             this.store.un('load', this.onLoad, this);
5323             this.store.un('loadexception', this.onLoadException, this);
5324         }else{
5325             var um = this.el.getUpdateManager();
5326             um.un('beforeupdate', this.onBeforeLoad, this);
5327             um.un('update', this.onLoad, this);
5328             um.un('failure', this.onLoad, this);
5329         }
5330     }
5331 };/*
5332  * - LGPL
5333  *
5334  * table
5335  * 
5336  */
5337
5338 /**
5339  * @class Roo.bootstrap.Table
5340  * @extends Roo.bootstrap.Component
5341  * Bootstrap Table class
5342  * @cfg {String} cls table class
5343  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5344  * @cfg {String} bgcolor Specifies the background color for a table
5345  * @cfg {Number} border Specifies whether the table cells should have borders or not
5346  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5347  * @cfg {Number} cellspacing Specifies the space between cells
5348  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5349  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5350  * @cfg {String} sortable Specifies that the table should be sortable
5351  * @cfg {String} summary Specifies a summary of the content of a table
5352  * @cfg {Number} width Specifies the width of a table
5353  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5354  * 
5355  * @cfg {boolean} striped Should the rows be alternative striped
5356  * @cfg {boolean} bordered Add borders to the table
5357  * @cfg {boolean} hover Add hover highlighting
5358  * @cfg {boolean} condensed Format condensed
5359  * @cfg {boolean} responsive Format condensed
5360  * @cfg {Boolean} loadMask (true|false) default false
5361  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5362  * @cfg {Boolean} thead (true|false) generate thead, default true
5363  * @cfg {Boolean} RowSelection (true|false) default false
5364  * @cfg {Boolean} CellSelection (true|false) default false
5365  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5366  
5367  * 
5368  * @constructor
5369  * Create a new Table
5370  * @param {Object} config The config object
5371  */
5372
5373 Roo.bootstrap.Table = function(config){
5374     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5375     
5376     if (this.sm) {
5377         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5378         this.sm = this.selModel;
5379         this.sm.xmodule = this.xmodule || false;
5380     }
5381     if (this.cm && typeof(this.cm.config) == 'undefined') {
5382         this.colModel = new Roo.grid.ColumnModel(this.cm);
5383         this.cm = this.colModel;
5384         this.cm.xmodule = this.xmodule || false;
5385     }
5386     if (this.store) {
5387         this.store= Roo.factory(this.store, Roo.data);
5388         this.ds = this.store;
5389         this.ds.xmodule = this.xmodule || false;
5390          
5391     }
5392     if (this.footer && this.store) {
5393         this.footer.dataSource = this.ds;
5394         this.footer = Roo.factory(this.footer);
5395     }
5396     
5397     /** @private */
5398     this.addEvents({
5399         /**
5400          * @event cellclick
5401          * Fires when a cell is clicked
5402          * @param {Roo.bootstrap.Table} this
5403          * @param {Roo.Element} el
5404          * @param {Number} rowIndex
5405          * @param {Number} columnIndex
5406          * @param {Roo.EventObject} e
5407          */
5408         "cellclick" : true,
5409         /**
5410          * @event celldblclick
5411          * Fires when a cell is double clicked
5412          * @param {Roo.bootstrap.Table} this
5413          * @param {Roo.Element} el
5414          * @param {Number} rowIndex
5415          * @param {Number} columnIndex
5416          * @param {Roo.EventObject} e
5417          */
5418         "celldblclick" : true,
5419         /**
5420          * @event rowclick
5421          * Fires when a row is clicked
5422          * @param {Roo.bootstrap.Table} this
5423          * @param {Roo.Element} el
5424          * @param {Number} rowIndex
5425          * @param {Roo.EventObject} e
5426          */
5427         "rowclick" : true,
5428         /**
5429          * @event rowdblclick
5430          * Fires when a row is double clicked
5431          * @param {Roo.bootstrap.Table} this
5432          * @param {Roo.Element} el
5433          * @param {Number} rowIndex
5434          * @param {Roo.EventObject} e
5435          */
5436         "rowdblclick" : true,
5437         /**
5438          * @event mouseover
5439          * Fires when a mouseover occur
5440          * @param {Roo.bootstrap.Table} this
5441          * @param {Roo.Element} el
5442          * @param {Number} rowIndex
5443          * @param {Number} columnIndex
5444          * @param {Roo.EventObject} e
5445          */
5446         "mouseover" : true,
5447         /**
5448          * @event mouseout
5449          * Fires when a mouseout occur
5450          * @param {Roo.bootstrap.Table} this
5451          * @param {Roo.Element} el
5452          * @param {Number} rowIndex
5453          * @param {Number} columnIndex
5454          * @param {Roo.EventObject} e
5455          */
5456         "mouseout" : true,
5457         /**
5458          * @event rowclass
5459          * Fires when a row is rendered, so you can change add a style to it.
5460          * @param {Roo.bootstrap.Table} this
5461          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5462          */
5463         'rowclass' : true,
5464           /**
5465          * @event rowsrendered
5466          * Fires when all the  rows have been rendered
5467          * @param {Roo.bootstrap.Table} this
5468          */
5469         'rowsrendered' : true
5470         
5471     });
5472 };
5473
5474 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5475     
5476     cls: false,
5477     align: false,
5478     bgcolor: false,
5479     border: false,
5480     cellpadding: false,
5481     cellspacing: false,
5482     frame: false,
5483     rules: false,
5484     sortable: false,
5485     summary: false,
5486     width: false,
5487     striped : false,
5488     bordered: false,
5489     hover:  false,
5490     condensed : false,
5491     responsive : false,
5492     sm : false,
5493     cm : false,
5494     store : false,
5495     loadMask : false,
5496     tfoot : true,
5497     thead : true,
5498     RowSelection : false,
5499     CellSelection : false,
5500     layout : false,
5501     
5502     // Roo.Element - the tbody
5503     mainBody: false, 
5504     
5505     getAutoCreate : function(){
5506         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5507         
5508         cfg = {
5509             tag: 'table',
5510             cls : 'table',
5511             cn : []
5512         }
5513             
5514         if (this.striped) {
5515             cfg.cls += ' table-striped';
5516         }
5517         
5518         if (this.hover) {
5519             cfg.cls += ' table-hover';
5520         }
5521         if (this.bordered) {
5522             cfg.cls += ' table-bordered';
5523         }
5524         if (this.condensed) {
5525             cfg.cls += ' table-condensed';
5526         }
5527         if (this.responsive) {
5528             cfg.cls += ' table-responsive';
5529         }
5530         
5531         if (this.cls) {
5532             cfg.cls+=  ' ' +this.cls;
5533         }
5534         
5535         // this lot should be simplifed...
5536         
5537         if (this.align) {
5538             cfg.align=this.align;
5539         }
5540         if (this.bgcolor) {
5541             cfg.bgcolor=this.bgcolor;
5542         }
5543         if (this.border) {
5544             cfg.border=this.border;
5545         }
5546         if (this.cellpadding) {
5547             cfg.cellpadding=this.cellpadding;
5548         }
5549         if (this.cellspacing) {
5550             cfg.cellspacing=this.cellspacing;
5551         }
5552         if (this.frame) {
5553             cfg.frame=this.frame;
5554         }
5555         if (this.rules) {
5556             cfg.rules=this.rules;
5557         }
5558         if (this.sortable) {
5559             cfg.sortable=this.sortable;
5560         }
5561         if (this.summary) {
5562             cfg.summary=this.summary;
5563         }
5564         if (this.width) {
5565             cfg.width=this.width;
5566         }
5567         if (this.layout) {
5568             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5569         }
5570         
5571         if(this.store || this.cm){
5572             if(this.thead){
5573                 cfg.cn.push(this.renderHeader());
5574             }
5575             
5576             cfg.cn.push(this.renderBody());
5577             
5578             if(this.tfoot){
5579                 cfg.cn.push(this.renderFooter());
5580             }
5581             
5582             cfg.cls+=  ' TableGrid';
5583         }
5584         
5585         return { cn : [ cfg ] };
5586     },
5587     
5588     initEvents : function()
5589     {   
5590         if(!this.store || !this.cm){
5591             return;
5592         }
5593         
5594         //Roo.log('initEvents with ds!!!!');
5595         
5596         this.mainBody = this.el.select('tbody', true).first();
5597         
5598         
5599         var _this = this;
5600         
5601         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5602             e.on('click', _this.sort, _this);
5603         });
5604         
5605         this.el.on("click", this.onClick, this);
5606         this.el.on("dblclick", this.onDblClick, this);
5607         
5608         // why is this done????? = it breaks dialogs??
5609         //this.parent().el.setStyle('position', 'relative');
5610         
5611         
5612         if (this.footer) {
5613             this.footer.parentId = this.id;
5614             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5615         }
5616         
5617         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5618         
5619         this.store.on('load', this.onLoad, this);
5620         this.store.on('beforeload', this.onBeforeLoad, this);
5621         this.store.on('update', this.onUpdate, this);
5622         this.store.on('add', this.onAdd, this);
5623         
5624     },
5625     
5626     onMouseover : function(e, el)
5627     {
5628         var cell = Roo.get(el);
5629         
5630         if(!cell){
5631             return;
5632         }
5633         
5634         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5635             cell = cell.findParent('td', false, true);
5636         }
5637         
5638         var row = cell.findParent('tr', false, true);
5639         var cellIndex = cell.dom.cellIndex;
5640         var rowIndex = row.dom.rowIndex - 1; // start from 0
5641         
5642         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5643         
5644     },
5645     
5646     onMouseout : function(e, el)
5647     {
5648         var cell = Roo.get(el);
5649         
5650         if(!cell){
5651             return;
5652         }
5653         
5654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5655             cell = cell.findParent('td', false, true);
5656         }
5657         
5658         var row = cell.findParent('tr', false, true);
5659         var cellIndex = cell.dom.cellIndex;
5660         var rowIndex = row.dom.rowIndex - 1; // start from 0
5661         
5662         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5663         
5664     },
5665     
5666     onClick : function(e, el)
5667     {
5668         var cell = Roo.get(el);
5669         
5670         if(!cell || (!this.CellSelection && !this.RowSelection)){
5671             return;
5672         }
5673         
5674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5675             cell = cell.findParent('td', false, true);
5676         }
5677         
5678         if(!cell || typeof(cell) == 'undefined'){
5679             return;
5680         }
5681         
5682         var row = cell.findParent('tr', false, true);
5683         
5684         if(!row || typeof(row) == 'undefined'){
5685             return;
5686         }
5687         
5688         var cellIndex = cell.dom.cellIndex;
5689         var rowIndex = this.getRowIndex(row);
5690         
5691         if(this.CellSelection){
5692             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5693         }
5694         
5695         if(this.RowSelection){
5696             this.fireEvent('rowclick', this, row, rowIndex, e);
5697         }
5698         
5699         
5700     },
5701     
5702     onDblClick : function(e,el)
5703     {
5704         var cell = Roo.get(el);
5705         
5706         if(!cell || (!this.CellSelection && !this.RowSelection)){
5707             return;
5708         }
5709         
5710         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5711             cell = cell.findParent('td', false, true);
5712         }
5713         
5714         if(!cell || typeof(cell) == 'undefined'){
5715             return;
5716         }
5717         
5718         var row = cell.findParent('tr', false, true);
5719         
5720         if(!row || typeof(row) == 'undefined'){
5721             return;
5722         }
5723         
5724         var cellIndex = cell.dom.cellIndex;
5725         var rowIndex = this.getRowIndex(row);
5726         
5727         if(this.CellSelection){
5728             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5729         }
5730         
5731         if(this.RowSelection){
5732             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5733         }
5734     },
5735     
5736     sort : function(e,el)
5737     {
5738         var col = Roo.get(el);
5739         
5740         if(!col.hasClass('sortable')){
5741             return;
5742         }
5743         
5744         var sort = col.attr('sort');
5745         var dir = 'ASC';
5746         
5747         if(col.hasClass('glyphicon-arrow-up')){
5748             dir = 'DESC';
5749         }
5750         
5751         this.store.sortInfo = {field : sort, direction : dir};
5752         
5753         if (this.footer) {
5754             Roo.log("calling footer first");
5755             this.footer.onClick('first');
5756         } else {
5757         
5758             this.store.load({ params : { start : 0 } });
5759         }
5760     },
5761     
5762     renderHeader : function()
5763     {
5764         var header = {
5765             tag: 'thead',
5766             cn : []
5767         };
5768         
5769         var cm = this.cm;
5770         
5771         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5772             
5773             var config = cm.config[i];
5774                     
5775             var c = {
5776                 tag: 'th',
5777                 style : '',
5778                 html: cm.getColumnHeader(i)
5779             };
5780             
5781             if(typeof(config.tooltip) != 'undefined'){
5782                 c.tooltip = config.tooltip;
5783             }
5784             
5785             if(typeof(config.colspan) != 'undefined'){
5786                 c.colspan = config.colspan;
5787             }
5788             
5789             if(typeof(config.hidden) != 'undefined' && config.hidden){
5790                 c.style += ' display:none;';
5791             }
5792             
5793             if(typeof(config.dataIndex) != 'undefined'){
5794                 c.sort = config.dataIndex;
5795             }
5796             
5797             if(typeof(config.sortable) != 'undefined' && config.sortable){
5798                 c.cls = 'sortable';
5799             }
5800             
5801             if(typeof(config.align) != 'undefined' && config.align.length){
5802                 c.style += ' text-align:' + config.align + ';';
5803             }
5804             
5805             if(typeof(config.width) != 'undefined'){
5806                 c.style += ' width:' + config.width + 'px;';
5807             }
5808             
5809             if(typeof(config.cls) != 'undefined'){
5810                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5811             }
5812             
5813             header.cn.push(c)
5814         }
5815         
5816         return header;
5817     },
5818     
5819     renderBody : function()
5820     {
5821         var body = {
5822             tag: 'tbody',
5823             cn : [
5824                 {
5825                     tag: 'tr',
5826                     cn : [
5827                         {
5828                             tag : 'td',
5829                             colspan :  this.cm.getColumnCount()
5830                         }
5831                     ]
5832                 }
5833             ]
5834         };
5835         
5836         return body;
5837     },
5838     
5839     renderFooter : function()
5840     {
5841         var footer = {
5842             tag: 'tfoot',
5843             cn : [
5844                 {
5845                     tag: 'tr',
5846                     cn : [
5847                         {
5848                             tag : 'td',
5849                             colspan :  this.cm.getColumnCount()
5850                         }
5851                     ]
5852                 }
5853             ]
5854         };
5855         
5856         return footer;
5857     },
5858     
5859     
5860     
5861     onLoad : function()
5862     {
5863         Roo.log('ds onload');
5864         this.clear();
5865         
5866         var _this = this;
5867         var cm = this.cm;
5868         var ds = this.store;
5869         
5870         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5871             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5872             
5873             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5874                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5875             }
5876             
5877             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5878                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5879             }
5880         });
5881         
5882         var tbody =  this.mainBody;
5883               
5884         if(ds.getCount() > 0){
5885             ds.data.each(function(d,rowIndex){
5886                 var row =  this.renderRow(cm, ds, rowIndex);
5887                 
5888                 tbody.createChild(row);
5889                 
5890                 var _this = this;
5891                 
5892                 if(row.cellObjects.length){
5893                     Roo.each(row.cellObjects, function(r){
5894                         _this.renderCellObject(r);
5895                     })
5896                 }
5897                 
5898             }, this);
5899         }
5900         
5901         Roo.each(this.el.select('tbody td', true).elements, function(e){
5902             e.on('mouseover', _this.onMouseover, _this);
5903         });
5904         
5905         Roo.each(this.el.select('tbody td', true).elements, function(e){
5906             e.on('mouseout', _this.onMouseout, _this);
5907         });
5908         this.fireEvent('rowsrendered', this);
5909         //if(this.loadMask){
5910         //    this.maskEl.hide();
5911         //}
5912     },
5913     
5914     
5915     onUpdate : function(ds,record)
5916     {
5917         this.refreshRow(record);
5918     },
5919     
5920     onRemove : function(ds, record, index, isUpdate){
5921         if(isUpdate !== true){
5922             this.fireEvent("beforerowremoved", this, index, record);
5923         }
5924         var bt = this.mainBody.dom;
5925         
5926         var rows = this.el.select('tbody > tr', true).elements;
5927         
5928         if(typeof(rows[index]) != 'undefined'){
5929             bt.removeChild(rows[index].dom);
5930         }
5931         
5932 //        if(bt.rows[index]){
5933 //            bt.removeChild(bt.rows[index]);
5934 //        }
5935         
5936         if(isUpdate !== true){
5937             //this.stripeRows(index);
5938             //this.syncRowHeights(index, index);
5939             //this.layout();
5940             this.fireEvent("rowremoved", this, index, record);
5941         }
5942     },
5943     
5944     onAdd : function(ds, records, rowIndex)
5945     {
5946         //Roo.log('on Add called');
5947         // - note this does not handle multiple adding very well..
5948         var bt = this.mainBody.dom;
5949         for (var i =0 ; i < records.length;i++) {
5950             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5951             //Roo.log(records[i]);
5952             //Roo.log(this.store.getAt(rowIndex+i));
5953             this.insertRow(this.store, rowIndex + i, false);
5954             return;
5955         }
5956         
5957     },
5958     
5959     
5960     refreshRow : function(record){
5961         var ds = this.store, index;
5962         if(typeof record == 'number'){
5963             index = record;
5964             record = ds.getAt(index);
5965         }else{
5966             index = ds.indexOf(record);
5967         }
5968         this.insertRow(ds, index, true);
5969         this.onRemove(ds, record, index+1, true);
5970         //this.syncRowHeights(index, index);
5971         //this.layout();
5972         this.fireEvent("rowupdated", this, index, record);
5973     },
5974     
5975     insertRow : function(dm, rowIndex, isUpdate){
5976         
5977         if(!isUpdate){
5978             this.fireEvent("beforerowsinserted", this, rowIndex);
5979         }
5980             //var s = this.getScrollState();
5981         var row = this.renderRow(this.cm, this.store, rowIndex);
5982         // insert before rowIndex..
5983         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5984         
5985         var _this = this;
5986                 
5987         if(row.cellObjects.length){
5988             Roo.each(row.cellObjects, function(r){
5989                 _this.renderCellObject(r);
5990             })
5991         }
5992             
5993         if(!isUpdate){
5994             this.fireEvent("rowsinserted", this, rowIndex);
5995             //this.syncRowHeights(firstRow, lastRow);
5996             //this.stripeRows(firstRow);
5997             //this.layout();
5998         }
5999         
6000     },
6001     
6002     
6003     getRowDom : function(rowIndex)
6004     {
6005         var rows = this.el.select('tbody > tr', true).elements;
6006         
6007         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6008         
6009     },
6010     // returns the object tree for a tr..
6011   
6012     
6013     renderRow : function(cm, ds, rowIndex) 
6014     {
6015         
6016         var d = ds.getAt(rowIndex);
6017         
6018         var row = {
6019             tag : 'tr',
6020             cn : []
6021         };
6022             
6023         var cellObjects = [];
6024         
6025         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6026             var config = cm.config[i];
6027             
6028             var renderer = cm.getRenderer(i);
6029             var value = '';
6030             var id = false;
6031             
6032             if(typeof(renderer) !== 'undefined'){
6033                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6034             }
6035             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6036             // and are rendered into the cells after the row is rendered - using the id for the element.
6037             
6038             if(typeof(value) === 'object'){
6039                 id = Roo.id();
6040                 cellObjects.push({
6041                     container : id,
6042                     cfg : value 
6043                 })
6044             }
6045             
6046             var rowcfg = {
6047                 record: d,
6048                 rowIndex : rowIndex,
6049                 colIndex : i,
6050                 rowClass : ''
6051             }
6052
6053             this.fireEvent('rowclass', this, rowcfg);
6054             
6055             var td = {
6056                 tag: 'td',
6057                 cls : rowcfg.rowClass,
6058                 style: '',
6059                 html: (typeof(value) === 'object') ? '' : value
6060             };
6061             
6062             if (id) {
6063                 td.id = id;
6064             }
6065             
6066             if(typeof(config.colspan) != 'undefined'){
6067                 td.colspan = config.colspan;
6068             }
6069             
6070             if(typeof(config.hidden) != 'undefined' && config.hidden){
6071                 td.style += ' display:none;';
6072             }
6073             
6074             if(typeof(config.align) != 'undefined' && config.align.length){
6075                 td.style += ' text-align:' + config.align + ';';
6076             }
6077             
6078             if(typeof(config.width) != 'undefined'){
6079                 td.style += ' width:' +  config.width + 'px;';
6080             }
6081             
6082             if(typeof(config.cursor) != 'undefined'){
6083                 td.style += ' cursor:' +  config.cursor + ';';
6084             }
6085             
6086             if(typeof(config.cls) != 'undefined'){
6087                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6088             }
6089              
6090             row.cn.push(td);
6091            
6092         }
6093         
6094         row.cellObjects = cellObjects;
6095         
6096         return row;
6097           
6098     },
6099     
6100     
6101     
6102     onBeforeLoad : function()
6103     {
6104         //Roo.log('ds onBeforeLoad');
6105         
6106         //this.clear();
6107         
6108         //if(this.loadMask){
6109         //    this.maskEl.show();
6110         //}
6111     },
6112      /**
6113      * Remove all rows
6114      */
6115     clear : function()
6116     {
6117         this.el.select('tbody', true).first().dom.innerHTML = '';
6118     },
6119     /**
6120      * Show or hide a row.
6121      * @param {Number} rowIndex to show or hide
6122      * @param {Boolean} state hide
6123      */
6124     setRowVisibility : function(rowIndex, state)
6125     {
6126         var bt = this.mainBody.dom;
6127         
6128         var rows = this.el.select('tbody > tr', true).elements;
6129         
6130         if(typeof(rows[rowIndex]) == 'undefined'){
6131             return;
6132         }
6133         rows[rowIndex].dom.style.display = state ? '' : 'none';
6134     },
6135     
6136     
6137     getSelectionModel : function(){
6138         if(!this.selModel){
6139             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6140         }
6141         return this.selModel;
6142     },
6143     /*
6144      * Render the Roo.bootstrap object from renderder
6145      */
6146     renderCellObject : function(r)
6147     {
6148         var _this = this;
6149         
6150         var t = r.cfg.render(r.container);
6151         
6152         if(r.cfg.cn){
6153             Roo.each(r.cfg.cn, function(c){
6154                 var child = {
6155                     container: t.getChildContainer(),
6156                     cfg: c
6157                 }
6158                 _this.renderCellObject(child);
6159             })
6160         }
6161     },
6162     
6163     getRowIndex : function(row)
6164     {
6165         var rowIndex = -1;
6166         
6167         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6168             if(el != row){
6169                 return;
6170             }
6171             
6172             rowIndex = index;
6173         });
6174         
6175         return rowIndex;
6176     }
6177    
6178 });
6179
6180  
6181
6182  /*
6183  * - LGPL
6184  *
6185  * table cell
6186  * 
6187  */
6188
6189 /**
6190  * @class Roo.bootstrap.TableCell
6191  * @extends Roo.bootstrap.Component
6192  * Bootstrap TableCell class
6193  * @cfg {String} html cell contain text
6194  * @cfg {String} cls cell class
6195  * @cfg {String} tag cell tag (td|th) default td
6196  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6197  * @cfg {String} align Aligns the content in a cell
6198  * @cfg {String} axis Categorizes cells
6199  * @cfg {String} bgcolor Specifies the background color of a cell
6200  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6201  * @cfg {Number} colspan Specifies the number of columns a cell should span
6202  * @cfg {String} headers Specifies one or more header cells a cell is related to
6203  * @cfg {Number} height Sets the height of a cell
6204  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6205  * @cfg {Number} rowspan Sets the number of rows a cell should span
6206  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6207  * @cfg {String} valign Vertical aligns the content in a cell
6208  * @cfg {Number} width Specifies the width of a cell
6209  * 
6210  * @constructor
6211  * Create a new TableCell
6212  * @param {Object} config The config object
6213  */
6214
6215 Roo.bootstrap.TableCell = function(config){
6216     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6217 };
6218
6219 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6220     
6221     html: false,
6222     cls: false,
6223     tag: false,
6224     abbr: false,
6225     align: false,
6226     axis: false,
6227     bgcolor: false,
6228     charoff: false,
6229     colspan: false,
6230     headers: false,
6231     height: false,
6232     nowrap: false,
6233     rowspan: false,
6234     scope: false,
6235     valign: false,
6236     width: false,
6237     
6238     
6239     getAutoCreate : function(){
6240         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6241         
6242         cfg = {
6243             tag: 'td'
6244         }
6245         
6246         if(this.tag){
6247             cfg.tag = this.tag;
6248         }
6249         
6250         if (this.html) {
6251             cfg.html=this.html
6252         }
6253         if (this.cls) {
6254             cfg.cls=this.cls
6255         }
6256         if (this.abbr) {
6257             cfg.abbr=this.abbr
6258         }
6259         if (this.align) {
6260             cfg.align=this.align
6261         }
6262         if (this.axis) {
6263             cfg.axis=this.axis
6264         }
6265         if (this.bgcolor) {
6266             cfg.bgcolor=this.bgcolor
6267         }
6268         if (this.charoff) {
6269             cfg.charoff=this.charoff
6270         }
6271         if (this.colspan) {
6272             cfg.colspan=this.colspan
6273         }
6274         if (this.headers) {
6275             cfg.headers=this.headers
6276         }
6277         if (this.height) {
6278             cfg.height=this.height
6279         }
6280         if (this.nowrap) {
6281             cfg.nowrap=this.nowrap
6282         }
6283         if (this.rowspan) {
6284             cfg.rowspan=this.rowspan
6285         }
6286         if (this.scope) {
6287             cfg.scope=this.scope
6288         }
6289         if (this.valign) {
6290             cfg.valign=this.valign
6291         }
6292         if (this.width) {
6293             cfg.width=this.width
6294         }
6295         
6296         
6297         return cfg;
6298     }
6299    
6300 });
6301
6302  
6303
6304  /*
6305  * - LGPL
6306  *
6307  * table row
6308  * 
6309  */
6310
6311 /**
6312  * @class Roo.bootstrap.TableRow
6313  * @extends Roo.bootstrap.Component
6314  * Bootstrap TableRow class
6315  * @cfg {String} cls row class
6316  * @cfg {String} align Aligns the content in a table row
6317  * @cfg {String} bgcolor Specifies a background color for a table row
6318  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6319  * @cfg {String} valign Vertical aligns the content in a table row
6320  * 
6321  * @constructor
6322  * Create a new TableRow
6323  * @param {Object} config The config object
6324  */
6325
6326 Roo.bootstrap.TableRow = function(config){
6327     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6328 };
6329
6330 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6331     
6332     cls: false,
6333     align: false,
6334     bgcolor: false,
6335     charoff: false,
6336     valign: false,
6337     
6338     getAutoCreate : function(){
6339         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6340         
6341         cfg = {
6342             tag: 'tr'
6343         }
6344             
6345         if(this.cls){
6346             cfg.cls = this.cls;
6347         }
6348         if(this.align){
6349             cfg.align = this.align;
6350         }
6351         if(this.bgcolor){
6352             cfg.bgcolor = this.bgcolor;
6353         }
6354         if(this.charoff){
6355             cfg.charoff = this.charoff;
6356         }
6357         if(this.valign){
6358             cfg.valign = this.valign;
6359         }
6360         
6361         return cfg;
6362     }
6363    
6364 });
6365
6366  
6367
6368  /*
6369  * - LGPL
6370  *
6371  * table body
6372  * 
6373  */
6374
6375 /**
6376  * @class Roo.bootstrap.TableBody
6377  * @extends Roo.bootstrap.Component
6378  * Bootstrap TableBody class
6379  * @cfg {String} cls element class
6380  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6381  * @cfg {String} align Aligns the content inside the element
6382  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6383  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6384  * 
6385  * @constructor
6386  * Create a new TableBody
6387  * @param {Object} config The config object
6388  */
6389
6390 Roo.bootstrap.TableBody = function(config){
6391     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6392 };
6393
6394 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6395     
6396     cls: false,
6397     tag: false,
6398     align: false,
6399     charoff: false,
6400     valign: false,
6401     
6402     getAutoCreate : function(){
6403         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6404         
6405         cfg = {
6406             tag: 'tbody'
6407         }
6408             
6409         if (this.cls) {
6410             cfg.cls=this.cls
6411         }
6412         if(this.tag){
6413             cfg.tag = this.tag;
6414         }
6415         
6416         if(this.align){
6417             cfg.align = this.align;
6418         }
6419         if(this.charoff){
6420             cfg.charoff = this.charoff;
6421         }
6422         if(this.valign){
6423             cfg.valign = this.valign;
6424         }
6425         
6426         return cfg;
6427     }
6428     
6429     
6430 //    initEvents : function()
6431 //    {
6432 //        
6433 //        if(!this.store){
6434 //            return;
6435 //        }
6436 //        
6437 //        this.store = Roo.factory(this.store, Roo.data);
6438 //        this.store.on('load', this.onLoad, this);
6439 //        
6440 //        this.store.load();
6441 //        
6442 //    },
6443 //    
6444 //    onLoad: function () 
6445 //    {   
6446 //        this.fireEvent('load', this);
6447 //    }
6448 //    
6449 //   
6450 });
6451
6452  
6453
6454  /*
6455  * Based on:
6456  * Ext JS Library 1.1.1
6457  * Copyright(c) 2006-2007, Ext JS, LLC.
6458  *
6459  * Originally Released Under LGPL - original licence link has changed is not relivant.
6460  *
6461  * Fork - LGPL
6462  * <script type="text/javascript">
6463  */
6464
6465 // as we use this in bootstrap.
6466 Roo.namespace('Roo.form');
6467  /**
6468  * @class Roo.form.Action
6469  * Internal Class used to handle form actions
6470  * @constructor
6471  * @param {Roo.form.BasicForm} el The form element or its id
6472  * @param {Object} config Configuration options
6473  */
6474
6475  
6476  
6477 // define the action interface
6478 Roo.form.Action = function(form, options){
6479     this.form = form;
6480     this.options = options || {};
6481 };
6482 /**
6483  * Client Validation Failed
6484  * @const 
6485  */
6486 Roo.form.Action.CLIENT_INVALID = 'client';
6487 /**
6488  * Server Validation Failed
6489  * @const 
6490  */
6491 Roo.form.Action.SERVER_INVALID = 'server';
6492  /**
6493  * Connect to Server Failed
6494  * @const 
6495  */
6496 Roo.form.Action.CONNECT_FAILURE = 'connect';
6497 /**
6498  * Reading Data from Server Failed
6499  * @const 
6500  */
6501 Roo.form.Action.LOAD_FAILURE = 'load';
6502
6503 Roo.form.Action.prototype = {
6504     type : 'default',
6505     failureType : undefined,
6506     response : undefined,
6507     result : undefined,
6508
6509     // interface method
6510     run : function(options){
6511
6512     },
6513
6514     // interface method
6515     success : function(response){
6516
6517     },
6518
6519     // interface method
6520     handleResponse : function(response){
6521
6522     },
6523
6524     // default connection failure
6525     failure : function(response){
6526         
6527         this.response = response;
6528         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6529         this.form.afterAction(this, false);
6530     },
6531
6532     processResponse : function(response){
6533         this.response = response;
6534         if(!response.responseText){
6535             return true;
6536         }
6537         this.result = this.handleResponse(response);
6538         return this.result;
6539     },
6540
6541     // utility functions used internally
6542     getUrl : function(appendParams){
6543         var url = this.options.url || this.form.url || this.form.el.dom.action;
6544         if(appendParams){
6545             var p = this.getParams();
6546             if(p){
6547                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6548             }
6549         }
6550         return url;
6551     },
6552
6553     getMethod : function(){
6554         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6555     },
6556
6557     getParams : function(){
6558         var bp = this.form.baseParams;
6559         var p = this.options.params;
6560         if(p){
6561             if(typeof p == "object"){
6562                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6563             }else if(typeof p == 'string' && bp){
6564                 p += '&' + Roo.urlEncode(bp);
6565             }
6566         }else if(bp){
6567             p = Roo.urlEncode(bp);
6568         }
6569         return p;
6570     },
6571
6572     createCallback : function(){
6573         return {
6574             success: this.success,
6575             failure: this.failure,
6576             scope: this,
6577             timeout: (this.form.timeout*1000),
6578             upload: this.form.fileUpload ? this.success : undefined
6579         };
6580     }
6581 };
6582
6583 Roo.form.Action.Submit = function(form, options){
6584     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6585 };
6586
6587 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6588     type : 'submit',
6589
6590     haveProgress : false,
6591     uploadComplete : false,
6592     
6593     // uploadProgress indicator.
6594     uploadProgress : function()
6595     {
6596         if (!this.form.progressUrl) {
6597             return;
6598         }
6599         
6600         if (!this.haveProgress) {
6601             Roo.MessageBox.progress("Uploading", "Uploading");
6602         }
6603         if (this.uploadComplete) {
6604            Roo.MessageBox.hide();
6605            return;
6606         }
6607         
6608         this.haveProgress = true;
6609    
6610         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6611         
6612         var c = new Roo.data.Connection();
6613         c.request({
6614             url : this.form.progressUrl,
6615             params: {
6616                 id : uid
6617             },
6618             method: 'GET',
6619             success : function(req){
6620                //console.log(data);
6621                 var rdata = false;
6622                 var edata;
6623                 try  {
6624                    rdata = Roo.decode(req.responseText)
6625                 } catch (e) {
6626                     Roo.log("Invalid data from server..");
6627                     Roo.log(edata);
6628                     return;
6629                 }
6630                 if (!rdata || !rdata.success) {
6631                     Roo.log(rdata);
6632                     Roo.MessageBox.alert(Roo.encode(rdata));
6633                     return;
6634                 }
6635                 var data = rdata.data;
6636                 
6637                 if (this.uploadComplete) {
6638                    Roo.MessageBox.hide();
6639                    return;
6640                 }
6641                    
6642                 if (data){
6643                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6644                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6645                     );
6646                 }
6647                 this.uploadProgress.defer(2000,this);
6648             },
6649        
6650             failure: function(data) {
6651                 Roo.log('progress url failed ');
6652                 Roo.log(data);
6653             },
6654             scope : this
6655         });
6656            
6657     },
6658     
6659     
6660     run : function()
6661     {
6662         // run get Values on the form, so it syncs any secondary forms.
6663         this.form.getValues();
6664         
6665         var o = this.options;
6666         var method = this.getMethod();
6667         var isPost = method == 'POST';
6668         if(o.clientValidation === false || this.form.isValid()){
6669             
6670             if (this.form.progressUrl) {
6671                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6672                     (new Date() * 1) + '' + Math.random());
6673                     
6674             } 
6675             
6676             
6677             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6678                 form:this.form.el.dom,
6679                 url:this.getUrl(!isPost),
6680                 method: method,
6681                 params:isPost ? this.getParams() : null,
6682                 isUpload: this.form.fileUpload
6683             }));
6684             
6685             this.uploadProgress();
6686
6687         }else if (o.clientValidation !== false){ // client validation failed
6688             this.failureType = Roo.form.Action.CLIENT_INVALID;
6689             this.form.afterAction(this, false);
6690         }
6691     },
6692
6693     success : function(response)
6694     {
6695         this.uploadComplete= true;
6696         if (this.haveProgress) {
6697             Roo.MessageBox.hide();
6698         }
6699         
6700         
6701         var result = this.processResponse(response);
6702         if(result === true || result.success){
6703             this.form.afterAction(this, true);
6704             return;
6705         }
6706         if(result.errors){
6707             this.form.markInvalid(result.errors);
6708             this.failureType = Roo.form.Action.SERVER_INVALID;
6709         }
6710         this.form.afterAction(this, false);
6711     },
6712     failure : function(response)
6713     {
6714         this.uploadComplete= true;
6715         if (this.haveProgress) {
6716             Roo.MessageBox.hide();
6717         }
6718         
6719         this.response = response;
6720         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6721         this.form.afterAction(this, false);
6722     },
6723     
6724     handleResponse : function(response){
6725         if(this.form.errorReader){
6726             var rs = this.form.errorReader.read(response);
6727             var errors = [];
6728             if(rs.records){
6729                 for(var i = 0, len = rs.records.length; i < len; i++) {
6730                     var r = rs.records[i];
6731                     errors[i] = r.data;
6732                 }
6733             }
6734             if(errors.length < 1){
6735                 errors = null;
6736             }
6737             return {
6738                 success : rs.success,
6739                 errors : errors
6740             };
6741         }
6742         var ret = false;
6743         try {
6744             ret = Roo.decode(response.responseText);
6745         } catch (e) {
6746             ret = {
6747                 success: false,
6748                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6749                 errors : []
6750             };
6751         }
6752         return ret;
6753         
6754     }
6755 });
6756
6757
6758 Roo.form.Action.Load = function(form, options){
6759     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6760     this.reader = this.form.reader;
6761 };
6762
6763 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6764     type : 'load',
6765
6766     run : function(){
6767         
6768         Roo.Ajax.request(Roo.apply(
6769                 this.createCallback(), {
6770                     method:this.getMethod(),
6771                     url:this.getUrl(false),
6772                     params:this.getParams()
6773         }));
6774     },
6775
6776     success : function(response){
6777         
6778         var result = this.processResponse(response);
6779         if(result === true || !result.success || !result.data){
6780             this.failureType = Roo.form.Action.LOAD_FAILURE;
6781             this.form.afterAction(this, false);
6782             return;
6783         }
6784         this.form.clearInvalid();
6785         this.form.setValues(result.data);
6786         this.form.afterAction(this, true);
6787     },
6788
6789     handleResponse : function(response){
6790         if(this.form.reader){
6791             var rs = this.form.reader.read(response);
6792             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6793             return {
6794                 success : rs.success,
6795                 data : data
6796             };
6797         }
6798         return Roo.decode(response.responseText);
6799     }
6800 });
6801
6802 Roo.form.Action.ACTION_TYPES = {
6803     'load' : Roo.form.Action.Load,
6804     'submit' : Roo.form.Action.Submit
6805 };/*
6806  * - LGPL
6807  *
6808  * form
6809  * 
6810  */
6811
6812 /**
6813  * @class Roo.bootstrap.Form
6814  * @extends Roo.bootstrap.Component
6815  * Bootstrap Form class
6816  * @cfg {String} method  GET | POST (default POST)
6817  * @cfg {String} labelAlign top | left (default top)
6818  * @cfg {String} align left  | right - for navbars
6819  * @cfg {Boolean} loadMask load mask when submit (default true)
6820
6821  * 
6822  * @constructor
6823  * Create a new Form
6824  * @param {Object} config The config object
6825  */
6826
6827
6828 Roo.bootstrap.Form = function(config){
6829     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6830     this.addEvents({
6831         /**
6832          * @event clientvalidation
6833          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6834          * @param {Form} this
6835          * @param {Boolean} valid true if the form has passed client-side validation
6836          */
6837         clientvalidation: true,
6838         /**
6839          * @event beforeaction
6840          * Fires before any action is performed. Return false to cancel the action.
6841          * @param {Form} this
6842          * @param {Action} action The action to be performed
6843          */
6844         beforeaction: true,
6845         /**
6846          * @event actionfailed
6847          * Fires when an action fails.
6848          * @param {Form} this
6849          * @param {Action} action The action that failed
6850          */
6851         actionfailed : true,
6852         /**
6853          * @event actioncomplete
6854          * Fires when an action is completed.
6855          * @param {Form} this
6856          * @param {Action} action The action that completed
6857          */
6858         actioncomplete : true
6859     });
6860     
6861 };
6862
6863 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6864       
6865      /**
6866      * @cfg {String} method
6867      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6868      */
6869     method : 'POST',
6870     /**
6871      * @cfg {String} url
6872      * The URL to use for form actions if one isn't supplied in the action options.
6873      */
6874     /**
6875      * @cfg {Boolean} fileUpload
6876      * Set to true if this form is a file upload.
6877      */
6878      
6879     /**
6880      * @cfg {Object} baseParams
6881      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6882      */
6883       
6884     /**
6885      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6886      */
6887     timeout: 30,
6888     /**
6889      * @cfg {Sting} align (left|right) for navbar forms
6890      */
6891     align : 'left',
6892
6893     // private
6894     activeAction : null,
6895  
6896     /**
6897      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6898      * element by passing it or its id or mask the form itself by passing in true.
6899      * @type Mixed
6900      */
6901     waitMsgTarget : false,
6902     
6903     loadMask : true,
6904     
6905     getAutoCreate : function(){
6906         
6907         var cfg = {
6908             tag: 'form',
6909             method : this.method || 'POST',
6910             id : this.id || Roo.id(),
6911             cls : ''
6912         }
6913         if (this.parent().xtype.match(/^Nav/)) {
6914             cfg.cls = 'navbar-form navbar-' + this.align;
6915             
6916         }
6917         
6918         if (this.labelAlign == 'left' ) {
6919             cfg.cls += ' form-horizontal';
6920         }
6921         
6922         
6923         return cfg;
6924     },
6925     initEvents : function()
6926     {
6927         this.el.on('submit', this.onSubmit, this);
6928         // this was added as random key presses on the form where triggering form submit.
6929         this.el.on('keypress', function(e) {
6930             if (e.getCharCode() != 13) {
6931                 return true;
6932             }
6933             // we might need to allow it for textareas.. and some other items.
6934             // check e.getTarget().
6935             
6936             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6937                 return true;
6938             }
6939         
6940             Roo.log("keypress blocked");
6941             
6942             e.preventDefault();
6943             return false;
6944         });
6945         
6946     },
6947     // private
6948     onSubmit : function(e){
6949         e.stopEvent();
6950     },
6951     
6952      /**
6953      * Returns true if client-side validation on the form is successful.
6954      * @return Boolean
6955      */
6956     isValid : function(){
6957         var items = this.getItems();
6958         var valid = true;
6959         items.each(function(f){
6960            if(!f.validate()){
6961                valid = false;
6962                
6963            }
6964         });
6965         return valid;
6966     },
6967     /**
6968      * Returns true if any fields in this form have changed since their original load.
6969      * @return Boolean
6970      */
6971     isDirty : function(){
6972         var dirty = false;
6973         var items = this.getItems();
6974         items.each(function(f){
6975            if(f.isDirty()){
6976                dirty = true;
6977                return false;
6978            }
6979            return true;
6980         });
6981         return dirty;
6982     },
6983      /**
6984      * Performs a predefined action (submit or load) or custom actions you define on this form.
6985      * @param {String} actionName The name of the action type
6986      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6987      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6988      * accept other config options):
6989      * <pre>
6990 Property          Type             Description
6991 ----------------  ---------------  ----------------------------------------------------------------------------------
6992 url               String           The url for the action (defaults to the form's url)
6993 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6994 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6995 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6996                                    validate the form on the client (defaults to false)
6997      * </pre>
6998      * @return {BasicForm} this
6999      */
7000     doAction : function(action, options){
7001         if(typeof action == 'string'){
7002             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7003         }
7004         if(this.fireEvent('beforeaction', this, action) !== false){
7005             this.beforeAction(action);
7006             action.run.defer(100, action);
7007         }
7008         return this;
7009     },
7010     
7011     // private
7012     beforeAction : function(action){
7013         var o = action.options;
7014         
7015         if(this.loadMask){
7016             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7017         }
7018         // not really supported yet.. ??
7019         
7020         //if(this.waitMsgTarget === true){
7021         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7022         //}else if(this.waitMsgTarget){
7023         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7024         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7025         //}else {
7026         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7027        // }
7028          
7029     },
7030
7031     // private
7032     afterAction : function(action, success){
7033         this.activeAction = null;
7034         var o = action.options;
7035         
7036         //if(this.waitMsgTarget === true){
7037             this.el.unmask();
7038         //}else if(this.waitMsgTarget){
7039         //    this.waitMsgTarget.unmask();
7040         //}else{
7041         //    Roo.MessageBox.updateProgress(1);
7042         //    Roo.MessageBox.hide();
7043        // }
7044         // 
7045         if(success){
7046             if(o.reset){
7047                 this.reset();
7048             }
7049             Roo.callback(o.success, o.scope, [this, action]);
7050             this.fireEvent('actioncomplete', this, action);
7051             
7052         }else{
7053             
7054             // failure condition..
7055             // we have a scenario where updates need confirming.
7056             // eg. if a locking scenario exists..
7057             // we look for { errors : { needs_confirm : true }} in the response.
7058             if (
7059                 (typeof(action.result) != 'undefined')  &&
7060                 (typeof(action.result.errors) != 'undefined')  &&
7061                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7062            ){
7063                 var _t = this;
7064                 Roo.log("not supported yet");
7065                  /*
7066                 
7067                 Roo.MessageBox.confirm(
7068                     "Change requires confirmation",
7069                     action.result.errorMsg,
7070                     function(r) {
7071                         if (r != 'yes') {
7072                             return;
7073                         }
7074                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7075                     }
7076                     
7077                 );
7078                 */
7079                 
7080                 
7081                 return;
7082             }
7083             
7084             Roo.callback(o.failure, o.scope, [this, action]);
7085             // show an error message if no failed handler is set..
7086             if (!this.hasListener('actionfailed')) {
7087                 Roo.log("need to add dialog support");
7088                 /*
7089                 Roo.MessageBox.alert("Error",
7090                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7091                         action.result.errorMsg :
7092                         "Saving Failed, please check your entries or try again"
7093                 );
7094                 */
7095             }
7096             
7097             this.fireEvent('actionfailed', this, action);
7098         }
7099         
7100     },
7101     /**
7102      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7103      * @param {String} id The value to search for
7104      * @return Field
7105      */
7106     findField : function(id){
7107         var items = this.getItems();
7108         var field = items.get(id);
7109         if(!field){
7110              items.each(function(f){
7111                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7112                     field = f;
7113                     return false;
7114                 }
7115                 return true;
7116             });
7117         }
7118         return field || null;
7119     },
7120      /**
7121      * Mark fields in this form invalid in bulk.
7122      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7123      * @return {BasicForm} this
7124      */
7125     markInvalid : function(errors){
7126         if(errors instanceof Array){
7127             for(var i = 0, len = errors.length; i < len; i++){
7128                 var fieldError = errors[i];
7129                 var f = this.findField(fieldError.id);
7130                 if(f){
7131                     f.markInvalid(fieldError.msg);
7132                 }
7133             }
7134         }else{
7135             var field, id;
7136             for(id in errors){
7137                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7138                     field.markInvalid(errors[id]);
7139                 }
7140             }
7141         }
7142         //Roo.each(this.childForms || [], function (f) {
7143         //    f.markInvalid(errors);
7144         //});
7145         
7146         return this;
7147     },
7148
7149     /**
7150      * Set values for fields in this form in bulk.
7151      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7152      * @return {BasicForm} this
7153      */
7154     setValues : function(values){
7155         if(values instanceof Array){ // array of objects
7156             for(var i = 0, len = values.length; i < len; i++){
7157                 var v = values[i];
7158                 var f = this.findField(v.id);
7159                 if(f){
7160                     f.setValue(v.value);
7161                     if(this.trackResetOnLoad){
7162                         f.originalValue = f.getValue();
7163                     }
7164                 }
7165             }
7166         }else{ // object hash
7167             var field, id;
7168             for(id in values){
7169                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7170                     
7171                     if (field.setFromData && 
7172                         field.valueField && 
7173                         field.displayField &&
7174                         // combos' with local stores can 
7175                         // be queried via setValue()
7176                         // to set their value..
7177                         (field.store && !field.store.isLocal)
7178                         ) {
7179                         // it's a combo
7180                         var sd = { };
7181                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7182                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7183                         field.setFromData(sd);
7184                         
7185                     } else {
7186                         field.setValue(values[id]);
7187                     }
7188                     
7189                     
7190                     if(this.trackResetOnLoad){
7191                         field.originalValue = field.getValue();
7192                     }
7193                 }
7194             }
7195         }
7196          
7197         //Roo.each(this.childForms || [], function (f) {
7198         //    f.setValues(values);
7199         //});
7200                 
7201         return this;
7202     },
7203
7204     /**
7205      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7206      * they are returned as an array.
7207      * @param {Boolean} asString
7208      * @return {Object}
7209      */
7210     getValues : function(asString){
7211         //if (this.childForms) {
7212             // copy values from the child forms
7213         //    Roo.each(this.childForms, function (f) {
7214         //        this.setValues(f.getValues());
7215         //    }, this);
7216         //}
7217         
7218         
7219         
7220         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7221         if(asString === true){
7222             return fs;
7223         }
7224         return Roo.urlDecode(fs);
7225     },
7226     
7227     /**
7228      * Returns the fields in this form as an object with key/value pairs. 
7229      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7230      * @return {Object}
7231      */
7232     getFieldValues : function(with_hidden)
7233     {
7234         var items = this.getItems();
7235         var ret = {};
7236         items.each(function(f){
7237             if (!f.getName()) {
7238                 return;
7239             }
7240             var v = f.getValue();
7241             if (f.inputType =='radio') {
7242                 if (typeof(ret[f.getName()]) == 'undefined') {
7243                     ret[f.getName()] = ''; // empty..
7244                 }
7245                 
7246                 if (!f.el.dom.checked) {
7247                     return;
7248                     
7249                 }
7250                 v = f.el.dom.value;
7251                 
7252             }
7253             
7254             // not sure if this supported any more..
7255             if ((typeof(v) == 'object') && f.getRawValue) {
7256                 v = f.getRawValue() ; // dates..
7257             }
7258             // combo boxes where name != hiddenName...
7259             if (f.name != f.getName()) {
7260                 ret[f.name] = f.getRawValue();
7261             }
7262             ret[f.getName()] = v;
7263         });
7264         
7265         return ret;
7266     },
7267
7268     /**
7269      * Clears all invalid messages in this form.
7270      * @return {BasicForm} this
7271      */
7272     clearInvalid : function(){
7273         var items = this.getItems();
7274         
7275         items.each(function(f){
7276            f.clearInvalid();
7277         });
7278         
7279         
7280         
7281         return this;
7282     },
7283
7284     /**
7285      * Resets this form.
7286      * @return {BasicForm} this
7287      */
7288     reset : function(){
7289         var items = this.getItems();
7290         items.each(function(f){
7291             f.reset();
7292         });
7293         
7294         Roo.each(this.childForms || [], function (f) {
7295             f.reset();
7296         });
7297        
7298         
7299         return this;
7300     },
7301     getItems : function()
7302     {
7303         var r=new Roo.util.MixedCollection(false, function(o){
7304             return o.id || (o.id = Roo.id());
7305         });
7306         var iter = function(el) {
7307             if (el.inputEl) {
7308                 r.add(el);
7309             }
7310             if (!el.items) {
7311                 return;
7312             }
7313             Roo.each(el.items,function(e) {
7314                 iter(e);
7315             });
7316             
7317             
7318         };
7319         
7320         iter(this);
7321         return r;
7322         
7323         
7324         
7325         
7326     }
7327     
7328 });
7329
7330  
7331 /*
7332  * Based on:
7333  * Ext JS Library 1.1.1
7334  * Copyright(c) 2006-2007, Ext JS, LLC.
7335  *
7336  * Originally Released Under LGPL - original licence link has changed is not relivant.
7337  *
7338  * Fork - LGPL
7339  * <script type="text/javascript">
7340  */
7341 /**
7342  * @class Roo.form.VTypes
7343  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7344  * @singleton
7345  */
7346 Roo.form.VTypes = function(){
7347     // closure these in so they are only created once.
7348     var alpha = /^[a-zA-Z_]+$/;
7349     var alphanum = /^[a-zA-Z0-9_]+$/;
7350     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7351     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7352
7353     // All these messages and functions are configurable
7354     return {
7355         /**
7356          * The function used to validate email addresses
7357          * @param {String} value The email address
7358          */
7359         'email' : function(v){
7360             return email.test(v);
7361         },
7362         /**
7363          * The error text to display when the email validation function returns false
7364          * @type String
7365          */
7366         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7367         /**
7368          * The keystroke filter mask to be applied on email input
7369          * @type RegExp
7370          */
7371         'emailMask' : /[a-z0-9_\.\-@]/i,
7372
7373         /**
7374          * The function used to validate URLs
7375          * @param {String} value The URL
7376          */
7377         'url' : function(v){
7378             return url.test(v);
7379         },
7380         /**
7381          * The error text to display when the url validation function returns false
7382          * @type String
7383          */
7384         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7385         
7386         /**
7387          * The function used to validate alpha values
7388          * @param {String} value The value
7389          */
7390         'alpha' : function(v){
7391             return alpha.test(v);
7392         },
7393         /**
7394          * The error text to display when the alpha validation function returns false
7395          * @type String
7396          */
7397         'alphaText' : 'This field should only contain letters and _',
7398         /**
7399          * The keystroke filter mask to be applied on alpha input
7400          * @type RegExp
7401          */
7402         'alphaMask' : /[a-z_]/i,
7403
7404         /**
7405          * The function used to validate alphanumeric values
7406          * @param {String} value The value
7407          */
7408         'alphanum' : function(v){
7409             return alphanum.test(v);
7410         },
7411         /**
7412          * The error text to display when the alphanumeric validation function returns false
7413          * @type String
7414          */
7415         'alphanumText' : 'This field should only contain letters, numbers and _',
7416         /**
7417          * The keystroke filter mask to be applied on alphanumeric input
7418          * @type RegExp
7419          */
7420         'alphanumMask' : /[a-z0-9_]/i
7421     };
7422 }();/*
7423  * - LGPL
7424  *
7425  * Input
7426  * 
7427  */
7428
7429 /**
7430  * @class Roo.bootstrap.Input
7431  * @extends Roo.bootstrap.Component
7432  * Bootstrap Input class
7433  * @cfg {Boolean} disabled is it disabled
7434  * @cfg {String} fieldLabel - the label associated
7435  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7436  * @cfg {String} name name of the input
7437  * @cfg {string} fieldLabel - the label associated
7438  * @cfg {string}  inputType - input / file submit ...
7439  * @cfg {string} placeholder - placeholder to put in text.
7440  * @cfg {string}  before - input group add on before
7441  * @cfg {string} after - input group add on after
7442  * @cfg {string} size - (lg|sm) or leave empty..
7443  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7444  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7445  * @cfg {Number} md colspan out of 12 for computer-sized screens
7446  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7447  * @cfg {string} value default value of the input
7448  * @cfg {Number} labelWidth set the width of label (0-12)
7449  * @cfg {String} labelAlign (top|left)
7450  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7451  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7452
7453  * @cfg {String} align (left|center|right) Default left
7454  * 
7455  * 
7456  * 
7457  * @constructor
7458  * Create a new Input
7459  * @param {Object} config The config object
7460  */
7461
7462 Roo.bootstrap.Input = function(config){
7463     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7464    
7465         this.addEvents({
7466             /**
7467              * @event focus
7468              * Fires when this field receives input focus.
7469              * @param {Roo.form.Field} this
7470              */
7471             focus : true,
7472             /**
7473              * @event blur
7474              * Fires when this field loses input focus.
7475              * @param {Roo.form.Field} this
7476              */
7477             blur : true,
7478             /**
7479              * @event specialkey
7480              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7481              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7482              * @param {Roo.form.Field} this
7483              * @param {Roo.EventObject} e The event object
7484              */
7485             specialkey : true,
7486             /**
7487              * @event change
7488              * Fires just before the field blurs if the field value has changed.
7489              * @param {Roo.form.Field} this
7490              * @param {Mixed} newValue The new value
7491              * @param {Mixed} oldValue The original value
7492              */
7493             change : true,
7494             /**
7495              * @event invalid
7496              * Fires after the field has been marked as invalid.
7497              * @param {Roo.form.Field} this
7498              * @param {String} msg The validation message
7499              */
7500             invalid : true,
7501             /**
7502              * @event valid
7503              * Fires after the field has been validated with no errors.
7504              * @param {Roo.form.Field} this
7505              */
7506             valid : true,
7507              /**
7508              * @event keyup
7509              * Fires after the key up
7510              * @param {Roo.form.Field} this
7511              * @param {Roo.EventObject}  e The event Object
7512              */
7513             keyup : true
7514         });
7515 };
7516
7517 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7518      /**
7519      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7520       automatic validation (defaults to "keyup").
7521      */
7522     validationEvent : "keyup",
7523      /**
7524      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7525      */
7526     validateOnBlur : true,
7527     /**
7528      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7529      */
7530     validationDelay : 250,
7531      /**
7532      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7533      */
7534     focusClass : "x-form-focus",  // not needed???
7535     
7536        
7537     /**
7538      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7539      */
7540     invalidClass : "has-warning",
7541     
7542     /**
7543      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7544      */
7545     validClass : "has-success",
7546     
7547     /**
7548      * @cfg {Boolean} hasFeedback (true|false) default true
7549      */
7550     hasFeedback : true,
7551     
7552     /**
7553      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7554      */
7555     invalidFeedbackClass : "glyphicon-warning-sign",
7556     
7557     /**
7558      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7559      */
7560     validFeedbackClass : "glyphicon-ok",
7561     
7562     /**
7563      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7564      */
7565     selectOnFocus : false,
7566     
7567      /**
7568      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7569      */
7570     maskRe : null,
7571        /**
7572      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7573      */
7574     vtype : null,
7575     
7576       /**
7577      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7578      */
7579     disableKeyFilter : false,
7580     
7581        /**
7582      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7583      */
7584     disabled : false,
7585      /**
7586      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7587      */
7588     allowBlank : true,
7589     /**
7590      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7591      */
7592     blankText : "This field is required",
7593     
7594      /**
7595      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7596      */
7597     minLength : 0,
7598     /**
7599      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7600      */
7601     maxLength : Number.MAX_VALUE,
7602     /**
7603      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7604      */
7605     minLengthText : "The minimum length for this field is {0}",
7606     /**
7607      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7608      */
7609     maxLengthText : "The maximum length for this field is {0}",
7610   
7611     
7612     /**
7613      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7614      * If available, this function will be called only after the basic validators all return true, and will be passed the
7615      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7616      */
7617     validator : null,
7618     /**
7619      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7620      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7621      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7622      */
7623     regex : null,
7624     /**
7625      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7626      */
7627     regexText : "",
7628     
7629     autocomplete: false,
7630     
7631     
7632     fieldLabel : '',
7633     inputType : 'text',
7634     
7635     name : false,
7636     placeholder: false,
7637     before : false,
7638     after : false,
7639     size : false,
7640     hasFocus : false,
7641     preventMark: false,
7642     isFormField : true,
7643     value : '',
7644     labelWidth : 2,
7645     labelAlign : false,
7646     readOnly : false,
7647     align : false,
7648     formatedValue : false,
7649     
7650     parentLabelAlign : function()
7651     {
7652         var parent = this;
7653         while (parent.parent()) {
7654             parent = parent.parent();
7655             if (typeof(parent.labelAlign) !='undefined') {
7656                 return parent.labelAlign;
7657             }
7658         }
7659         return 'left';
7660         
7661     },
7662     
7663     getAutoCreate : function(){
7664         
7665         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7666         
7667         var id = Roo.id();
7668         
7669         var cfg = {};
7670         
7671         if(this.inputType != 'hidden'){
7672             cfg.cls = 'form-group' //input-group
7673         }
7674         
7675         var input =  {
7676             tag: 'input',
7677             id : id,
7678             type : this.inputType,
7679             value : this.value,
7680             cls : 'form-control',
7681             placeholder : this.placeholder || '',
7682             autocomplete : this.autocomplete || 'new-password'
7683         };
7684         
7685         
7686         if(this.align){
7687             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7688         }
7689         
7690         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7691             input.maxLength = this.maxLength;
7692         }
7693         
7694         if (this.disabled) {
7695             input.disabled=true;
7696         }
7697         
7698         if (this.readOnly) {
7699             input.readonly=true;
7700         }
7701         
7702         if (this.name) {
7703             input.name = this.name;
7704         }
7705         if (this.size) {
7706             input.cls += ' input-' + this.size;
7707         }
7708         var settings=this;
7709         ['xs','sm','md','lg'].map(function(size){
7710             if (settings[size]) {
7711                 cfg.cls += ' col-' + size + '-' + settings[size];
7712             }
7713         });
7714         
7715         var inputblock = input;
7716         
7717         var feedback = {
7718             tag: 'span',
7719             cls: 'glyphicon form-control-feedback'
7720         };
7721             
7722         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7723             
7724             inputblock = {
7725                 cls : 'has-feedback',
7726                 cn :  [
7727                     input,
7728                     feedback
7729                 ] 
7730             };  
7731         }
7732         
7733         if (this.before || this.after) {
7734             
7735             inputblock = {
7736                 cls : 'input-group',
7737                 cn :  [] 
7738             };
7739             
7740             if (this.before && typeof(this.before) == 'string') {
7741                 
7742                 inputblock.cn.push({
7743                     tag :'span',
7744                     cls : 'roo-input-before input-group-addon',
7745                     html : this.before
7746                 });
7747             }
7748             if (this.before && typeof(this.before) == 'object') {
7749                 this.before = Roo.factory(this.before);
7750                 Roo.log(this.before);
7751                 inputblock.cn.push({
7752                     tag :'span',
7753                     cls : 'roo-input-before input-group-' +
7754                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7755                 });
7756             }
7757             
7758             inputblock.cn.push(input);
7759             
7760             if (this.after && typeof(this.after) == 'string') {
7761                 inputblock.cn.push({
7762                     tag :'span',
7763                     cls : 'roo-input-after input-group-addon',
7764                     html : this.after
7765                 });
7766             }
7767             if (this.after && typeof(this.after) == 'object') {
7768                 this.after = Roo.factory(this.after);
7769                 Roo.log(this.after);
7770                 inputblock.cn.push({
7771                     tag :'span',
7772                     cls : 'roo-input-after input-group-' +
7773                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7774                 });
7775             }
7776             
7777             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7778                 inputblock.cls += ' has-feedback';
7779                 inputblock.cn.push(feedback);
7780             }
7781         };
7782         
7783         if (align ==='left' && this.fieldLabel.length) {
7784                 Roo.log("left and has label");
7785                 cfg.cn = [
7786                     
7787                     {
7788                         tag: 'label',
7789                         'for' :  id,
7790                         cls : 'control-label col-sm-' + this.labelWidth,
7791                         html : this.fieldLabel
7792                         
7793                     },
7794                     {
7795                         cls : "col-sm-" + (12 - this.labelWidth), 
7796                         cn: [
7797                             inputblock
7798                         ]
7799                     }
7800                     
7801                 ];
7802         } else if ( this.fieldLabel.length) {
7803                 Roo.log(" label");
7804                  cfg.cn = [
7805                    
7806                     {
7807                         tag: 'label',
7808                         //cls : 'input-group-addon',
7809                         html : this.fieldLabel
7810                         
7811                     },
7812                     
7813                     inputblock
7814                     
7815                 ];
7816
7817         } else {
7818             
7819                 Roo.log(" no label && no align");
7820                 cfg.cn = [
7821                     
7822                         inputblock
7823                     
7824                 ];
7825                 
7826                 
7827         };
7828         Roo.log('input-parentType: ' + this.parentType);
7829         
7830         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7831            cfg.cls += ' navbar-form';
7832            Roo.log(cfg);
7833         }
7834         
7835         return cfg;
7836         
7837     },
7838     /**
7839      * return the real input element.
7840      */
7841     inputEl: function ()
7842     {
7843         return this.el.select('input.form-control',true).first();
7844     },
7845     
7846     tooltipEl : function()
7847     {
7848         return this.inputEl();
7849     },
7850     
7851     setDisabled : function(v)
7852     {
7853         var i  = this.inputEl().dom;
7854         if (!v) {
7855             i.removeAttribute('disabled');
7856             return;
7857             
7858         }
7859         i.setAttribute('disabled','true');
7860     },
7861     initEvents : function()
7862     {
7863           
7864         this.inputEl().on("keydown" , this.fireKey,  this);
7865         this.inputEl().on("focus", this.onFocus,  this);
7866         this.inputEl().on("blur", this.onBlur,  this);
7867         
7868         this.inputEl().relayEvent('keyup', this);
7869
7870         // reference to original value for reset
7871         this.originalValue = this.getValue();
7872         //Roo.form.TextField.superclass.initEvents.call(this);
7873         if(this.validationEvent == 'keyup'){
7874             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7875             this.inputEl().on('keyup', this.filterValidation, this);
7876         }
7877         else if(this.validationEvent !== false){
7878             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7879         }
7880         
7881         if(this.selectOnFocus){
7882             this.on("focus", this.preFocus, this);
7883             
7884         }
7885         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7886             this.inputEl().on("keypress", this.filterKeys, this);
7887         }
7888        /* if(this.grow){
7889             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7890             this.el.on("click", this.autoSize,  this);
7891         }
7892         */
7893         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7894             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7895         }
7896         
7897         if (typeof(this.before) == 'object') {
7898             this.before.render(this.el.select('.roo-input-before',true).first());
7899         }
7900         if (typeof(this.after) == 'object') {
7901             this.after.render(this.el.select('.roo-input-after',true).first());
7902         }
7903         
7904         
7905     },
7906     filterValidation : function(e){
7907         if(!e.isNavKeyPress()){
7908             this.validationTask.delay(this.validationDelay);
7909         }
7910     },
7911      /**
7912      * Validates the field value
7913      * @return {Boolean} True if the value is valid, else false
7914      */
7915     validate : function(){
7916         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7917         if(this.disabled || this.validateValue(this.getRawValue())){
7918             this.markValid();
7919             return true;
7920         }
7921         
7922         this.markInvalid();
7923         return false;
7924     },
7925     
7926     
7927     /**
7928      * Validates a value according to the field's validation rules and marks the field as invalid
7929      * if the validation fails
7930      * @param {Mixed} value The value to validate
7931      * @return {Boolean} True if the value is valid, else false
7932      */
7933     validateValue : function(value){
7934         if(value.length < 1)  { // if it's blank
7935             if(this.allowBlank){
7936                 return true;
7937             }
7938             return false;
7939         }
7940         
7941         if(value.length < this.minLength){
7942             return false;
7943         }
7944         if(value.length > this.maxLength){
7945             return false;
7946         }
7947         if(this.vtype){
7948             var vt = Roo.form.VTypes;
7949             if(!vt[this.vtype](value, this)){
7950                 return false;
7951             }
7952         }
7953         if(typeof this.validator == "function"){
7954             var msg = this.validator(value);
7955             if(msg !== true){
7956                 return false;
7957             }
7958         }
7959         
7960         if(this.regex && !this.regex.test(value)){
7961             return false;
7962         }
7963         
7964         return true;
7965     },
7966
7967     
7968     
7969      // private
7970     fireKey : function(e){
7971         //Roo.log('field ' + e.getKey());
7972         if(e.isNavKeyPress()){
7973             this.fireEvent("specialkey", this, e);
7974         }
7975     },
7976     focus : function (selectText){
7977         if(this.rendered){
7978             this.inputEl().focus();
7979             if(selectText === true){
7980                 this.inputEl().dom.select();
7981             }
7982         }
7983         return this;
7984     } ,
7985     
7986     onFocus : function(){
7987         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7988            // this.el.addClass(this.focusClass);
7989         }
7990         if(!this.hasFocus){
7991             this.hasFocus = true;
7992             this.startValue = this.getValue();
7993             this.fireEvent("focus", this);
7994         }
7995     },
7996     
7997     beforeBlur : Roo.emptyFn,
7998
7999     
8000     // private
8001     onBlur : function(){
8002         this.beforeBlur();
8003         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8004             //this.el.removeClass(this.focusClass);
8005         }
8006         this.hasFocus = false;
8007         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8008             this.validate();
8009         }
8010         var v = this.getValue();
8011         if(String(v) !== String(this.startValue)){
8012             this.fireEvent('change', this, v, this.startValue);
8013         }
8014         this.fireEvent("blur", this);
8015     },
8016     
8017     /**
8018      * Resets the current field value to the originally loaded value and clears any validation messages
8019      */
8020     reset : function(){
8021         this.setValue(this.originalValue);
8022         this.validate();
8023     },
8024      /**
8025      * Returns the name of the field
8026      * @return {Mixed} name The name field
8027      */
8028     getName: function(){
8029         return this.name;
8030     },
8031      /**
8032      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8033      * @return {Mixed} value The field value
8034      */
8035     getValue : function(){
8036         
8037         var v = this.inputEl().getValue();
8038         
8039         return v;
8040     },
8041     /**
8042      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8043      * @return {Mixed} value The field value
8044      */
8045     getRawValue : function(){
8046         var v = this.inputEl().getValue();
8047         
8048         return v;
8049     },
8050     
8051     /**
8052      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8053      * @param {Mixed} value The value to set
8054      */
8055     setRawValue : function(v){
8056         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8057     },
8058     
8059     selectText : function(start, end){
8060         var v = this.getRawValue();
8061         if(v.length > 0){
8062             start = start === undefined ? 0 : start;
8063             end = end === undefined ? v.length : end;
8064             var d = this.inputEl().dom;
8065             if(d.setSelectionRange){
8066                 d.setSelectionRange(start, end);
8067             }else if(d.createTextRange){
8068                 var range = d.createTextRange();
8069                 range.moveStart("character", start);
8070                 range.moveEnd("character", v.length-end);
8071                 range.select();
8072             }
8073         }
8074     },
8075     
8076     /**
8077      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8078      * @param {Mixed} value The value to set
8079      */
8080     setValue : function(v){
8081         this.value = v;
8082         if(this.rendered){
8083             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8084             this.validate();
8085         }
8086     },
8087     
8088     /*
8089     processValue : function(value){
8090         if(this.stripCharsRe){
8091             var newValue = value.replace(this.stripCharsRe, '');
8092             if(newValue !== value){
8093                 this.setRawValue(newValue);
8094                 return newValue;
8095             }
8096         }
8097         return value;
8098     },
8099   */
8100     preFocus : function(){
8101         
8102         if(this.selectOnFocus){
8103             this.inputEl().dom.select();
8104         }
8105     },
8106     filterKeys : function(e){
8107         var k = e.getKey();
8108         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8109             return;
8110         }
8111         var c = e.getCharCode(), cc = String.fromCharCode(c);
8112         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8113             return;
8114         }
8115         if(!this.maskRe.test(cc)){
8116             e.stopEvent();
8117         }
8118     },
8119      /**
8120      * Clear any invalid styles/messages for this field
8121      */
8122     clearInvalid : function(){
8123         
8124         if(!this.el || this.preventMark){ // not rendered
8125             return;
8126         }
8127         this.el.removeClass(this.invalidClass);
8128         
8129         this.fireEvent('valid', this);
8130     },
8131     
8132      /**
8133      * Mark this field as valid
8134      */
8135     markValid : function(){
8136         if(!this.el  || this.preventMark){ // not rendered
8137             return;
8138         }
8139         
8140         this.el.removeClass([this.invalidClass, this.validClass]);
8141         
8142         if(this.disabled || this.allowBlank){
8143             return;
8144         }
8145         
8146         this.el.addClass(this.validClass);
8147         
8148         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8149             
8150             var feedback = this.el.select('.form-control-feedback', true).first();
8151             
8152             if(feedback){
8153                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8154                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8155             }
8156             
8157         }
8158         
8159         this.fireEvent('valid', this);
8160     },
8161     
8162      /**
8163      * Mark this field as invalid
8164      * @param {String} msg The validation message
8165      */
8166     markInvalid : function(msg){
8167         if(!this.el  || this.preventMark){ // not rendered
8168             return;
8169         }
8170         
8171         this.el.removeClass([this.invalidClass, this.validClass]);
8172         
8173         if(this.disabled || this.allowBlank){
8174             return;
8175         }
8176         
8177         this.el.addClass(this.invalidClass);
8178         
8179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8180             
8181             var feedback = this.el.select('.form-control-feedback', true).first();
8182             
8183             if(feedback){
8184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8185                 
8186                 if(this.getValue().length){
8187                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8188                 }
8189                 
8190             }
8191             
8192         }
8193         
8194         this.fireEvent('invalid', this, msg);
8195     },
8196     // private
8197     SafariOnKeyDown : function(event)
8198     {
8199         // this is a workaround for a password hang bug on chrome/ webkit.
8200         
8201         var isSelectAll = false;
8202         
8203         if(this.inputEl().dom.selectionEnd > 0){
8204             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8205         }
8206         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8207             event.preventDefault();
8208             this.setValue('');
8209             return;
8210         }
8211         
8212         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8213             
8214             event.preventDefault();
8215             // this is very hacky as keydown always get's upper case.
8216             //
8217             var cc = String.fromCharCode(event.getCharCode());
8218             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8219             
8220         }
8221     },
8222     adjustWidth : function(tag, w){
8223         tag = tag.toLowerCase();
8224         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8225             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8226                 if(tag == 'input'){
8227                     return w + 2;
8228                 }
8229                 if(tag == 'textarea'){
8230                     return w-2;
8231                 }
8232             }else if(Roo.isOpera){
8233                 if(tag == 'input'){
8234                     return w + 2;
8235                 }
8236                 if(tag == 'textarea'){
8237                     return w-2;
8238                 }
8239             }
8240         }
8241         return w;
8242     }
8243     
8244 });
8245
8246  
8247 /*
8248  * - LGPL
8249  *
8250  * Input
8251  * 
8252  */
8253
8254 /**
8255  * @class Roo.bootstrap.TextArea
8256  * @extends Roo.bootstrap.Input
8257  * Bootstrap TextArea class
8258  * @cfg {Number} cols Specifies the visible width of a text area
8259  * @cfg {Number} rows Specifies the visible number of lines in a text area
8260  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8261  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8262  * @cfg {string} html text
8263  * 
8264  * @constructor
8265  * Create a new TextArea
8266  * @param {Object} config The config object
8267  */
8268
8269 Roo.bootstrap.TextArea = function(config){
8270     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8271    
8272 };
8273
8274 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8275      
8276     cols : false,
8277     rows : 5,
8278     readOnly : false,
8279     warp : 'soft',
8280     resize : false,
8281     value: false,
8282     html: false,
8283     
8284     getAutoCreate : function(){
8285         
8286         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8287         
8288         var id = Roo.id();
8289         
8290         var cfg = {};
8291         
8292         var input =  {
8293             tag: 'textarea',
8294             id : id,
8295             warp : this.warp,
8296             rows : this.rows,
8297             value : this.value || '',
8298             html: this.html || '',
8299             cls : 'form-control',
8300             placeholder : this.placeholder || '' 
8301             
8302         };
8303         
8304         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8305             input.maxLength = this.maxLength;
8306         }
8307         
8308         if(this.resize){
8309             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8310         }
8311         
8312         if(this.cols){
8313             input.cols = this.cols;
8314         }
8315         
8316         if (this.readOnly) {
8317             input.readonly = true;
8318         }
8319         
8320         if (this.name) {
8321             input.name = this.name;
8322         }
8323         
8324         if (this.size) {
8325             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8326         }
8327         
8328         var settings=this;
8329         ['xs','sm','md','lg'].map(function(size){
8330             if (settings[size]) {
8331                 cfg.cls += ' col-' + size + '-' + settings[size];
8332             }
8333         });
8334         
8335         var inputblock = input;
8336         
8337         if(this.hasFeedback && !this.allowBlank){
8338             
8339             var feedback = {
8340                 tag: 'span',
8341                 cls: 'glyphicon form-control-feedback'
8342             };
8343
8344             inputblock = {
8345                 cls : 'has-feedback',
8346                 cn :  [
8347                     input,
8348                     feedback
8349                 ] 
8350             };  
8351         }
8352         
8353         
8354         if (this.before || this.after) {
8355             
8356             inputblock = {
8357                 cls : 'input-group',
8358                 cn :  [] 
8359             };
8360             if (this.before) {
8361                 inputblock.cn.push({
8362                     tag :'span',
8363                     cls : 'input-group-addon',
8364                     html : this.before
8365                 });
8366             }
8367             
8368             inputblock.cn.push(input);
8369             
8370             if(this.hasFeedback && !this.allowBlank){
8371                 inputblock.cls += ' has-feedback';
8372                 inputblock.cn.push(feedback);
8373             }
8374             
8375             if (this.after) {
8376                 inputblock.cn.push({
8377                     tag :'span',
8378                     cls : 'input-group-addon',
8379                     html : this.after
8380                 });
8381             }
8382             
8383         }
8384         
8385         if (align ==='left' && this.fieldLabel.length) {
8386                 Roo.log("left and has label");
8387                 cfg.cn = [
8388                     
8389                     {
8390                         tag: 'label',
8391                         'for' :  id,
8392                         cls : 'control-label col-sm-' + this.labelWidth,
8393                         html : this.fieldLabel
8394                         
8395                     },
8396                     {
8397                         cls : "col-sm-" + (12 - this.labelWidth), 
8398                         cn: [
8399                             inputblock
8400                         ]
8401                     }
8402                     
8403                 ];
8404         } else if ( this.fieldLabel.length) {
8405                 Roo.log(" label");
8406                  cfg.cn = [
8407                    
8408                     {
8409                         tag: 'label',
8410                         //cls : 'input-group-addon',
8411                         html : this.fieldLabel
8412                         
8413                     },
8414                     
8415                     inputblock
8416                     
8417                 ];
8418
8419         } else {
8420             
8421                    Roo.log(" no label && no align");
8422                 cfg.cn = [
8423                     
8424                         inputblock
8425                     
8426                 ];
8427                 
8428                 
8429         }
8430         
8431         if (this.disabled) {
8432             input.disabled=true;
8433         }
8434         
8435         return cfg;
8436         
8437     },
8438     /**
8439      * return the real textarea element.
8440      */
8441     inputEl: function ()
8442     {
8443         return this.el.select('textarea.form-control',true).first();
8444     }
8445 });
8446
8447  
8448 /*
8449  * - LGPL
8450  *
8451  * trigger field - base class for combo..
8452  * 
8453  */
8454  
8455 /**
8456  * @class Roo.bootstrap.TriggerField
8457  * @extends Roo.bootstrap.Input
8458  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8459  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8460  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8461  * for which you can provide a custom implementation.  For example:
8462  * <pre><code>
8463 var trigger = new Roo.bootstrap.TriggerField();
8464 trigger.onTriggerClick = myTriggerFn;
8465 trigger.applyTo('my-field');
8466 </code></pre>
8467  *
8468  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8469  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8470  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8471  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8472  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8473
8474  * @constructor
8475  * Create a new TriggerField.
8476  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8477  * to the base TextField)
8478  */
8479 Roo.bootstrap.TriggerField = function(config){
8480     this.mimicing = false;
8481     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8482 };
8483
8484 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8485     /**
8486      * @cfg {String} triggerClass A CSS class to apply to the trigger
8487      */
8488      /**
8489      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8490      */
8491     hideTrigger:false,
8492
8493     /**
8494      * @cfg {Boolean} removable (true|false) special filter default false
8495      */
8496     removable : false,
8497     
8498     /** @cfg {Boolean} grow @hide */
8499     /** @cfg {Number} growMin @hide */
8500     /** @cfg {Number} growMax @hide */
8501
8502     /**
8503      * @hide 
8504      * @method
8505      */
8506     autoSize: Roo.emptyFn,
8507     // private
8508     monitorTab : true,
8509     // private
8510     deferHeight : true,
8511
8512     
8513     actionMode : 'wrap',
8514     
8515     caret : false,
8516     
8517     
8518     getAutoCreate : function(){
8519        
8520         var align = this.labelAlign || this.parentLabelAlign();
8521         
8522         var id = Roo.id();
8523         
8524         var cfg = {
8525             cls: 'form-group' //input-group
8526         };
8527         
8528         
8529         var input =  {
8530             tag: 'input',
8531             id : id,
8532             type : this.inputType,
8533             cls : 'form-control',
8534             autocomplete: 'new-password',
8535             placeholder : this.placeholder || '' 
8536             
8537         };
8538         if (this.name) {
8539             input.name = this.name;
8540         }
8541         if (this.size) {
8542             input.cls += ' input-' + this.size;
8543         }
8544         
8545         if (this.disabled) {
8546             input.disabled=true;
8547         }
8548         
8549         var inputblock = input;
8550         
8551         if(this.hasFeedback && !this.allowBlank){
8552             
8553             var feedback = {
8554                 tag: 'span',
8555                 cls: 'glyphicon form-control-feedback'
8556             };
8557             
8558             if(this.removable && !this.editable && !this.tickable){
8559                 inputblock = {
8560                     cls : 'has-feedback',
8561                     cn :  [
8562                         inputblock,
8563                         {
8564                             tag: 'button',
8565                             html : 'x',
8566                             cls : 'roo-combo-removable-btn close'
8567                         },
8568                         feedback
8569                     ] 
8570                 };
8571             } else {
8572                 inputblock = {
8573                     cls : 'has-feedback',
8574                     cn :  [
8575                         inputblock,
8576                         feedback
8577                     ] 
8578                 };
8579             }
8580               
8581         } else {
8582             if(this.removable && !this.editable && !this.tickable){
8583                 inputblock = {
8584                     cls : 'roo-removable',
8585                     cn :  [
8586                         inputblock,
8587                         {
8588                             tag: 'button',
8589                             html : 'x',
8590                             cls : 'roo-combo-removable-btn close'
8591                         }
8592                     ] 
8593                 };
8594             }
8595         }
8596         
8597         if (this.before || this.after) {
8598             
8599             inputblock = {
8600                 cls : 'input-group',
8601                 cn :  [] 
8602             };
8603             if (this.before) {
8604                 inputblock.cn.push({
8605                     tag :'span',
8606                     cls : 'input-group-addon',
8607                     html : this.before
8608                 });
8609             }
8610             
8611             inputblock.cn.push(input);
8612             
8613             if(this.hasFeedback && !this.allowBlank){
8614                 inputblock.cls += ' has-feedback';
8615                 inputblock.cn.push(feedback);
8616             }
8617             
8618             if (this.after) {
8619                 inputblock.cn.push({
8620                     tag :'span',
8621                     cls : 'input-group-addon',
8622                     html : this.after
8623                 });
8624             }
8625             
8626         };
8627         
8628         var box = {
8629             tag: 'div',
8630             cn: [
8631                 {
8632                     tag: 'input',
8633                     type : 'hidden',
8634                     cls: 'form-hidden-field'
8635                 },
8636                 inputblock
8637             ]
8638             
8639         };
8640         
8641         if(this.multiple){
8642             Roo.log('multiple');
8643             
8644             box = {
8645                 tag: 'div',
8646                 cn: [
8647                     {
8648                         tag: 'input',
8649                         type : 'hidden',
8650                         cls: 'form-hidden-field'
8651                     },
8652                     {
8653                         tag: 'ul',
8654                         cls: 'select2-choices',
8655                         cn:[
8656                             {
8657                                 tag: 'li',
8658                                 cls: 'select2-search-field',
8659                                 cn: [
8660
8661                                     inputblock
8662                                 ]
8663                             }
8664                         ]
8665                     }
8666                 ]
8667             }
8668         };
8669         
8670         var combobox = {
8671             cls: 'select2-container input-group',
8672             cn: [
8673                 box
8674 //                {
8675 //                    tag: 'ul',
8676 //                    cls: 'typeahead typeahead-long dropdown-menu',
8677 //                    style: 'display:none'
8678 //                }
8679             ]
8680         };
8681         
8682         if(!this.multiple && this.showToggleBtn){
8683             
8684             var caret = {
8685                         tag: 'span',
8686                         cls: 'caret'
8687              };
8688             if (this.caret != false) {
8689                 caret = {
8690                      tag: 'i',
8691                      cls: 'fa fa-' + this.caret
8692                 };
8693                 
8694             }
8695             
8696             combobox.cn.push({
8697                 tag :'span',
8698                 cls : 'input-group-addon btn dropdown-toggle',
8699                 cn : [
8700                     caret,
8701                     {
8702                         tag: 'span',
8703                         cls: 'combobox-clear',
8704                         cn  : [
8705                             {
8706                                 tag : 'i',
8707                                 cls: 'icon-remove'
8708                             }
8709                         ]
8710                     }
8711                 ]
8712
8713             })
8714         }
8715         
8716         if(this.multiple){
8717             combobox.cls += ' select2-container-multi';
8718         }
8719         
8720         if (align ==='left' && this.fieldLabel.length) {
8721             
8722                 Roo.log("left and has label");
8723                 cfg.cn = [
8724                     
8725                     {
8726                         tag: 'label',
8727                         'for' :  id,
8728                         cls : 'control-label col-sm-' + this.labelWidth,
8729                         html : this.fieldLabel
8730                         
8731                     },
8732                     {
8733                         cls : "col-sm-" + (12 - this.labelWidth), 
8734                         cn: [
8735                             combobox
8736                         ]
8737                     }
8738                     
8739                 ];
8740         } else if ( this.fieldLabel.length) {
8741                 Roo.log(" label");
8742                  cfg.cn = [
8743                    
8744                     {
8745                         tag: 'label',
8746                         //cls : 'input-group-addon',
8747                         html : this.fieldLabel
8748                         
8749                     },
8750                     
8751                     combobox
8752                     
8753                 ];
8754
8755         } else {
8756             
8757                 Roo.log(" no label && no align");
8758                 cfg = combobox
8759                      
8760                 
8761         }
8762          
8763         var settings=this;
8764         ['xs','sm','md','lg'].map(function(size){
8765             if (settings[size]) {
8766                 cfg.cls += ' col-' + size + '-' + settings[size];
8767             }
8768         });
8769         
8770         return cfg;
8771         
8772     },
8773     
8774     
8775     
8776     // private
8777     onResize : function(w, h){
8778 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8779 //        if(typeof w == 'number'){
8780 //            var x = w - this.trigger.getWidth();
8781 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8782 //            this.trigger.setStyle('left', x+'px');
8783 //        }
8784     },
8785
8786     // private
8787     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8788
8789     // private
8790     getResizeEl : function(){
8791         return this.inputEl();
8792     },
8793
8794     // private
8795     getPositionEl : function(){
8796         return this.inputEl();
8797     },
8798
8799     // private
8800     alignErrorIcon : function(){
8801         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8802     },
8803
8804     // private
8805     initEvents : function(){
8806         
8807         this.createList();
8808         
8809         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8810         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8811         if(!this.multiple && this.showToggleBtn){
8812             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8813             if(this.hideTrigger){
8814                 this.trigger.setDisplayed(false);
8815             }
8816             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8817         }
8818         
8819         if(this.multiple){
8820             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8821         }
8822         
8823         if(this.removable && !this.editable && !this.tickable){
8824             var close = this.closeTriggerEl();
8825             
8826             if(close){
8827                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8828                 close.on('click', this.removeBtnClick, this, close);
8829             }
8830         }
8831         
8832         //this.trigger.addClassOnOver('x-form-trigger-over');
8833         //this.trigger.addClassOnClick('x-form-trigger-click');
8834         
8835         //if(!this.width){
8836         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8837         //}
8838     },
8839     
8840     closeTriggerEl : function()
8841     {
8842         var close = this.el.select('.roo-combo-removable-btn', true).first();
8843         return close ? close : false;
8844     },
8845     
8846     removeBtnClick : function(e, h, el)
8847     {
8848         e.preventDefault();
8849         
8850         this.fireEvent("remove", this);
8851     },
8852     
8853     createList : function()
8854     {
8855         this.list = Roo.get(document.body).createChild({
8856             tag: 'ul',
8857             cls: 'typeahead typeahead-long dropdown-menu',
8858             style: 'display:none'
8859         });
8860         
8861         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8862         
8863     },
8864
8865     // private
8866     initTrigger : function(){
8867        
8868     },
8869
8870     // private
8871     onDestroy : function(){
8872         if(this.trigger){
8873             this.trigger.removeAllListeners();
8874           //  this.trigger.remove();
8875         }
8876         //if(this.wrap){
8877         //    this.wrap.remove();
8878         //}
8879         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8880     },
8881
8882     // private
8883     onFocus : function(){
8884         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8885         /*
8886         if(!this.mimicing){
8887             this.wrap.addClass('x-trigger-wrap-focus');
8888             this.mimicing = true;
8889             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8890             if(this.monitorTab){
8891                 this.el.on("keydown", this.checkTab, this);
8892             }
8893         }
8894         */
8895     },
8896
8897     // private
8898     checkTab : function(e){
8899         if(e.getKey() == e.TAB){
8900             this.triggerBlur();
8901         }
8902     },
8903
8904     // private
8905     onBlur : function(){
8906         // do nothing
8907     },
8908
8909     // private
8910     mimicBlur : function(e, t){
8911         /*
8912         if(!this.wrap.contains(t) && this.validateBlur()){
8913             this.triggerBlur();
8914         }
8915         */
8916     },
8917
8918     // private
8919     triggerBlur : function(){
8920         this.mimicing = false;
8921         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8922         if(this.monitorTab){
8923             this.el.un("keydown", this.checkTab, this);
8924         }
8925         //this.wrap.removeClass('x-trigger-wrap-focus');
8926         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8927     },
8928
8929     // private
8930     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8931     validateBlur : function(e, t){
8932         return true;
8933     },
8934
8935     // private
8936     onDisable : function(){
8937         this.inputEl().dom.disabled = true;
8938         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8939         //if(this.wrap){
8940         //    this.wrap.addClass('x-item-disabled');
8941         //}
8942     },
8943
8944     // private
8945     onEnable : function(){
8946         this.inputEl().dom.disabled = false;
8947         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8948         //if(this.wrap){
8949         //    this.el.removeClass('x-item-disabled');
8950         //}
8951     },
8952
8953     // private
8954     onShow : function(){
8955         var ae = this.getActionEl();
8956         
8957         if(ae){
8958             ae.dom.style.display = '';
8959             ae.dom.style.visibility = 'visible';
8960         }
8961     },
8962
8963     // private
8964     
8965     onHide : function(){
8966         var ae = this.getActionEl();
8967         ae.dom.style.display = 'none';
8968     },
8969
8970     /**
8971      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8972      * by an implementing function.
8973      * @method
8974      * @param {EventObject} e
8975      */
8976     onTriggerClick : Roo.emptyFn
8977 });
8978  /*
8979  * Based on:
8980  * Ext JS Library 1.1.1
8981  * Copyright(c) 2006-2007, Ext JS, LLC.
8982  *
8983  * Originally Released Under LGPL - original licence link has changed is not relivant.
8984  *
8985  * Fork - LGPL
8986  * <script type="text/javascript">
8987  */
8988
8989
8990 /**
8991  * @class Roo.data.SortTypes
8992  * @singleton
8993  * Defines the default sorting (casting?) comparison functions used when sorting data.
8994  */
8995 Roo.data.SortTypes = {
8996     /**
8997      * Default sort that does nothing
8998      * @param {Mixed} s The value being converted
8999      * @return {Mixed} The comparison value
9000      */
9001     none : function(s){
9002         return s;
9003     },
9004     
9005     /**
9006      * The regular expression used to strip tags
9007      * @type {RegExp}
9008      * @property
9009      */
9010     stripTagsRE : /<\/?[^>]+>/gi,
9011     
9012     /**
9013      * Strips all HTML tags to sort on text only
9014      * @param {Mixed} s The value being converted
9015      * @return {String} The comparison value
9016      */
9017     asText : function(s){
9018         return String(s).replace(this.stripTagsRE, "");
9019     },
9020     
9021     /**
9022      * Strips all HTML tags to sort on text only - Case insensitive
9023      * @param {Mixed} s The value being converted
9024      * @return {String} The comparison value
9025      */
9026     asUCText : function(s){
9027         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9028     },
9029     
9030     /**
9031      * Case insensitive string
9032      * @param {Mixed} s The value being converted
9033      * @return {String} The comparison value
9034      */
9035     asUCString : function(s) {
9036         return String(s).toUpperCase();
9037     },
9038     
9039     /**
9040      * Date sorting
9041      * @param {Mixed} s The value being converted
9042      * @return {Number} The comparison value
9043      */
9044     asDate : function(s) {
9045         if(!s){
9046             return 0;
9047         }
9048         if(s instanceof Date){
9049             return s.getTime();
9050         }
9051         return Date.parse(String(s));
9052     },
9053     
9054     /**
9055      * Float sorting
9056      * @param {Mixed} s The value being converted
9057      * @return {Float} The comparison value
9058      */
9059     asFloat : function(s) {
9060         var val = parseFloat(String(s).replace(/,/g, ""));
9061         if(isNaN(val)) val = 0;
9062         return val;
9063     },
9064     
9065     /**
9066      * Integer sorting
9067      * @param {Mixed} s The value being converted
9068      * @return {Number} The comparison value
9069      */
9070     asInt : function(s) {
9071         var val = parseInt(String(s).replace(/,/g, ""));
9072         if(isNaN(val)) val = 0;
9073         return val;
9074     }
9075 };/*
9076  * Based on:
9077  * Ext JS Library 1.1.1
9078  * Copyright(c) 2006-2007, Ext JS, LLC.
9079  *
9080  * Originally Released Under LGPL - original licence link has changed is not relivant.
9081  *
9082  * Fork - LGPL
9083  * <script type="text/javascript">
9084  */
9085
9086 /**
9087 * @class Roo.data.Record
9088  * Instances of this class encapsulate both record <em>definition</em> information, and record
9089  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9090  * to access Records cached in an {@link Roo.data.Store} object.<br>
9091  * <p>
9092  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9093  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9094  * objects.<br>
9095  * <p>
9096  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9097  * @constructor
9098  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9099  * {@link #create}. The parameters are the same.
9100  * @param {Array} data An associative Array of data values keyed by the field name.
9101  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9102  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9103  * not specified an integer id is generated.
9104  */
9105 Roo.data.Record = function(data, id){
9106     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9107     this.data = data;
9108 };
9109
9110 /**
9111  * Generate a constructor for a specific record layout.
9112  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9113  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9114  * Each field definition object may contain the following properties: <ul>
9115  * <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,
9116  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9117  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9118  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9119  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9120  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9121  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9122  * this may be omitted.</p></li>
9123  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9124  * <ul><li>auto (Default, implies no conversion)</li>
9125  * <li>string</li>
9126  * <li>int</li>
9127  * <li>float</li>
9128  * <li>boolean</li>
9129  * <li>date</li></ul></p></li>
9130  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9131  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9132  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9133  * by the Reader into an object that will be stored in the Record. It is passed the
9134  * following parameters:<ul>
9135  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9136  * </ul></p></li>
9137  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9138  * </ul>
9139  * <br>usage:<br><pre><code>
9140 var TopicRecord = Roo.data.Record.create(
9141     {name: 'title', mapping: 'topic_title'},
9142     {name: 'author', mapping: 'username'},
9143     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9144     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9145     {name: 'lastPoster', mapping: 'user2'},
9146     {name: 'excerpt', mapping: 'post_text'}
9147 );
9148
9149 var myNewRecord = new TopicRecord({
9150     title: 'Do my job please',
9151     author: 'noobie',
9152     totalPosts: 1,
9153     lastPost: new Date(),
9154     lastPoster: 'Animal',
9155     excerpt: 'No way dude!'
9156 });
9157 myStore.add(myNewRecord);
9158 </code></pre>
9159  * @method create
9160  * @static
9161  */
9162 Roo.data.Record.create = function(o){
9163     var f = function(){
9164         f.superclass.constructor.apply(this, arguments);
9165     };
9166     Roo.extend(f, Roo.data.Record);
9167     var p = f.prototype;
9168     p.fields = new Roo.util.MixedCollection(false, function(field){
9169         return field.name;
9170     });
9171     for(var i = 0, len = o.length; i < len; i++){
9172         p.fields.add(new Roo.data.Field(o[i]));
9173     }
9174     f.getField = function(name){
9175         return p.fields.get(name);  
9176     };
9177     return f;
9178 };
9179
9180 Roo.data.Record.AUTO_ID = 1000;
9181 Roo.data.Record.EDIT = 'edit';
9182 Roo.data.Record.REJECT = 'reject';
9183 Roo.data.Record.COMMIT = 'commit';
9184
9185 Roo.data.Record.prototype = {
9186     /**
9187      * Readonly flag - true if this record has been modified.
9188      * @type Boolean
9189      */
9190     dirty : false,
9191     editing : false,
9192     error: null,
9193     modified: null,
9194
9195     // private
9196     join : function(store){
9197         this.store = store;
9198     },
9199
9200     /**
9201      * Set the named field to the specified value.
9202      * @param {String} name The name of the field to set.
9203      * @param {Object} value The value to set the field to.
9204      */
9205     set : function(name, value){
9206         if(this.data[name] == value){
9207             return;
9208         }
9209         this.dirty = true;
9210         if(!this.modified){
9211             this.modified = {};
9212         }
9213         if(typeof this.modified[name] == 'undefined'){
9214             this.modified[name] = this.data[name];
9215         }
9216         this.data[name] = value;
9217         if(!this.editing && this.store){
9218             this.store.afterEdit(this);
9219         }       
9220     },
9221
9222     /**
9223      * Get the value of the named field.
9224      * @param {String} name The name of the field to get the value of.
9225      * @return {Object} The value of the field.
9226      */
9227     get : function(name){
9228         return this.data[name]; 
9229     },
9230
9231     // private
9232     beginEdit : function(){
9233         this.editing = true;
9234         this.modified = {}; 
9235     },
9236
9237     // private
9238     cancelEdit : function(){
9239         this.editing = false;
9240         delete this.modified;
9241     },
9242
9243     // private
9244     endEdit : function(){
9245         this.editing = false;
9246         if(this.dirty && this.store){
9247             this.store.afterEdit(this);
9248         }
9249     },
9250
9251     /**
9252      * Usually called by the {@link Roo.data.Store} which owns the Record.
9253      * Rejects all changes made to the Record since either creation, or the last commit operation.
9254      * Modified fields are reverted to their original values.
9255      * <p>
9256      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9257      * of reject operations.
9258      */
9259     reject : function(){
9260         var m = this.modified;
9261         for(var n in m){
9262             if(typeof m[n] != "function"){
9263                 this.data[n] = m[n];
9264             }
9265         }
9266         this.dirty = false;
9267         delete this.modified;
9268         this.editing = false;
9269         if(this.store){
9270             this.store.afterReject(this);
9271         }
9272     },
9273
9274     /**
9275      * Usually called by the {@link Roo.data.Store} which owns the Record.
9276      * Commits all changes made to the Record since either creation, or the last commit operation.
9277      * <p>
9278      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9279      * of commit operations.
9280      */
9281     commit : function(){
9282         this.dirty = false;
9283         delete this.modified;
9284         this.editing = false;
9285         if(this.store){
9286             this.store.afterCommit(this);
9287         }
9288     },
9289
9290     // private
9291     hasError : function(){
9292         return this.error != null;
9293     },
9294
9295     // private
9296     clearError : function(){
9297         this.error = null;
9298     },
9299
9300     /**
9301      * Creates a copy of this record.
9302      * @param {String} id (optional) A new record id if you don't want to use this record's id
9303      * @return {Record}
9304      */
9305     copy : function(newId) {
9306         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9307     }
9308 };/*
9309  * Based on:
9310  * Ext JS Library 1.1.1
9311  * Copyright(c) 2006-2007, Ext JS, LLC.
9312  *
9313  * Originally Released Under LGPL - original licence link has changed is not relivant.
9314  *
9315  * Fork - LGPL
9316  * <script type="text/javascript">
9317  */
9318
9319
9320
9321 /**
9322  * @class Roo.data.Store
9323  * @extends Roo.util.Observable
9324  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9325  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9326  * <p>
9327  * 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
9328  * has no knowledge of the format of the data returned by the Proxy.<br>
9329  * <p>
9330  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9331  * instances from the data object. These records are cached and made available through accessor functions.
9332  * @constructor
9333  * Creates a new Store.
9334  * @param {Object} config A config object containing the objects needed for the Store to access data,
9335  * and read the data into Records.
9336  */
9337 Roo.data.Store = function(config){
9338     this.data = new Roo.util.MixedCollection(false);
9339     this.data.getKey = function(o){
9340         return o.id;
9341     };
9342     this.baseParams = {};
9343     // private
9344     this.paramNames = {
9345         "start" : "start",
9346         "limit" : "limit",
9347         "sort" : "sort",
9348         "dir" : "dir",
9349         "multisort" : "_multisort"
9350     };
9351
9352     if(config && config.data){
9353         this.inlineData = config.data;
9354         delete config.data;
9355     }
9356
9357     Roo.apply(this, config);
9358     
9359     if(this.reader){ // reader passed
9360         this.reader = Roo.factory(this.reader, Roo.data);
9361         this.reader.xmodule = this.xmodule || false;
9362         if(!this.recordType){
9363             this.recordType = this.reader.recordType;
9364         }
9365         if(this.reader.onMetaChange){
9366             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9367         }
9368     }
9369
9370     if(this.recordType){
9371         this.fields = this.recordType.prototype.fields;
9372     }
9373     this.modified = [];
9374
9375     this.addEvents({
9376         /**
9377          * @event datachanged
9378          * Fires when the data cache has changed, and a widget which is using this Store
9379          * as a Record cache should refresh its view.
9380          * @param {Store} this
9381          */
9382         datachanged : true,
9383         /**
9384          * @event metachange
9385          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9386          * @param {Store} this
9387          * @param {Object} meta The JSON metadata
9388          */
9389         metachange : true,
9390         /**
9391          * @event add
9392          * Fires when Records have been added to the Store
9393          * @param {Store} this
9394          * @param {Roo.data.Record[]} records The array of Records added
9395          * @param {Number} index The index at which the record(s) were added
9396          */
9397         add : true,
9398         /**
9399          * @event remove
9400          * Fires when a Record has been removed from the Store
9401          * @param {Store} this
9402          * @param {Roo.data.Record} record The Record that was removed
9403          * @param {Number} index The index at which the record was removed
9404          */
9405         remove : true,
9406         /**
9407          * @event update
9408          * Fires when a Record has been updated
9409          * @param {Store} this
9410          * @param {Roo.data.Record} record The Record that was updated
9411          * @param {String} operation The update operation being performed.  Value may be one of:
9412          * <pre><code>
9413  Roo.data.Record.EDIT
9414  Roo.data.Record.REJECT
9415  Roo.data.Record.COMMIT
9416          * </code></pre>
9417          */
9418         update : true,
9419         /**
9420          * @event clear
9421          * Fires when the data cache has been cleared.
9422          * @param {Store} this
9423          */
9424         clear : true,
9425         /**
9426          * @event beforeload
9427          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9428          * the load action will be canceled.
9429          * @param {Store} this
9430          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9431          */
9432         beforeload : true,
9433         /**
9434          * @event beforeloadadd
9435          * Fires after a new set of Records has been loaded.
9436          * @param {Store} this
9437          * @param {Roo.data.Record[]} records The Records that were loaded
9438          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9439          */
9440         beforeloadadd : true,
9441         /**
9442          * @event load
9443          * Fires after a new set of Records has been loaded, before they are added to the store.
9444          * @param {Store} this
9445          * @param {Roo.data.Record[]} records The Records that were loaded
9446          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9447          * @params {Object} return from reader
9448          */
9449         load : true,
9450         /**
9451          * @event loadexception
9452          * Fires if an exception occurs in the Proxy during loading.
9453          * Called with the signature of the Proxy's "loadexception" event.
9454          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9455          * 
9456          * @param {Proxy} 
9457          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9458          * @param {Object} load options 
9459          * @param {Object} jsonData from your request (normally this contains the Exception)
9460          */
9461         loadexception : true
9462     });
9463     
9464     if(this.proxy){
9465         this.proxy = Roo.factory(this.proxy, Roo.data);
9466         this.proxy.xmodule = this.xmodule || false;
9467         this.relayEvents(this.proxy,  ["loadexception"]);
9468     }
9469     this.sortToggle = {};
9470     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9471
9472     Roo.data.Store.superclass.constructor.call(this);
9473
9474     if(this.inlineData){
9475         this.loadData(this.inlineData);
9476         delete this.inlineData;
9477     }
9478 };
9479
9480 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9481      /**
9482     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9483     * without a remote query - used by combo/forms at present.
9484     */
9485     
9486     /**
9487     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9488     */
9489     /**
9490     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9491     */
9492     /**
9493     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9494     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9495     */
9496     /**
9497     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9498     * on any HTTP request
9499     */
9500     /**
9501     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9502     */
9503     /**
9504     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9505     */
9506     multiSort: false,
9507     /**
9508     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9509     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9510     */
9511     remoteSort : false,
9512
9513     /**
9514     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9515      * loaded or when a record is removed. (defaults to false).
9516     */
9517     pruneModifiedRecords : false,
9518
9519     // private
9520     lastOptions : null,
9521
9522     /**
9523      * Add Records to the Store and fires the add event.
9524      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9525      */
9526     add : function(records){
9527         records = [].concat(records);
9528         for(var i = 0, len = records.length; i < len; i++){
9529             records[i].join(this);
9530         }
9531         var index = this.data.length;
9532         this.data.addAll(records);
9533         this.fireEvent("add", this, records, index);
9534     },
9535
9536     /**
9537      * Remove a Record from the Store and fires the remove event.
9538      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9539      */
9540     remove : function(record){
9541         var index = this.data.indexOf(record);
9542         this.data.removeAt(index);
9543         if(this.pruneModifiedRecords){
9544             this.modified.remove(record);
9545         }
9546         this.fireEvent("remove", this, record, index);
9547     },
9548
9549     /**
9550      * Remove all Records from the Store and fires the clear event.
9551      */
9552     removeAll : function(){
9553         this.data.clear();
9554         if(this.pruneModifiedRecords){
9555             this.modified = [];
9556         }
9557         this.fireEvent("clear", this);
9558     },
9559
9560     /**
9561      * Inserts Records to the Store at the given index and fires the add event.
9562      * @param {Number} index The start index at which to insert the passed Records.
9563      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9564      */
9565     insert : function(index, records){
9566         records = [].concat(records);
9567         for(var i = 0, len = records.length; i < len; i++){
9568             this.data.insert(index, records[i]);
9569             records[i].join(this);
9570         }
9571         this.fireEvent("add", this, records, index);
9572     },
9573
9574     /**
9575      * Get the index within the cache of the passed Record.
9576      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9577      * @return {Number} The index of the passed Record. Returns -1 if not found.
9578      */
9579     indexOf : function(record){
9580         return this.data.indexOf(record);
9581     },
9582
9583     /**
9584      * Get the index within the cache of the Record with the passed id.
9585      * @param {String} id The id of the Record to find.
9586      * @return {Number} The index of the Record. Returns -1 if not found.
9587      */
9588     indexOfId : function(id){
9589         return this.data.indexOfKey(id);
9590     },
9591
9592     /**
9593      * Get the Record with the specified id.
9594      * @param {String} id The id of the Record to find.
9595      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9596      */
9597     getById : function(id){
9598         return this.data.key(id);
9599     },
9600
9601     /**
9602      * Get the Record at the specified index.
9603      * @param {Number} index The index of the Record to find.
9604      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9605      */
9606     getAt : function(index){
9607         return this.data.itemAt(index);
9608     },
9609
9610     /**
9611      * Returns a range of Records between specified indices.
9612      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9613      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9614      * @return {Roo.data.Record[]} An array of Records
9615      */
9616     getRange : function(start, end){
9617         return this.data.getRange(start, end);
9618     },
9619
9620     // private
9621     storeOptions : function(o){
9622         o = Roo.apply({}, o);
9623         delete o.callback;
9624         delete o.scope;
9625         this.lastOptions = o;
9626     },
9627
9628     /**
9629      * Loads the Record cache from the configured Proxy using the configured Reader.
9630      * <p>
9631      * If using remote paging, then the first load call must specify the <em>start</em>
9632      * and <em>limit</em> properties in the options.params property to establish the initial
9633      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9634      * <p>
9635      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9636      * and this call will return before the new data has been loaded. Perform any post-processing
9637      * in a callback function, or in a "load" event handler.</strong>
9638      * <p>
9639      * @param {Object} options An object containing properties which control loading options:<ul>
9640      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9641      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9642      * passed the following arguments:<ul>
9643      * <li>r : Roo.data.Record[]</li>
9644      * <li>options: Options object from the load call</li>
9645      * <li>success: Boolean success indicator</li></ul></li>
9646      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9647      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9648      * </ul>
9649      */
9650     load : function(options){
9651         options = options || {};
9652         if(this.fireEvent("beforeload", this, options) !== false){
9653             this.storeOptions(options);
9654             var p = Roo.apply(options.params || {}, this.baseParams);
9655             // if meta was not loaded from remote source.. try requesting it.
9656             if (!this.reader.metaFromRemote) {
9657                 p._requestMeta = 1;
9658             }
9659             if(this.sortInfo && this.remoteSort){
9660                 var pn = this.paramNames;
9661                 p[pn["sort"]] = this.sortInfo.field;
9662                 p[pn["dir"]] = this.sortInfo.direction;
9663             }
9664             if (this.multiSort) {
9665                 var pn = this.paramNames;
9666                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9667             }
9668             
9669             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9670         }
9671     },
9672
9673     /**
9674      * Reloads the Record cache from the configured Proxy using the configured Reader and
9675      * the options from the last load operation performed.
9676      * @param {Object} options (optional) An object containing properties which may override the options
9677      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9678      * the most recently used options are reused).
9679      */
9680     reload : function(options){
9681         this.load(Roo.applyIf(options||{}, this.lastOptions));
9682     },
9683
9684     // private
9685     // Called as a callback by the Reader during a load operation.
9686     loadRecords : function(o, options, success){
9687         if(!o || success === false){
9688             if(success !== false){
9689                 this.fireEvent("load", this, [], options, o);
9690             }
9691             if(options.callback){
9692                 options.callback.call(options.scope || this, [], options, false);
9693             }
9694             return;
9695         }
9696         // if data returned failure - throw an exception.
9697         if (o.success === false) {
9698             // show a message if no listener is registered.
9699             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9700                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9701             }
9702             // loadmask wil be hooked into this..
9703             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9704             return;
9705         }
9706         var r = o.records, t = o.totalRecords || r.length;
9707         
9708         this.fireEvent("beforeloadadd", this, r, options, o);
9709         
9710         if(!options || options.add !== true){
9711             if(this.pruneModifiedRecords){
9712                 this.modified = [];
9713             }
9714             for(var i = 0, len = r.length; i < len; i++){
9715                 r[i].join(this);
9716             }
9717             if(this.snapshot){
9718                 this.data = this.snapshot;
9719                 delete this.snapshot;
9720             }
9721             this.data.clear();
9722             this.data.addAll(r);
9723             this.totalLength = t;
9724             this.applySort();
9725             this.fireEvent("datachanged", this);
9726         }else{
9727             this.totalLength = Math.max(t, this.data.length+r.length);
9728             this.add(r);
9729         }
9730         this.fireEvent("load", this, r, options, o);
9731         if(options.callback){
9732             options.callback.call(options.scope || this, r, options, true);
9733         }
9734     },
9735
9736
9737     /**
9738      * Loads data from a passed data block. A Reader which understands the format of the data
9739      * must have been configured in the constructor.
9740      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9741      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9742      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9743      */
9744     loadData : function(o, append){
9745         var r = this.reader.readRecords(o);
9746         this.loadRecords(r, {add: append}, true);
9747     },
9748
9749     /**
9750      * Gets the number of cached records.
9751      * <p>
9752      * <em>If using paging, this may not be the total size of the dataset. If the data object
9753      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9754      * the data set size</em>
9755      */
9756     getCount : function(){
9757         return this.data.length || 0;
9758     },
9759
9760     /**
9761      * Gets the total number of records in the dataset as returned by the server.
9762      * <p>
9763      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9764      * the dataset size</em>
9765      */
9766     getTotalCount : function(){
9767         return this.totalLength || 0;
9768     },
9769
9770     /**
9771      * Returns the sort state of the Store as an object with two properties:
9772      * <pre><code>
9773  field {String} The name of the field by which the Records are sorted
9774  direction {String} The sort order, "ASC" or "DESC"
9775      * </code></pre>
9776      */
9777     getSortState : function(){
9778         return this.sortInfo;
9779     },
9780
9781     // private
9782     applySort : function(){
9783         if(this.sortInfo && !this.remoteSort){
9784             var s = this.sortInfo, f = s.field;
9785             var st = this.fields.get(f).sortType;
9786             var fn = function(r1, r2){
9787                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9788                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9789             };
9790             this.data.sort(s.direction, fn);
9791             if(this.snapshot && this.snapshot != this.data){
9792                 this.snapshot.sort(s.direction, fn);
9793             }
9794         }
9795     },
9796
9797     /**
9798      * Sets the default sort column and order to be used by the next load operation.
9799      * @param {String} fieldName The name of the field to sort by.
9800      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9801      */
9802     setDefaultSort : function(field, dir){
9803         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9804     },
9805
9806     /**
9807      * Sort the Records.
9808      * If remote sorting is used, the sort is performed on the server, and the cache is
9809      * reloaded. If local sorting is used, the cache is sorted internally.
9810      * @param {String} fieldName The name of the field to sort by.
9811      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9812      */
9813     sort : function(fieldName, dir){
9814         var f = this.fields.get(fieldName);
9815         if(!dir){
9816             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9817             
9818             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9819                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9820             }else{
9821                 dir = f.sortDir;
9822             }
9823         }
9824         this.sortToggle[f.name] = dir;
9825         this.sortInfo = {field: f.name, direction: dir};
9826         if(!this.remoteSort){
9827             this.applySort();
9828             this.fireEvent("datachanged", this);
9829         }else{
9830             this.load(this.lastOptions);
9831         }
9832     },
9833
9834     /**
9835      * Calls the specified function for each of the Records in the cache.
9836      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9837      * Returning <em>false</em> aborts and exits the iteration.
9838      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9839      */
9840     each : function(fn, scope){
9841         this.data.each(fn, scope);
9842     },
9843
9844     /**
9845      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9846      * (e.g., during paging).
9847      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9848      */
9849     getModifiedRecords : function(){
9850         return this.modified;
9851     },
9852
9853     // private
9854     createFilterFn : function(property, value, anyMatch){
9855         if(!value.exec){ // not a regex
9856             value = String(value);
9857             if(value.length == 0){
9858                 return false;
9859             }
9860             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9861         }
9862         return function(r){
9863             return value.test(r.data[property]);
9864         };
9865     },
9866
9867     /**
9868      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9869      * @param {String} property A field on your records
9870      * @param {Number} start The record index to start at (defaults to 0)
9871      * @param {Number} end The last record index to include (defaults to length - 1)
9872      * @return {Number} The sum
9873      */
9874     sum : function(property, start, end){
9875         var rs = this.data.items, v = 0;
9876         start = start || 0;
9877         end = (end || end === 0) ? end : rs.length-1;
9878
9879         for(var i = start; i <= end; i++){
9880             v += (rs[i].data[property] || 0);
9881         }
9882         return v;
9883     },
9884
9885     /**
9886      * Filter the records by a specified property.
9887      * @param {String} field A field on your records
9888      * @param {String/RegExp} value Either a string that the field
9889      * should start with or a RegExp to test against the field
9890      * @param {Boolean} anyMatch True to match any part not just the beginning
9891      */
9892     filter : function(property, value, anyMatch){
9893         var fn = this.createFilterFn(property, value, anyMatch);
9894         return fn ? this.filterBy(fn) : this.clearFilter();
9895     },
9896
9897     /**
9898      * Filter by a function. The specified function will be called with each
9899      * record in this data source. If the function returns true the record is included,
9900      * otherwise it is filtered.
9901      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9902      * @param {Object} scope (optional) The scope of the function (defaults to this)
9903      */
9904     filterBy : function(fn, scope){
9905         this.snapshot = this.snapshot || this.data;
9906         this.data = this.queryBy(fn, scope||this);
9907         this.fireEvent("datachanged", this);
9908     },
9909
9910     /**
9911      * Query the records by a specified property.
9912      * @param {String} field A field on your records
9913      * @param {String/RegExp} value Either a string that the field
9914      * should start with or a RegExp to test against the field
9915      * @param {Boolean} anyMatch True to match any part not just the beginning
9916      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9917      */
9918     query : function(property, value, anyMatch){
9919         var fn = this.createFilterFn(property, value, anyMatch);
9920         return fn ? this.queryBy(fn) : this.data.clone();
9921     },
9922
9923     /**
9924      * Query by a function. The specified function will be called with each
9925      * record in this data source. If the function returns true the record is included
9926      * in the results.
9927      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9928      * @param {Object} scope (optional) The scope of the function (defaults to this)
9929       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9930      **/
9931     queryBy : function(fn, scope){
9932         var data = this.snapshot || this.data;
9933         return data.filterBy(fn, scope||this);
9934     },
9935
9936     /**
9937      * Collects unique values for a particular dataIndex from this store.
9938      * @param {String} dataIndex The property to collect
9939      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9940      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9941      * @return {Array} An array of the unique values
9942      **/
9943     collect : function(dataIndex, allowNull, bypassFilter){
9944         var d = (bypassFilter === true && this.snapshot) ?
9945                 this.snapshot.items : this.data.items;
9946         var v, sv, r = [], l = {};
9947         for(var i = 0, len = d.length; i < len; i++){
9948             v = d[i].data[dataIndex];
9949             sv = String(v);
9950             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9951                 l[sv] = true;
9952                 r[r.length] = v;
9953             }
9954         }
9955         return r;
9956     },
9957
9958     /**
9959      * Revert to a view of the Record cache with no filtering applied.
9960      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9961      */
9962     clearFilter : function(suppressEvent){
9963         if(this.snapshot && this.snapshot != this.data){
9964             this.data = this.snapshot;
9965             delete this.snapshot;
9966             if(suppressEvent !== true){
9967                 this.fireEvent("datachanged", this);
9968             }
9969         }
9970     },
9971
9972     // private
9973     afterEdit : function(record){
9974         if(this.modified.indexOf(record) == -1){
9975             this.modified.push(record);
9976         }
9977         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9978     },
9979     
9980     // private
9981     afterReject : function(record){
9982         this.modified.remove(record);
9983         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9984     },
9985
9986     // private
9987     afterCommit : function(record){
9988         this.modified.remove(record);
9989         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9990     },
9991
9992     /**
9993      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9994      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9995      */
9996     commitChanges : function(){
9997         var m = this.modified.slice(0);
9998         this.modified = [];
9999         for(var i = 0, len = m.length; i < len; i++){
10000             m[i].commit();
10001         }
10002     },
10003
10004     /**
10005      * Cancel outstanding changes on all changed records.
10006      */
10007     rejectChanges : function(){
10008         var m = this.modified.slice(0);
10009         this.modified = [];
10010         for(var i = 0, len = m.length; i < len; i++){
10011             m[i].reject();
10012         }
10013     },
10014
10015     onMetaChange : function(meta, rtype, o){
10016         this.recordType = rtype;
10017         this.fields = rtype.prototype.fields;
10018         delete this.snapshot;
10019         this.sortInfo = meta.sortInfo || this.sortInfo;
10020         this.modified = [];
10021         this.fireEvent('metachange', this, this.reader.meta);
10022     },
10023     
10024     moveIndex : function(data, type)
10025     {
10026         var index = this.indexOf(data);
10027         
10028         var newIndex = index + type;
10029         
10030         this.remove(data);
10031         
10032         this.insert(newIndex, data);
10033         
10034     }
10035 });/*
10036  * Based on:
10037  * Ext JS Library 1.1.1
10038  * Copyright(c) 2006-2007, Ext JS, LLC.
10039  *
10040  * Originally Released Under LGPL - original licence link has changed is not relivant.
10041  *
10042  * Fork - LGPL
10043  * <script type="text/javascript">
10044  */
10045
10046 /**
10047  * @class Roo.data.SimpleStore
10048  * @extends Roo.data.Store
10049  * Small helper class to make creating Stores from Array data easier.
10050  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10051  * @cfg {Array} fields An array of field definition objects, or field name strings.
10052  * @cfg {Array} data The multi-dimensional array of data
10053  * @constructor
10054  * @param {Object} config
10055  */
10056 Roo.data.SimpleStore = function(config){
10057     Roo.data.SimpleStore.superclass.constructor.call(this, {
10058         isLocal : true,
10059         reader: new Roo.data.ArrayReader({
10060                 id: config.id
10061             },
10062             Roo.data.Record.create(config.fields)
10063         ),
10064         proxy : new Roo.data.MemoryProxy(config.data)
10065     });
10066     this.load();
10067 };
10068 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10069  * Based on:
10070  * Ext JS Library 1.1.1
10071  * Copyright(c) 2006-2007, Ext JS, LLC.
10072  *
10073  * Originally Released Under LGPL - original licence link has changed is not relivant.
10074  *
10075  * Fork - LGPL
10076  * <script type="text/javascript">
10077  */
10078
10079 /**
10080 /**
10081  * @extends Roo.data.Store
10082  * @class Roo.data.JsonStore
10083  * Small helper class to make creating Stores for JSON data easier. <br/>
10084 <pre><code>
10085 var store = new Roo.data.JsonStore({
10086     url: 'get-images.php',
10087     root: 'images',
10088     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10089 });
10090 </code></pre>
10091  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10092  * JsonReader and HttpProxy (unless inline data is provided).</b>
10093  * @cfg {Array} fields An array of field definition objects, or field name strings.
10094  * @constructor
10095  * @param {Object} config
10096  */
10097 Roo.data.JsonStore = function(c){
10098     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10099         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10100         reader: new Roo.data.JsonReader(c, c.fields)
10101     }));
10102 };
10103 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10104  * Based on:
10105  * Ext JS Library 1.1.1
10106  * Copyright(c) 2006-2007, Ext JS, LLC.
10107  *
10108  * Originally Released Under LGPL - original licence link has changed is not relivant.
10109  *
10110  * Fork - LGPL
10111  * <script type="text/javascript">
10112  */
10113
10114  
10115 Roo.data.Field = function(config){
10116     if(typeof config == "string"){
10117         config = {name: config};
10118     }
10119     Roo.apply(this, config);
10120     
10121     if(!this.type){
10122         this.type = "auto";
10123     }
10124     
10125     var st = Roo.data.SortTypes;
10126     // named sortTypes are supported, here we look them up
10127     if(typeof this.sortType == "string"){
10128         this.sortType = st[this.sortType];
10129     }
10130     
10131     // set default sortType for strings and dates
10132     if(!this.sortType){
10133         switch(this.type){
10134             case "string":
10135                 this.sortType = st.asUCString;
10136                 break;
10137             case "date":
10138                 this.sortType = st.asDate;
10139                 break;
10140             default:
10141                 this.sortType = st.none;
10142         }
10143     }
10144
10145     // define once
10146     var stripRe = /[\$,%]/g;
10147
10148     // prebuilt conversion function for this field, instead of
10149     // switching every time we're reading a value
10150     if(!this.convert){
10151         var cv, dateFormat = this.dateFormat;
10152         switch(this.type){
10153             case "":
10154             case "auto":
10155             case undefined:
10156                 cv = function(v){ return v; };
10157                 break;
10158             case "string":
10159                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10160                 break;
10161             case "int":
10162                 cv = function(v){
10163                     return v !== undefined && v !== null && v !== '' ?
10164                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10165                     };
10166                 break;
10167             case "float":
10168                 cv = function(v){
10169                     return v !== undefined && v !== null && v !== '' ?
10170                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10171                     };
10172                 break;
10173             case "bool":
10174             case "boolean":
10175                 cv = function(v){ return v === true || v === "true" || v == 1; };
10176                 break;
10177             case "date":
10178                 cv = function(v){
10179                     if(!v){
10180                         return '';
10181                     }
10182                     if(v instanceof Date){
10183                         return v;
10184                     }
10185                     if(dateFormat){
10186                         if(dateFormat == "timestamp"){
10187                             return new Date(v*1000);
10188                         }
10189                         return Date.parseDate(v, dateFormat);
10190                     }
10191                     var parsed = Date.parse(v);
10192                     return parsed ? new Date(parsed) : null;
10193                 };
10194              break;
10195             
10196         }
10197         this.convert = cv;
10198     }
10199 };
10200
10201 Roo.data.Field.prototype = {
10202     dateFormat: null,
10203     defaultValue: "",
10204     mapping: null,
10205     sortType : null,
10206     sortDir : "ASC"
10207 };/*
10208  * Based on:
10209  * Ext JS Library 1.1.1
10210  * Copyright(c) 2006-2007, Ext JS, LLC.
10211  *
10212  * Originally Released Under LGPL - original licence link has changed is not relivant.
10213  *
10214  * Fork - LGPL
10215  * <script type="text/javascript">
10216  */
10217  
10218 // Base class for reading structured data from a data source.  This class is intended to be
10219 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10220
10221 /**
10222  * @class Roo.data.DataReader
10223  * Base class for reading structured data from a data source.  This class is intended to be
10224  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10225  */
10226
10227 Roo.data.DataReader = function(meta, recordType){
10228     
10229     this.meta = meta;
10230     
10231     this.recordType = recordType instanceof Array ? 
10232         Roo.data.Record.create(recordType) : recordType;
10233 };
10234
10235 Roo.data.DataReader.prototype = {
10236      /**
10237      * Create an empty record
10238      * @param {Object} data (optional) - overlay some values
10239      * @return {Roo.data.Record} record created.
10240      */
10241     newRow :  function(d) {
10242         var da =  {};
10243         this.recordType.prototype.fields.each(function(c) {
10244             switch( c.type) {
10245                 case 'int' : da[c.name] = 0; break;
10246                 case 'date' : da[c.name] = new Date(); break;
10247                 case 'float' : da[c.name] = 0.0; break;
10248                 case 'boolean' : da[c.name] = false; break;
10249                 default : da[c.name] = ""; break;
10250             }
10251             
10252         });
10253         return new this.recordType(Roo.apply(da, d));
10254     }
10255     
10256 };/*
10257  * Based on:
10258  * Ext JS Library 1.1.1
10259  * Copyright(c) 2006-2007, Ext JS, LLC.
10260  *
10261  * Originally Released Under LGPL - original licence link has changed is not relivant.
10262  *
10263  * Fork - LGPL
10264  * <script type="text/javascript">
10265  */
10266
10267 /**
10268  * @class Roo.data.DataProxy
10269  * @extends Roo.data.Observable
10270  * This class is an abstract base class for implementations which provide retrieval of
10271  * unformatted data objects.<br>
10272  * <p>
10273  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10274  * (of the appropriate type which knows how to parse the data object) to provide a block of
10275  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10276  * <p>
10277  * Custom implementations must implement the load method as described in
10278  * {@link Roo.data.HttpProxy#load}.
10279  */
10280 Roo.data.DataProxy = function(){
10281     this.addEvents({
10282         /**
10283          * @event beforeload
10284          * Fires before a network request is made to retrieve a data object.
10285          * @param {Object} This DataProxy object.
10286          * @param {Object} params The params parameter to the load function.
10287          */
10288         beforeload : true,
10289         /**
10290          * @event load
10291          * Fires before the load method's callback is called.
10292          * @param {Object} This DataProxy object.
10293          * @param {Object} o The data object.
10294          * @param {Object} arg The callback argument object passed to the load function.
10295          */
10296         load : true,
10297         /**
10298          * @event loadexception
10299          * Fires if an Exception occurs during data retrieval.
10300          * @param {Object} This DataProxy object.
10301          * @param {Object} o The data object.
10302          * @param {Object} arg The callback argument object passed to the load function.
10303          * @param {Object} e The Exception.
10304          */
10305         loadexception : true
10306     });
10307     Roo.data.DataProxy.superclass.constructor.call(this);
10308 };
10309
10310 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10311
10312     /**
10313      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10314      */
10315 /*
10316  * Based on:
10317  * Ext JS Library 1.1.1
10318  * Copyright(c) 2006-2007, Ext JS, LLC.
10319  *
10320  * Originally Released Under LGPL - original licence link has changed is not relivant.
10321  *
10322  * Fork - LGPL
10323  * <script type="text/javascript">
10324  */
10325 /**
10326  * @class Roo.data.MemoryProxy
10327  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10328  * to the Reader when its load method is called.
10329  * @constructor
10330  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10331  */
10332 Roo.data.MemoryProxy = function(data){
10333     if (data.data) {
10334         data = data.data;
10335     }
10336     Roo.data.MemoryProxy.superclass.constructor.call(this);
10337     this.data = data;
10338 };
10339
10340 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10341     /**
10342      * Load data from the requested source (in this case an in-memory
10343      * data object passed to the constructor), read the data object into
10344      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10345      * process that block using the passed callback.
10346      * @param {Object} params This parameter is not used by the MemoryProxy class.
10347      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10348      * object into a block of Roo.data.Records.
10349      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10350      * The function must be passed <ul>
10351      * <li>The Record block object</li>
10352      * <li>The "arg" argument from the load function</li>
10353      * <li>A boolean success indicator</li>
10354      * </ul>
10355      * @param {Object} scope The scope in which to call the callback
10356      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10357      */
10358     load : function(params, reader, callback, scope, arg){
10359         params = params || {};
10360         var result;
10361         try {
10362             result = reader.readRecords(this.data);
10363         }catch(e){
10364             this.fireEvent("loadexception", this, arg, null, e);
10365             callback.call(scope, null, arg, false);
10366             return;
10367         }
10368         callback.call(scope, result, arg, true);
10369     },
10370     
10371     // private
10372     update : function(params, records){
10373         
10374     }
10375 });/*
10376  * Based on:
10377  * Ext JS Library 1.1.1
10378  * Copyright(c) 2006-2007, Ext JS, LLC.
10379  *
10380  * Originally Released Under LGPL - original licence link has changed is not relivant.
10381  *
10382  * Fork - LGPL
10383  * <script type="text/javascript">
10384  */
10385 /**
10386  * @class Roo.data.HttpProxy
10387  * @extends Roo.data.DataProxy
10388  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10389  * configured to reference a certain URL.<br><br>
10390  * <p>
10391  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10392  * from which the running page was served.<br><br>
10393  * <p>
10394  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10395  * <p>
10396  * Be aware that to enable the browser to parse an XML document, the server must set
10397  * the Content-Type header in the HTTP response to "text/xml".
10398  * @constructor
10399  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10400  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10401  * will be used to make the request.
10402  */
10403 Roo.data.HttpProxy = function(conn){
10404     Roo.data.HttpProxy.superclass.constructor.call(this);
10405     // is conn a conn config or a real conn?
10406     this.conn = conn;
10407     this.useAjax = !conn || !conn.events;
10408   
10409 };
10410
10411 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10412     // thse are take from connection...
10413     
10414     /**
10415      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10416      */
10417     /**
10418      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10419      * extra parameters to each request made by this object. (defaults to undefined)
10420      */
10421     /**
10422      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10423      *  to each request made by this object. (defaults to undefined)
10424      */
10425     /**
10426      * @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)
10427      */
10428     /**
10429      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10430      */
10431      /**
10432      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10433      * @type Boolean
10434      */
10435   
10436
10437     /**
10438      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10439      * @type Boolean
10440      */
10441     /**
10442      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10443      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10444      * a finer-grained basis than the DataProxy events.
10445      */
10446     getConnection : function(){
10447         return this.useAjax ? Roo.Ajax : this.conn;
10448     },
10449
10450     /**
10451      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10452      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10453      * process that block using the passed callback.
10454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10455      * for the request to the remote server.
10456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10457      * object into a block of Roo.data.Records.
10458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10459      * The function must be passed <ul>
10460      * <li>The Record block object</li>
10461      * <li>The "arg" argument from the load function</li>
10462      * <li>A boolean success indicator</li>
10463      * </ul>
10464      * @param {Object} scope The scope in which to call the callback
10465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10466      */
10467     load : function(params, reader, callback, scope, arg){
10468         if(this.fireEvent("beforeload", this, params) !== false){
10469             var  o = {
10470                 params : params || {},
10471                 request: {
10472                     callback : callback,
10473                     scope : scope,
10474                     arg : arg
10475                 },
10476                 reader: reader,
10477                 callback : this.loadResponse,
10478                 scope: this
10479             };
10480             if(this.useAjax){
10481                 Roo.applyIf(o, this.conn);
10482                 if(this.activeRequest){
10483                     Roo.Ajax.abort(this.activeRequest);
10484                 }
10485                 this.activeRequest = Roo.Ajax.request(o);
10486             }else{
10487                 this.conn.request(o);
10488             }
10489         }else{
10490             callback.call(scope||this, null, arg, false);
10491         }
10492     },
10493
10494     // private
10495     loadResponse : function(o, success, response){
10496         delete this.activeRequest;
10497         if(!success){
10498             this.fireEvent("loadexception", this, o, response);
10499             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10500             return;
10501         }
10502         var result;
10503         try {
10504             result = o.reader.read(response);
10505         }catch(e){
10506             this.fireEvent("loadexception", this, o, response, e);
10507             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10508             return;
10509         }
10510         
10511         this.fireEvent("load", this, o, o.request.arg);
10512         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10513     },
10514
10515     // private
10516     update : function(dataSet){
10517
10518     },
10519
10520     // private
10521     updateResponse : function(dataSet){
10522
10523     }
10524 });/*
10525  * Based on:
10526  * Ext JS Library 1.1.1
10527  * Copyright(c) 2006-2007, Ext JS, LLC.
10528  *
10529  * Originally Released Under LGPL - original licence link has changed is not relivant.
10530  *
10531  * Fork - LGPL
10532  * <script type="text/javascript">
10533  */
10534
10535 /**
10536  * @class Roo.data.ScriptTagProxy
10537  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10538  * other than the originating domain of the running page.<br><br>
10539  * <p>
10540  * <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
10541  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10542  * <p>
10543  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10544  * source code that is used as the source inside a &lt;script> tag.<br><br>
10545  * <p>
10546  * In order for the browser to process the returned data, the server must wrap the data object
10547  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10548  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10549  * depending on whether the callback name was passed:
10550  * <p>
10551  * <pre><code>
10552 boolean scriptTag = false;
10553 String cb = request.getParameter("callback");
10554 if (cb != null) {
10555     scriptTag = true;
10556     response.setContentType("text/javascript");
10557 } else {
10558     response.setContentType("application/x-json");
10559 }
10560 Writer out = response.getWriter();
10561 if (scriptTag) {
10562     out.write(cb + "(");
10563 }
10564 out.print(dataBlock.toJsonString());
10565 if (scriptTag) {
10566     out.write(");");
10567 }
10568 </pre></code>
10569  *
10570  * @constructor
10571  * @param {Object} config A configuration object.
10572  */
10573 Roo.data.ScriptTagProxy = function(config){
10574     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10575     Roo.apply(this, config);
10576     this.head = document.getElementsByTagName("head")[0];
10577 };
10578
10579 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10580
10581 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10582     /**
10583      * @cfg {String} url The URL from which to request the data object.
10584      */
10585     /**
10586      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10587      */
10588     timeout : 30000,
10589     /**
10590      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10591      * the server the name of the callback function set up by the load call to process the returned data object.
10592      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10593      * javascript output which calls this named function passing the data object as its only parameter.
10594      */
10595     callbackParam : "callback",
10596     /**
10597      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10598      * name to the request.
10599      */
10600     nocache : true,
10601
10602     /**
10603      * Load data from the configured URL, read the data object into
10604      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10605      * process that block using the passed callback.
10606      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10607      * for the request to the remote server.
10608      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10609      * object into a block of Roo.data.Records.
10610      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10611      * The function must be passed <ul>
10612      * <li>The Record block object</li>
10613      * <li>The "arg" argument from the load function</li>
10614      * <li>A boolean success indicator</li>
10615      * </ul>
10616      * @param {Object} scope The scope in which to call the callback
10617      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10618      */
10619     load : function(params, reader, callback, scope, arg){
10620         if(this.fireEvent("beforeload", this, params) !== false){
10621
10622             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10623
10624             var url = this.url;
10625             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10626             if(this.nocache){
10627                 url += "&_dc=" + (new Date().getTime());
10628             }
10629             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10630             var trans = {
10631                 id : transId,
10632                 cb : "stcCallback"+transId,
10633                 scriptId : "stcScript"+transId,
10634                 params : params,
10635                 arg : arg,
10636                 url : url,
10637                 callback : callback,
10638                 scope : scope,
10639                 reader : reader
10640             };
10641             var conn = this;
10642
10643             window[trans.cb] = function(o){
10644                 conn.handleResponse(o, trans);
10645             };
10646
10647             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10648
10649             if(this.autoAbort !== false){
10650                 this.abort();
10651             }
10652
10653             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10654
10655             var script = document.createElement("script");
10656             script.setAttribute("src", url);
10657             script.setAttribute("type", "text/javascript");
10658             script.setAttribute("id", trans.scriptId);
10659             this.head.appendChild(script);
10660
10661             this.trans = trans;
10662         }else{
10663             callback.call(scope||this, null, arg, false);
10664         }
10665     },
10666
10667     // private
10668     isLoading : function(){
10669         return this.trans ? true : false;
10670     },
10671
10672     /**
10673      * Abort the current server request.
10674      */
10675     abort : function(){
10676         if(this.isLoading()){
10677             this.destroyTrans(this.trans);
10678         }
10679     },
10680
10681     // private
10682     destroyTrans : function(trans, isLoaded){
10683         this.head.removeChild(document.getElementById(trans.scriptId));
10684         clearTimeout(trans.timeoutId);
10685         if(isLoaded){
10686             window[trans.cb] = undefined;
10687             try{
10688                 delete window[trans.cb];
10689             }catch(e){}
10690         }else{
10691             // if hasn't been loaded, wait for load to remove it to prevent script error
10692             window[trans.cb] = function(){
10693                 window[trans.cb] = undefined;
10694                 try{
10695                     delete window[trans.cb];
10696                 }catch(e){}
10697             };
10698         }
10699     },
10700
10701     // private
10702     handleResponse : function(o, trans){
10703         this.trans = false;
10704         this.destroyTrans(trans, true);
10705         var result;
10706         try {
10707             result = trans.reader.readRecords(o);
10708         }catch(e){
10709             this.fireEvent("loadexception", this, o, trans.arg, e);
10710             trans.callback.call(trans.scope||window, null, trans.arg, false);
10711             return;
10712         }
10713         this.fireEvent("load", this, o, trans.arg);
10714         trans.callback.call(trans.scope||window, result, trans.arg, true);
10715     },
10716
10717     // private
10718     handleFailure : function(trans){
10719         this.trans = false;
10720         this.destroyTrans(trans, false);
10721         this.fireEvent("loadexception", this, null, trans.arg);
10722         trans.callback.call(trans.scope||window, null, trans.arg, false);
10723     }
10724 });/*
10725  * Based on:
10726  * Ext JS Library 1.1.1
10727  * Copyright(c) 2006-2007, Ext JS, LLC.
10728  *
10729  * Originally Released Under LGPL - original licence link has changed is not relivant.
10730  *
10731  * Fork - LGPL
10732  * <script type="text/javascript">
10733  */
10734
10735 /**
10736  * @class Roo.data.JsonReader
10737  * @extends Roo.data.DataReader
10738  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10739  * based on mappings in a provided Roo.data.Record constructor.
10740  * 
10741  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10742  * in the reply previously. 
10743  * 
10744  * <p>
10745  * Example code:
10746  * <pre><code>
10747 var RecordDef = Roo.data.Record.create([
10748     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10749     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10750 ]);
10751 var myReader = new Roo.data.JsonReader({
10752     totalProperty: "results",    // The property which contains the total dataset size (optional)
10753     root: "rows",                // The property which contains an Array of row objects
10754     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10755 }, RecordDef);
10756 </code></pre>
10757  * <p>
10758  * This would consume a JSON file like this:
10759  * <pre><code>
10760 { 'results': 2, 'rows': [
10761     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10762     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10763 }
10764 </code></pre>
10765  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10766  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10767  * paged from the remote server.
10768  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10769  * @cfg {String} root name of the property which contains the Array of row objects.
10770  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10771  * @cfg {Array} fields Array of field definition objects
10772  * @constructor
10773  * Create a new JsonReader
10774  * @param {Object} meta Metadata configuration options
10775  * @param {Object} recordType Either an Array of field definition objects,
10776  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10777  */
10778 Roo.data.JsonReader = function(meta, recordType){
10779     
10780     meta = meta || {};
10781     // set some defaults:
10782     Roo.applyIf(meta, {
10783         totalProperty: 'total',
10784         successProperty : 'success',
10785         root : 'data',
10786         id : 'id'
10787     });
10788     
10789     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10790 };
10791 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10792     
10793     /**
10794      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10795      * Used by Store query builder to append _requestMeta to params.
10796      * 
10797      */
10798     metaFromRemote : false,
10799     /**
10800      * This method is only used by a DataProxy which has retrieved data from a remote server.
10801      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10802      * @return {Object} data A data block which is used by an Roo.data.Store object as
10803      * a cache of Roo.data.Records.
10804      */
10805     read : function(response){
10806         var json = response.responseText;
10807        
10808         var o = /* eval:var:o */ eval("("+json+")");
10809         if(!o) {
10810             throw {message: "JsonReader.read: Json object not found"};
10811         }
10812         
10813         if(o.metaData){
10814             
10815             delete this.ef;
10816             this.metaFromRemote = true;
10817             this.meta = o.metaData;
10818             this.recordType = Roo.data.Record.create(o.metaData.fields);
10819             this.onMetaChange(this.meta, this.recordType, o);
10820         }
10821         return this.readRecords(o);
10822     },
10823
10824     // private function a store will implement
10825     onMetaChange : function(meta, recordType, o){
10826
10827     },
10828
10829     /**
10830          * @ignore
10831          */
10832     simpleAccess: function(obj, subsc) {
10833         return obj[subsc];
10834     },
10835
10836         /**
10837          * @ignore
10838          */
10839     getJsonAccessor: function(){
10840         var re = /[\[\.]/;
10841         return function(expr) {
10842             try {
10843                 return(re.test(expr))
10844                     ? new Function("obj", "return obj." + expr)
10845                     : function(obj){
10846                         return obj[expr];
10847                     };
10848             } catch(e){}
10849             return Roo.emptyFn;
10850         };
10851     }(),
10852
10853     /**
10854      * Create a data block containing Roo.data.Records from an XML document.
10855      * @param {Object} o An object which contains an Array of row objects in the property specified
10856      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10857      * which contains the total size of the dataset.
10858      * @return {Object} data A data block which is used by an Roo.data.Store object as
10859      * a cache of Roo.data.Records.
10860      */
10861     readRecords : function(o){
10862         /**
10863          * After any data loads, the raw JSON data is available for further custom processing.
10864          * @type Object
10865          */
10866         this.o = o;
10867         var s = this.meta, Record = this.recordType,
10868             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10869
10870 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10871         if (!this.ef) {
10872             if(s.totalProperty) {
10873                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10874                 }
10875                 if(s.successProperty) {
10876                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10877                 }
10878                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10879                 if (s.id) {
10880                         var g = this.getJsonAccessor(s.id);
10881                         this.getId = function(rec) {
10882                                 var r = g(rec);  
10883                                 return (r === undefined || r === "") ? null : r;
10884                         };
10885                 } else {
10886                         this.getId = function(){return null;};
10887                 }
10888             this.ef = [];
10889             for(var jj = 0; jj < fl; jj++){
10890                 f = fi[jj];
10891                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10892                 this.ef[jj] = this.getJsonAccessor(map);
10893             }
10894         }
10895
10896         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10897         if(s.totalProperty){
10898             var vt = parseInt(this.getTotal(o), 10);
10899             if(!isNaN(vt)){
10900                 totalRecords = vt;
10901             }
10902         }
10903         if(s.successProperty){
10904             var vs = this.getSuccess(o);
10905             if(vs === false || vs === 'false'){
10906                 success = false;
10907             }
10908         }
10909         var records = [];
10910         for(var i = 0; i < c; i++){
10911                 var n = root[i];
10912             var values = {};
10913             var id = this.getId(n);
10914             for(var j = 0; j < fl; j++){
10915                 f = fi[j];
10916             var v = this.ef[j](n);
10917             if (!f.convert) {
10918                 Roo.log('missing convert for ' + f.name);
10919                 Roo.log(f);
10920                 continue;
10921             }
10922             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10923             }
10924             var record = new Record(values, id);
10925             record.json = n;
10926             records[i] = record;
10927         }
10928         return {
10929             raw : o,
10930             success : success,
10931             records : records,
10932             totalRecords : totalRecords
10933         };
10934     }
10935 });/*
10936  * Based on:
10937  * Ext JS Library 1.1.1
10938  * Copyright(c) 2006-2007, Ext JS, LLC.
10939  *
10940  * Originally Released Under LGPL - original licence link has changed is not relivant.
10941  *
10942  * Fork - LGPL
10943  * <script type="text/javascript">
10944  */
10945
10946 /**
10947  * @class Roo.data.ArrayReader
10948  * @extends Roo.data.DataReader
10949  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10950  * Each element of that Array represents a row of data fields. The
10951  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10952  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10953  * <p>
10954  * Example code:.
10955  * <pre><code>
10956 var RecordDef = Roo.data.Record.create([
10957     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10958     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10959 ]);
10960 var myReader = new Roo.data.ArrayReader({
10961     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10962 }, RecordDef);
10963 </code></pre>
10964  * <p>
10965  * This would consume an Array like this:
10966  * <pre><code>
10967 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10968   </code></pre>
10969  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10970  * @constructor
10971  * Create a new JsonReader
10972  * @param {Object} meta Metadata configuration options.
10973  * @param {Object} recordType Either an Array of field definition objects
10974  * as specified to {@link Roo.data.Record#create},
10975  * or an {@link Roo.data.Record} object
10976  * created using {@link Roo.data.Record#create}.
10977  */
10978 Roo.data.ArrayReader = function(meta, recordType){
10979     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10980 };
10981
10982 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10983     /**
10984      * Create a data block containing Roo.data.Records from an XML document.
10985      * @param {Object} o An Array of row objects which represents the dataset.
10986      * @return {Object} data A data block which is used by an Roo.data.Store object as
10987      * a cache of Roo.data.Records.
10988      */
10989     readRecords : function(o){
10990         var sid = this.meta ? this.meta.id : null;
10991         var recordType = this.recordType, fields = recordType.prototype.fields;
10992         var records = [];
10993         var root = o;
10994             for(var i = 0; i < root.length; i++){
10995                     var n = root[i];
10996                 var values = {};
10997                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10998                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10999                 var f = fields.items[j];
11000                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11001                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11002                 v = f.convert(v);
11003                 values[f.name] = v;
11004             }
11005                 var record = new recordType(values, id);
11006                 record.json = n;
11007                 records[records.length] = record;
11008             }
11009             return {
11010                 records : records,
11011                 totalRecords : records.length
11012             };
11013     }
11014 });/*
11015  * - LGPL
11016  * * 
11017  */
11018
11019 /**
11020  * @class Roo.bootstrap.ComboBox
11021  * @extends Roo.bootstrap.TriggerField
11022  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11023  * @cfg {Boolean} append (true|false) default false
11024  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11025  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11026  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11027  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11028  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11029  * @constructor
11030  * Create a new ComboBox.
11031  * @param {Object} config Configuration options
11032  */
11033 Roo.bootstrap.ComboBox = function(config){
11034     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11035     this.addEvents({
11036         /**
11037          * @event expand
11038          * Fires when the dropdown list is expanded
11039              * @param {Roo.bootstrap.ComboBox} combo This combo box
11040              */
11041         'expand' : true,
11042         /**
11043          * @event collapse
11044          * Fires when the dropdown list is collapsed
11045              * @param {Roo.bootstrap.ComboBox} combo This combo box
11046              */
11047         'collapse' : true,
11048         /**
11049          * @event beforeselect
11050          * Fires before a list item is selected. Return false to cancel the selection.
11051              * @param {Roo.bootstrap.ComboBox} combo This combo box
11052              * @param {Roo.data.Record} record The data record returned from the underlying store
11053              * @param {Number} index The index of the selected item in the dropdown list
11054              */
11055         'beforeselect' : true,
11056         /**
11057          * @event select
11058          * Fires when a list item is selected
11059              * @param {Roo.bootstrap.ComboBox} combo This combo box
11060              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11061              * @param {Number} index The index of the selected item in the dropdown list
11062              */
11063         'select' : true,
11064         /**
11065          * @event beforequery
11066          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11067          * The event object passed has these properties:
11068              * @param {Roo.bootstrap.ComboBox} combo This combo box
11069              * @param {String} query The query
11070              * @param {Boolean} forceAll true to force "all" query
11071              * @param {Boolean} cancel true to cancel the query
11072              * @param {Object} e The query event object
11073              */
11074         'beforequery': true,
11075          /**
11076          * @event add
11077          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11078              * @param {Roo.bootstrap.ComboBox} combo This combo box
11079              */
11080         'add' : true,
11081         /**
11082          * @event edit
11083          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11084              * @param {Roo.bootstrap.ComboBox} combo This combo box
11085              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11086              */
11087         'edit' : true,
11088         /**
11089          * @event remove
11090          * Fires when the remove value from the combobox array
11091              * @param {Roo.bootstrap.ComboBox} combo This combo box
11092              */
11093         'remove' : true,
11094         /**
11095          * @event specialfilter
11096          * Fires when specialfilter
11097             * @param {Roo.bootstrap.ComboBox} combo This combo box
11098             */
11099         'specialfilter' : true
11100         
11101     });
11102     
11103     this.item = [];
11104     this.tickItems = [];
11105     
11106     this.selectedIndex = -1;
11107     if(this.mode == 'local'){
11108         if(config.queryDelay === undefined){
11109             this.queryDelay = 10;
11110         }
11111         if(config.minChars === undefined){
11112             this.minChars = 0;
11113         }
11114     }
11115 };
11116
11117 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11118      
11119     /**
11120      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11121      * rendering into an Roo.Editor, defaults to false)
11122      */
11123     /**
11124      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11125      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11126      */
11127     /**
11128      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11129      */
11130     /**
11131      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11132      * the dropdown list (defaults to undefined, with no header element)
11133      */
11134
11135      /**
11136      * @cfg {String/Roo.Template} tpl The template to use to render the output
11137      */
11138      
11139      /**
11140      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11141      */
11142     listWidth: undefined,
11143     /**
11144      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11145      * mode = 'remote' or 'text' if mode = 'local')
11146      */
11147     displayField: undefined,
11148     
11149     /**
11150      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11151      * mode = 'remote' or 'value' if mode = 'local'). 
11152      * Note: use of a valueField requires the user make a selection
11153      * in order for a value to be mapped.
11154      */
11155     valueField: undefined,
11156     
11157     
11158     /**
11159      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11160      * field's data value (defaults to the underlying DOM element's name)
11161      */
11162     hiddenName: undefined,
11163     /**
11164      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11165      */
11166     listClass: '',
11167     /**
11168      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11169      */
11170     selectedClass: 'active',
11171     
11172     /**
11173      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11174      */
11175     shadow:'sides',
11176     /**
11177      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11178      * anchor positions (defaults to 'tl-bl')
11179      */
11180     listAlign: 'tl-bl?',
11181     /**
11182      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11183      */
11184     maxHeight: 300,
11185     /**
11186      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11187      * query specified by the allQuery config option (defaults to 'query')
11188      */
11189     triggerAction: 'query',
11190     /**
11191      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11192      * (defaults to 4, does not apply if editable = false)
11193      */
11194     minChars : 4,
11195     /**
11196      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11197      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11198      */
11199     typeAhead: false,
11200     /**
11201      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11202      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11203      */
11204     queryDelay: 500,
11205     /**
11206      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11207      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11208      */
11209     pageSize: 0,
11210     /**
11211      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11212      * when editable = true (defaults to false)
11213      */
11214     selectOnFocus:false,
11215     /**
11216      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11217      */
11218     queryParam: 'query',
11219     /**
11220      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11221      * when mode = 'remote' (defaults to 'Loading...')
11222      */
11223     loadingText: 'Loading...',
11224     /**
11225      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11226      */
11227     resizable: false,
11228     /**
11229      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11230      */
11231     handleHeight : 8,
11232     /**
11233      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11234      * traditional select (defaults to true)
11235      */
11236     editable: true,
11237     /**
11238      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11239      */
11240     allQuery: '',
11241     /**
11242      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11243      */
11244     mode: 'remote',
11245     /**
11246      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11247      * listWidth has a higher value)
11248      */
11249     minListWidth : 70,
11250     /**
11251      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11252      * allow the user to set arbitrary text into the field (defaults to false)
11253      */
11254     forceSelection:false,
11255     /**
11256      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11257      * if typeAhead = true (defaults to 250)
11258      */
11259     typeAheadDelay : 250,
11260     /**
11261      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11262      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11263      */
11264     valueNotFoundText : undefined,
11265     /**
11266      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11267      */
11268     blockFocus : false,
11269     
11270     /**
11271      * @cfg {Boolean} disableClear Disable showing of clear button.
11272      */
11273     disableClear : false,
11274     /**
11275      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11276      */
11277     alwaysQuery : false,
11278     
11279     /**
11280      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11281      */
11282     multiple : false,
11283     
11284     /**
11285      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11286      */
11287     invalidClass : "has-warning",
11288     
11289     /**
11290      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11291      */
11292     validClass : "has-success",
11293     
11294     /**
11295      * @cfg {Boolean} specialFilter (true|false) special filter default false
11296      */
11297     specialFilter : false,
11298     
11299     //private
11300     addicon : false,
11301     editicon: false,
11302     
11303     page: 0,
11304     hasQuery: false,
11305     append: false,
11306     loadNext: false,
11307     autoFocus : true,
11308     tickable : false,
11309     btnPosition : 'right',
11310     triggerList : true,
11311     showToggleBtn : true,
11312     // element that contains real text value.. (when hidden is used..)
11313     
11314     getAutoCreate : function()
11315     {
11316         var cfg = false;
11317         
11318         /*
11319          *  Normal ComboBox
11320          */
11321         if(!this.tickable){
11322             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11323             return cfg;
11324         }
11325         
11326         /*
11327          *  ComboBox with tickable selections
11328          */
11329              
11330         var align = this.labelAlign || this.parentLabelAlign();
11331         
11332         cfg = {
11333             cls : 'form-group roo-combobox-tickable' //input-group
11334         };
11335         
11336         var buttons = {
11337             tag : 'div',
11338             cls : 'tickable-buttons',
11339             cn : [
11340                 {
11341                     tag : 'button',
11342                     type : 'button',
11343                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11344                     html : 'Edit'
11345                 },
11346                 {
11347                     tag : 'button',
11348                     type : 'button',
11349                     name : 'ok',
11350                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11351                     html : 'Done'
11352                 },
11353                 {
11354                     tag : 'button',
11355                     type : 'button',
11356                     name : 'cancel',
11357                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11358                     html : 'Cancel'
11359                 }
11360             ]
11361         };
11362         
11363         if(this.editable){
11364             buttons.cn.unshift({
11365                 tag: 'input',
11366                 cls: 'select2-search-field-input'
11367             });
11368         }
11369         
11370         var _this = this;
11371         
11372         Roo.each(buttons.cn, function(c){
11373             if (_this.size) {
11374                 c.cls += ' btn-' + _this.size;
11375             }
11376
11377             if (_this.disabled) {
11378                 c.disabled = true;
11379             }
11380         });
11381         
11382         var box = {
11383             tag: 'div',
11384             cn: [
11385                 {
11386                     tag: 'input',
11387                     type : 'hidden',
11388                     cls: 'form-hidden-field'
11389                 },
11390                 {
11391                     tag: 'ul',
11392                     cls: 'select2-choices',
11393                     cn:[
11394                         {
11395                             tag: 'li',
11396                             cls: 'select2-search-field',
11397                             cn: [
11398
11399                                 buttons
11400                             ]
11401                         }
11402                     ]
11403                 }
11404             ]
11405         }
11406         
11407         var combobox = {
11408             cls: 'select2-container input-group select2-container-multi',
11409             cn: [
11410                 box
11411 //                {
11412 //                    tag: 'ul',
11413 //                    cls: 'typeahead typeahead-long dropdown-menu',
11414 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11415 //                }
11416             ]
11417         };
11418         
11419         if(this.hasFeedback && !this.allowBlank){
11420             
11421             var feedback = {
11422                 tag: 'span',
11423                 cls: 'glyphicon form-control-feedback'
11424             };
11425
11426             combobox.cn.push(feedback);
11427         }
11428         
11429         if (align ==='left' && this.fieldLabel.length) {
11430             
11431                 Roo.log("left and has label");
11432                 cfg.cn = [
11433                     
11434                     {
11435                         tag: 'label',
11436                         'for' :  id,
11437                         cls : 'control-label col-sm-' + this.labelWidth,
11438                         html : this.fieldLabel
11439                         
11440                     },
11441                     {
11442                         cls : "col-sm-" + (12 - this.labelWidth), 
11443                         cn: [
11444                             combobox
11445                         ]
11446                     }
11447                     
11448                 ];
11449         } else if ( this.fieldLabel.length) {
11450                 Roo.log(" label");
11451                  cfg.cn = [
11452                    
11453                     {
11454                         tag: 'label',
11455                         //cls : 'input-group-addon',
11456                         html : this.fieldLabel
11457                         
11458                     },
11459                     
11460                     combobox
11461                     
11462                 ];
11463
11464         } else {
11465             
11466                 Roo.log(" no label && no align");
11467                 cfg = combobox
11468                      
11469                 
11470         }
11471          
11472         var settings=this;
11473         ['xs','sm','md','lg'].map(function(size){
11474             if (settings[size]) {
11475                 cfg.cls += ' col-' + size + '-' + settings[size];
11476             }
11477         });
11478         
11479         return cfg;
11480         
11481     },
11482     
11483     // private
11484     initEvents: function()
11485     {
11486         
11487         if (!this.store) {
11488             throw "can not find store for combo";
11489         }
11490         this.store = Roo.factory(this.store, Roo.data);
11491         
11492         if(this.tickable){
11493             this.initTickableEvents();
11494             return;
11495         }
11496         
11497         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11498         
11499         if(this.hiddenName){
11500             
11501             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11502             
11503             this.hiddenField.dom.value =
11504                 this.hiddenValue !== undefined ? this.hiddenValue :
11505                 this.value !== undefined ? this.value : '';
11506
11507             // prevent input submission
11508             this.el.dom.removeAttribute('name');
11509             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11510              
11511              
11512         }
11513         //if(Roo.isGecko){
11514         //    this.el.dom.setAttribute('autocomplete', 'off');
11515         //}
11516         
11517         var cls = 'x-combo-list';
11518         
11519         //this.list = new Roo.Layer({
11520         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11521         //});
11522         
11523         var _this = this;
11524         
11525         (function(){
11526             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11527             _this.list.setWidth(lw);
11528         }).defer(100);
11529         
11530         this.list.on('mouseover', this.onViewOver, this);
11531         this.list.on('mousemove', this.onViewMove, this);
11532         
11533         this.list.on('scroll', this.onViewScroll, this);
11534         
11535         /*
11536         this.list.swallowEvent('mousewheel');
11537         this.assetHeight = 0;
11538
11539         if(this.title){
11540             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11541             this.assetHeight += this.header.getHeight();
11542         }
11543
11544         this.innerList = this.list.createChild({cls:cls+'-inner'});
11545         this.innerList.on('mouseover', this.onViewOver, this);
11546         this.innerList.on('mousemove', this.onViewMove, this);
11547         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11548         
11549         if(this.allowBlank && !this.pageSize && !this.disableClear){
11550             this.footer = this.list.createChild({cls:cls+'-ft'});
11551             this.pageTb = new Roo.Toolbar(this.footer);
11552            
11553         }
11554         if(this.pageSize){
11555             this.footer = this.list.createChild({cls:cls+'-ft'});
11556             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11557                     {pageSize: this.pageSize});
11558             
11559         }
11560         
11561         if (this.pageTb && this.allowBlank && !this.disableClear) {
11562             var _this = this;
11563             this.pageTb.add(new Roo.Toolbar.Fill(), {
11564                 cls: 'x-btn-icon x-btn-clear',
11565                 text: '&#160;',
11566                 handler: function()
11567                 {
11568                     _this.collapse();
11569                     _this.clearValue();
11570                     _this.onSelect(false, -1);
11571                 }
11572             });
11573         }
11574         if (this.footer) {
11575             this.assetHeight += this.footer.getHeight();
11576         }
11577         */
11578             
11579         if(!this.tpl){
11580             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11581         }
11582
11583         this.view = new Roo.View(this.list, this.tpl, {
11584             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11585         });
11586         //this.view.wrapEl.setDisplayed(false);
11587         this.view.on('click', this.onViewClick, this);
11588         
11589         
11590         
11591         this.store.on('beforeload', this.onBeforeLoad, this);
11592         this.store.on('load', this.onLoad, this);
11593         this.store.on('loadexception', this.onLoadException, this);
11594         /*
11595         if(this.resizable){
11596             this.resizer = new Roo.Resizable(this.list,  {
11597                pinned:true, handles:'se'
11598             });
11599             this.resizer.on('resize', function(r, w, h){
11600                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11601                 this.listWidth = w;
11602                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11603                 this.restrictHeight();
11604             }, this);
11605             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11606         }
11607         */
11608         if(!this.editable){
11609             this.editable = true;
11610             this.setEditable(false);
11611         }
11612         
11613         /*
11614         
11615         if (typeof(this.events.add.listeners) != 'undefined') {
11616             
11617             this.addicon = this.wrap.createChild(
11618                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11619        
11620             this.addicon.on('click', function(e) {
11621                 this.fireEvent('add', this);
11622             }, this);
11623         }
11624         if (typeof(this.events.edit.listeners) != 'undefined') {
11625             
11626             this.editicon = this.wrap.createChild(
11627                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11628             if (this.addicon) {
11629                 this.editicon.setStyle('margin-left', '40px');
11630             }
11631             this.editicon.on('click', function(e) {
11632                 
11633                 // we fire even  if inothing is selected..
11634                 this.fireEvent('edit', this, this.lastData );
11635                 
11636             }, this);
11637         }
11638         */
11639         
11640         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11641             "up" : function(e){
11642                 this.inKeyMode = true;
11643                 this.selectPrev();
11644             },
11645
11646             "down" : function(e){
11647                 if(!this.isExpanded()){
11648                     this.onTriggerClick();
11649                 }else{
11650                     this.inKeyMode = true;
11651                     this.selectNext();
11652                 }
11653             },
11654
11655             "enter" : function(e){
11656 //                this.onViewClick();
11657                 //return true;
11658                 this.collapse();
11659                 
11660                 if(this.fireEvent("specialkey", this, e)){
11661                     this.onViewClick(false);
11662                 }
11663                 
11664                 return true;
11665             },
11666
11667             "esc" : function(e){
11668                 this.collapse();
11669             },
11670
11671             "tab" : function(e){
11672                 this.collapse();
11673                 
11674                 if(this.fireEvent("specialkey", this, e)){
11675                     this.onViewClick(false);
11676                 }
11677                 
11678                 return true;
11679             },
11680
11681             scope : this,
11682
11683             doRelay : function(foo, bar, hname){
11684                 if(hname == 'down' || this.scope.isExpanded()){
11685                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11686                 }
11687                 return true;
11688             },
11689
11690             forceKeyDown: true
11691         });
11692         
11693         
11694         this.queryDelay = Math.max(this.queryDelay || 10,
11695                 this.mode == 'local' ? 10 : 250);
11696         
11697         
11698         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11699         
11700         if(this.typeAhead){
11701             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11702         }
11703         if(this.editable !== false){
11704             this.inputEl().on("keyup", this.onKeyUp, this);
11705         }
11706         if(this.forceSelection){
11707             this.inputEl().on('blur', this.doForce, this);
11708         }
11709         
11710         if(this.multiple){
11711             this.choices = this.el.select('ul.select2-choices', true).first();
11712             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11713         }
11714     },
11715     
11716     initTickableEvents: function()
11717     {   
11718         this.createList();
11719         
11720         if(this.hiddenName){
11721             
11722             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11723             
11724             this.hiddenField.dom.value =
11725                 this.hiddenValue !== undefined ? this.hiddenValue :
11726                 this.value !== undefined ? this.value : '';
11727
11728             // prevent input submission
11729             this.el.dom.removeAttribute('name');
11730             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11731              
11732              
11733         }
11734         
11735 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11736         
11737         this.choices = this.el.select('ul.select2-choices', true).first();
11738         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11739         if(this.triggerList){
11740             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11741         }
11742          
11743         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11744         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11745         
11746         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11747         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11748         
11749         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11750         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11751         
11752         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11753         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11754         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11755         
11756         this.okBtn.hide();
11757         this.cancelBtn.hide();
11758         
11759         var _this = this;
11760         
11761         (function(){
11762             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11763             _this.list.setWidth(lw);
11764         }).defer(100);
11765         
11766         this.list.on('mouseover', this.onViewOver, this);
11767         this.list.on('mousemove', this.onViewMove, this);
11768         
11769         this.list.on('scroll', this.onViewScroll, this);
11770         
11771         if(!this.tpl){
11772             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>';
11773         }
11774
11775         this.view = new Roo.View(this.list, this.tpl, {
11776             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11777         });
11778         
11779         //this.view.wrapEl.setDisplayed(false);
11780         this.view.on('click', this.onViewClick, this);
11781         
11782         
11783         
11784         this.store.on('beforeload', this.onBeforeLoad, this);
11785         this.store.on('load', this.onLoad, this);
11786         this.store.on('loadexception', this.onLoadException, this);
11787         
11788         if(this.editable){
11789             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11790                 "up" : function(e){
11791                     this.inKeyMode = true;
11792                     this.selectPrev();
11793                 },
11794
11795                 "down" : function(e){
11796                     this.inKeyMode = true;
11797                     this.selectNext();
11798                 },
11799
11800                 "enter" : function(e){
11801                     if(this.fireEvent("specialkey", this, e)){
11802                         this.onViewClick(false);
11803                     }
11804                     
11805                     return true;
11806                 },
11807
11808                 "esc" : function(e){
11809                     this.onTickableFooterButtonClick(e, false, false);
11810                 },
11811
11812                 "tab" : function(e){
11813                     this.fireEvent("specialkey", this, e);
11814                     
11815                     this.onTickableFooterButtonClick(e, false, false);
11816                     
11817                     return true;
11818                 },
11819
11820                 scope : this,
11821
11822                 doRelay : function(e, fn, key){
11823                     if(this.scope.isExpanded()){
11824                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11825                     }
11826                     return true;
11827                 },
11828
11829                 forceKeyDown: true
11830             });
11831         }
11832         
11833         this.queryDelay = Math.max(this.queryDelay || 10,
11834                 this.mode == 'local' ? 10 : 250);
11835         
11836         
11837         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11838         
11839         if(this.typeAhead){
11840             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11841         }
11842         
11843         if(this.editable !== false){
11844             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11845         }
11846         
11847     },
11848
11849     onDestroy : function(){
11850         if(this.view){
11851             this.view.setStore(null);
11852             this.view.el.removeAllListeners();
11853             this.view.el.remove();
11854             this.view.purgeListeners();
11855         }
11856         if(this.list){
11857             this.list.dom.innerHTML  = '';
11858         }
11859         
11860         if(this.store){
11861             this.store.un('beforeload', this.onBeforeLoad, this);
11862             this.store.un('load', this.onLoad, this);
11863             this.store.un('loadexception', this.onLoadException, this);
11864         }
11865         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11866     },
11867
11868     // private
11869     fireKey : function(e){
11870         if(e.isNavKeyPress() && !this.list.isVisible()){
11871             this.fireEvent("specialkey", this, e);
11872         }
11873     },
11874
11875     // private
11876     onResize: function(w, h){
11877 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11878 //        
11879 //        if(typeof w != 'number'){
11880 //            // we do not handle it!?!?
11881 //            return;
11882 //        }
11883 //        var tw = this.trigger.getWidth();
11884 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11885 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11886 //        var x = w - tw;
11887 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11888 //            
11889 //        //this.trigger.setStyle('left', x+'px');
11890 //        
11891 //        if(this.list && this.listWidth === undefined){
11892 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11893 //            this.list.setWidth(lw);
11894 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11895 //        }
11896         
11897     
11898         
11899     },
11900
11901     /**
11902      * Allow or prevent the user from directly editing the field text.  If false is passed,
11903      * the user will only be able to select from the items defined in the dropdown list.  This method
11904      * is the runtime equivalent of setting the 'editable' config option at config time.
11905      * @param {Boolean} value True to allow the user to directly edit the field text
11906      */
11907     setEditable : function(value){
11908         if(value == this.editable){
11909             return;
11910         }
11911         this.editable = value;
11912         if(!value){
11913             this.inputEl().dom.setAttribute('readOnly', true);
11914             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11915             this.inputEl().addClass('x-combo-noedit');
11916         }else{
11917             this.inputEl().dom.setAttribute('readOnly', false);
11918             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11919             this.inputEl().removeClass('x-combo-noedit');
11920         }
11921     },
11922
11923     // private
11924     
11925     onBeforeLoad : function(combo,opts){
11926         if(!this.hasFocus){
11927             return;
11928         }
11929          if (!opts.add) {
11930             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11931          }
11932         this.restrictHeight();
11933         this.selectedIndex = -1;
11934     },
11935
11936     // private
11937     onLoad : function(){
11938         
11939         this.hasQuery = false;
11940         
11941         if(!this.hasFocus){
11942             return;
11943         }
11944         
11945         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11946             this.loading.hide();
11947         }
11948              
11949         if(this.store.getCount() > 0){
11950             this.expand();
11951             this.restrictHeight();
11952             if(this.lastQuery == this.allQuery){
11953                 if(this.editable && !this.tickable){
11954                     this.inputEl().dom.select();
11955                 }
11956                 
11957                 if(
11958                     !this.selectByValue(this.value, true) &&
11959                     this.autoFocus && 
11960                     (
11961                         !this.store.lastOptions ||
11962                         typeof(this.store.lastOptions.add) == 'undefined' || 
11963                         this.store.lastOptions.add != true
11964                     )
11965                 ){
11966                     this.select(0, true);
11967                 }
11968             }else{
11969                 if(this.autoFocus){
11970                     this.selectNext();
11971                 }
11972                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11973                     this.taTask.delay(this.typeAheadDelay);
11974                 }
11975             }
11976         }else{
11977             this.onEmptyResults();
11978         }
11979         
11980         //this.el.focus();
11981     },
11982     // private
11983     onLoadException : function()
11984     {
11985         this.hasQuery = false;
11986         
11987         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11988             this.loading.hide();
11989         }
11990         
11991         if(this.tickable && this.editable){
11992             return;
11993         }
11994         
11995         this.collapse();
11996         
11997         Roo.log(this.store.reader.jsonData);
11998         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11999             // fixme
12000             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12001         }
12002         
12003         
12004     },
12005     // private
12006     onTypeAhead : function(){
12007         if(this.store.getCount() > 0){
12008             var r = this.store.getAt(0);
12009             var newValue = r.data[this.displayField];
12010             var len = newValue.length;
12011             var selStart = this.getRawValue().length;
12012             
12013             if(selStart != len){
12014                 this.setRawValue(newValue);
12015                 this.selectText(selStart, newValue.length);
12016             }
12017         }
12018     },
12019
12020     // private
12021     onSelect : function(record, index){
12022         
12023         if(this.fireEvent('beforeselect', this, record, index) !== false){
12024         
12025             this.setFromData(index > -1 ? record.data : false);
12026             
12027             this.collapse();
12028             this.fireEvent('select', this, record, index);
12029         }
12030     },
12031
12032     /**
12033      * Returns the currently selected field value or empty string if no value is set.
12034      * @return {String} value The selected value
12035      */
12036     getValue : function(){
12037         
12038         if(this.multiple){
12039             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12040         }
12041         
12042         if(this.valueField){
12043             return typeof this.value != 'undefined' ? this.value : '';
12044         }else{
12045             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12046         }
12047     },
12048
12049     /**
12050      * Clears any text/value currently set in the field
12051      */
12052     clearValue : function(){
12053         if(this.hiddenField){
12054             this.hiddenField.dom.value = '';
12055         }
12056         this.value = '';
12057         this.setRawValue('');
12058         this.lastSelectionText = '';
12059         this.lastData = false;
12060         
12061         var close = this.closeTriggerEl();
12062         
12063         if(close){
12064             close.hide();
12065         }
12066         
12067     },
12068
12069     /**
12070      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12071      * will be displayed in the field.  If the value does not match the data value of an existing item,
12072      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12073      * Otherwise the field will be blank (although the value will still be set).
12074      * @param {String} value The value to match
12075      */
12076     setValue : function(v){
12077         if(this.multiple){
12078             this.syncValue();
12079             return;
12080         }
12081         
12082         var text = v;
12083         if(this.valueField){
12084             var r = this.findRecord(this.valueField, v);
12085             if(r){
12086                 text = r.data[this.displayField];
12087             }else if(this.valueNotFoundText !== undefined){
12088                 text = this.valueNotFoundText;
12089             }
12090         }
12091         this.lastSelectionText = text;
12092         if(this.hiddenField){
12093             this.hiddenField.dom.value = v;
12094         }
12095         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12096         this.value = v;
12097         
12098         var close = this.closeTriggerEl();
12099         
12100         if(close){
12101             (v.length || v * 1 > 0) ? close.show() : close.hide();
12102         }
12103     },
12104     /**
12105      * @property {Object} the last set data for the element
12106      */
12107     
12108     lastData : false,
12109     /**
12110      * Sets the value of the field based on a object which is related to the record format for the store.
12111      * @param {Object} value the value to set as. or false on reset?
12112      */
12113     setFromData : function(o){
12114         
12115         if(this.multiple){
12116             this.addItem(o);
12117             return;
12118         }
12119             
12120         var dv = ''; // display value
12121         var vv = ''; // value value..
12122         this.lastData = o;
12123         if (this.displayField) {
12124             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12125         } else {
12126             // this is an error condition!!!
12127             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12128         }
12129         
12130         if(this.valueField){
12131             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12132         }
12133         
12134         var close = this.closeTriggerEl();
12135         
12136         if(close){
12137             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12138         }
12139         
12140         if(this.hiddenField){
12141             this.hiddenField.dom.value = vv;
12142             
12143             this.lastSelectionText = dv;
12144             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12145             this.value = vv;
12146             return;
12147         }
12148         // no hidden field.. - we store the value in 'value', but still display
12149         // display field!!!!
12150         this.lastSelectionText = dv;
12151         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12152         this.value = vv;
12153         
12154         
12155         
12156     },
12157     // private
12158     reset : function(){
12159         // overridden so that last data is reset..
12160         
12161         if(this.multiple){
12162             this.clearItem();
12163             return;
12164         }
12165         
12166         this.setValue(this.originalValue);
12167         this.clearInvalid();
12168         this.lastData = false;
12169         if (this.view) {
12170             this.view.clearSelections();
12171         }
12172     },
12173     // private
12174     findRecord : function(prop, value){
12175         var record;
12176         if(this.store.getCount() > 0){
12177             this.store.each(function(r){
12178                 if(r.data[prop] == value){
12179                     record = r;
12180                     return false;
12181                 }
12182                 return true;
12183             });
12184         }
12185         return record;
12186     },
12187     
12188     getName: function()
12189     {
12190         // returns hidden if it's set..
12191         if (!this.rendered) {return ''};
12192         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12193         
12194     },
12195     // private
12196     onViewMove : function(e, t){
12197         this.inKeyMode = false;
12198     },
12199
12200     // private
12201     onViewOver : function(e, t){
12202         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12203             return;
12204         }
12205         var item = this.view.findItemFromChild(t);
12206         
12207         if(item){
12208             var index = this.view.indexOf(item);
12209             this.select(index, false);
12210         }
12211     },
12212
12213     // private
12214     onViewClick : function(view, doFocus, el, e)
12215     {
12216         var index = this.view.getSelectedIndexes()[0];
12217         
12218         var r = this.store.getAt(index);
12219         
12220         if(this.tickable){
12221             
12222             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12223                 return;
12224             }
12225             
12226             var rm = false;
12227             var _this = this;
12228             
12229             Roo.each(this.tickItems, function(v,k){
12230                 
12231                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12232                     _this.tickItems.splice(k, 1);
12233                     
12234                     if(typeof(e) == 'undefined' && view == false){
12235                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12236                     }
12237                     
12238                     rm = true;
12239                     return;
12240                 }
12241             });
12242             
12243             if(rm){
12244                 return;
12245             }
12246             
12247             this.tickItems.push(r.data);
12248             
12249             if(typeof(e) == 'undefined' && view == false){
12250                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12251             }
12252                     
12253             return;
12254         }
12255         
12256         if(r){
12257             this.onSelect(r, index);
12258         }
12259         if(doFocus !== false && !this.blockFocus){
12260             this.inputEl().focus();
12261         }
12262     },
12263
12264     // private
12265     restrictHeight : function(){
12266         //this.innerList.dom.style.height = '';
12267         //var inner = this.innerList.dom;
12268         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12269         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12270         //this.list.beginUpdate();
12271         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12272         this.list.alignTo(this.inputEl(), this.listAlign);
12273         this.list.alignTo(this.inputEl(), this.listAlign);
12274         //this.list.endUpdate();
12275     },
12276
12277     // private
12278     onEmptyResults : function(){
12279         
12280         if(this.tickable && this.editable){
12281             this.restrictHeight();
12282             return;
12283         }
12284         
12285         this.collapse();
12286     },
12287
12288     /**
12289      * Returns true if the dropdown list is expanded, else false.
12290      */
12291     isExpanded : function(){
12292         return this.list.isVisible();
12293     },
12294
12295     /**
12296      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12297      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12298      * @param {String} value The data value of the item to select
12299      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12300      * selected item if it is not currently in view (defaults to true)
12301      * @return {Boolean} True if the value matched an item in the list, else false
12302      */
12303     selectByValue : function(v, scrollIntoView){
12304         if(v !== undefined && v !== null){
12305             var r = this.findRecord(this.valueField || this.displayField, v);
12306             if(r){
12307                 this.select(this.store.indexOf(r), scrollIntoView);
12308                 return true;
12309             }
12310         }
12311         return false;
12312     },
12313
12314     /**
12315      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12316      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12317      * @param {Number} index The zero-based index of the list item to select
12318      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12319      * selected item if it is not currently in view (defaults to true)
12320      */
12321     select : function(index, scrollIntoView){
12322         this.selectedIndex = index;
12323         this.view.select(index);
12324         if(scrollIntoView !== false){
12325             var el = this.view.getNode(index);
12326             /*
12327              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12328              */
12329             if(el){
12330                 this.list.scrollChildIntoView(el, false);
12331             }
12332         }
12333     },
12334
12335     // private
12336     selectNext : function(){
12337         var ct = this.store.getCount();
12338         if(ct > 0){
12339             if(this.selectedIndex == -1){
12340                 this.select(0);
12341             }else if(this.selectedIndex < ct-1){
12342                 this.select(this.selectedIndex+1);
12343             }
12344         }
12345     },
12346
12347     // private
12348     selectPrev : function(){
12349         var ct = this.store.getCount();
12350         if(ct > 0){
12351             if(this.selectedIndex == -1){
12352                 this.select(0);
12353             }else if(this.selectedIndex != 0){
12354                 this.select(this.selectedIndex-1);
12355             }
12356         }
12357     },
12358
12359     // private
12360     onKeyUp : function(e){
12361         if(this.editable !== false && !e.isSpecialKey()){
12362             this.lastKey = e.getKey();
12363             this.dqTask.delay(this.queryDelay);
12364         }
12365     },
12366
12367     // private
12368     validateBlur : function(){
12369         return !this.list || !this.list.isVisible();   
12370     },
12371
12372     // private
12373     initQuery : function(){
12374         
12375         var v = this.getRawValue();
12376         
12377         if(this.tickable && this.editable){
12378             v = this.tickableInputEl().getValue();
12379         }
12380         
12381         this.doQuery(v);
12382     },
12383
12384     // private
12385     doForce : function(){
12386         if(this.inputEl().dom.value.length > 0){
12387             this.inputEl().dom.value =
12388                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12389              
12390         }
12391     },
12392
12393     /**
12394      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12395      * query allowing the query action to be canceled if needed.
12396      * @param {String} query The SQL query to execute
12397      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12398      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12399      * saved in the current store (defaults to false)
12400      */
12401     doQuery : function(q, forceAll){
12402         
12403         if(q === undefined || q === null){
12404             q = '';
12405         }
12406         var qe = {
12407             query: q,
12408             forceAll: forceAll,
12409             combo: this,
12410             cancel:false
12411         };
12412         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12413             return false;
12414         }
12415         q = qe.query;
12416         
12417         forceAll = qe.forceAll;
12418         if(forceAll === true || (q.length >= this.minChars)){
12419             
12420             this.hasQuery = true;
12421             
12422             if(this.lastQuery != q || this.alwaysQuery){
12423                 this.lastQuery = q;
12424                 if(this.mode == 'local'){
12425                     this.selectedIndex = -1;
12426                     if(forceAll){
12427                         this.store.clearFilter();
12428                     }else{
12429                         
12430                         if(this.specialFilter){
12431                             this.fireEvent('specialfilter', this);
12432                             this.onLoad();
12433                             return;
12434                         }
12435                         
12436                         this.store.filter(this.displayField, q);
12437                     }
12438                     
12439                     this.store.fireEvent("datachanged", this.store);
12440                     
12441                     this.onLoad();
12442                     
12443                     
12444                 }else{
12445                     
12446                     this.store.baseParams[this.queryParam] = q;
12447                     
12448                     var options = {params : this.getParams(q)};
12449                     
12450                     if(this.loadNext){
12451                         options.add = true;
12452                         options.params.start = this.page * this.pageSize;
12453                     }
12454                     
12455                     this.store.load(options);
12456                     
12457                     /*
12458                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12459                      *  we should expand the list on onLoad
12460                      *  so command out it
12461                      */
12462 //                    this.expand();
12463                 }
12464             }else{
12465                 this.selectedIndex = -1;
12466                 this.onLoad();   
12467             }
12468         }
12469         
12470         this.loadNext = false;
12471     },
12472     
12473     // private
12474     getParams : function(q){
12475         var p = {};
12476         //p[this.queryParam] = q;
12477         
12478         if(this.pageSize){
12479             p.start = 0;
12480             p.limit = this.pageSize;
12481         }
12482         return p;
12483     },
12484
12485     /**
12486      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12487      */
12488     collapse : function(){
12489         if(!this.isExpanded()){
12490             return;
12491         }
12492         
12493         this.list.hide();
12494         
12495         if(this.tickable){
12496             this.hasFocus = false;
12497             this.okBtn.hide();
12498             this.cancelBtn.hide();
12499             this.trigger.show();
12500             
12501             if(this.editable){
12502                 this.tickableInputEl().dom.value = '';
12503                 this.tickableInputEl().blur();
12504             }
12505             
12506         }
12507         
12508         Roo.get(document).un('mousedown', this.collapseIf, this);
12509         Roo.get(document).un('mousewheel', this.collapseIf, this);
12510         if (!this.editable) {
12511             Roo.get(document).un('keydown', this.listKeyPress, this);
12512         }
12513         this.fireEvent('collapse', this);
12514     },
12515
12516     // private
12517     collapseIf : function(e){
12518         var in_combo  = e.within(this.el);
12519         var in_list =  e.within(this.list);
12520         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12521         
12522         if (in_combo || in_list || is_list) {
12523             //e.stopPropagation();
12524             return;
12525         }
12526         
12527         if(this.tickable){
12528             this.onTickableFooterButtonClick(e, false, false);
12529         }
12530
12531         this.collapse();
12532         
12533     },
12534
12535     /**
12536      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12537      */
12538     expand : function(){
12539        
12540         if(this.isExpanded() || !this.hasFocus){
12541             return;
12542         }
12543         
12544         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12545         this.list.setWidth(lw);
12546         
12547         
12548          Roo.log('expand');
12549         
12550         this.list.show();
12551         
12552         this.restrictHeight();
12553         
12554         if(this.tickable){
12555             
12556             this.tickItems = Roo.apply([], this.item);
12557             
12558             this.okBtn.show();
12559             this.cancelBtn.show();
12560             this.trigger.hide();
12561             
12562             if(this.editable){
12563                 this.tickableInputEl().focus();
12564             }
12565             
12566         }
12567         
12568         Roo.get(document).on('mousedown', this.collapseIf, this);
12569         Roo.get(document).on('mousewheel', this.collapseIf, this);
12570         if (!this.editable) {
12571             Roo.get(document).on('keydown', this.listKeyPress, this);
12572         }
12573         
12574         this.fireEvent('expand', this);
12575     },
12576
12577     // private
12578     // Implements the default empty TriggerField.onTriggerClick function
12579     onTriggerClick : function(e)
12580     {
12581         Roo.log('trigger click');
12582         
12583         if(this.disabled || !this.triggerList){
12584             return;
12585         }
12586         
12587         this.page = 0;
12588         this.loadNext = false;
12589         
12590         if(this.isExpanded()){
12591             this.collapse();
12592             if (!this.blockFocus) {
12593                 this.inputEl().focus();
12594             }
12595             
12596         }else {
12597             this.hasFocus = true;
12598             if(this.triggerAction == 'all') {
12599                 this.doQuery(this.allQuery, true);
12600             } else {
12601                 this.doQuery(this.getRawValue());
12602             }
12603             if (!this.blockFocus) {
12604                 this.inputEl().focus();
12605             }
12606         }
12607     },
12608     
12609     onTickableTriggerClick : function(e)
12610     {
12611         if(this.disabled){
12612             return;
12613         }
12614         
12615         this.page = 0;
12616         this.loadNext = false;
12617         this.hasFocus = true;
12618         
12619         if(this.triggerAction == 'all') {
12620             this.doQuery(this.allQuery, true);
12621         } else {
12622             this.doQuery(this.getRawValue());
12623         }
12624     },
12625     
12626     onSearchFieldClick : function(e)
12627     {
12628         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12629             this.onTickableFooterButtonClick(e, false, false);
12630             return;
12631         }
12632         
12633         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12634             return;
12635         }
12636         
12637         this.page = 0;
12638         this.loadNext = false;
12639         this.hasFocus = true;
12640         
12641         if(this.triggerAction == 'all') {
12642             this.doQuery(this.allQuery, true);
12643         } else {
12644             this.doQuery(this.getRawValue());
12645         }
12646     },
12647     
12648     listKeyPress : function(e)
12649     {
12650         //Roo.log('listkeypress');
12651         // scroll to first matching element based on key pres..
12652         if (e.isSpecialKey()) {
12653             return false;
12654         }
12655         var k = String.fromCharCode(e.getKey()).toUpperCase();
12656         //Roo.log(k);
12657         var match  = false;
12658         var csel = this.view.getSelectedNodes();
12659         var cselitem = false;
12660         if (csel.length) {
12661             var ix = this.view.indexOf(csel[0]);
12662             cselitem  = this.store.getAt(ix);
12663             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12664                 cselitem = false;
12665             }
12666             
12667         }
12668         
12669         this.store.each(function(v) { 
12670             if (cselitem) {
12671                 // start at existing selection.
12672                 if (cselitem.id == v.id) {
12673                     cselitem = false;
12674                 }
12675                 return true;
12676             }
12677                 
12678             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12679                 match = this.store.indexOf(v);
12680                 return false;
12681             }
12682             return true;
12683         }, this);
12684         
12685         if (match === false) {
12686             return true; // no more action?
12687         }
12688         // scroll to?
12689         this.view.select(match);
12690         var sn = Roo.get(this.view.getSelectedNodes()[0])
12691         sn.scrollIntoView(sn.dom.parentNode, false);
12692     },
12693     
12694     onViewScroll : function(e, t){
12695         
12696         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){
12697             return;
12698         }
12699         
12700         this.hasQuery = true;
12701         
12702         this.loading = this.list.select('.loading', true).first();
12703         
12704         if(this.loading === null){
12705             this.list.createChild({
12706                 tag: 'div',
12707                 cls: 'loading select2-more-results select2-active',
12708                 html: 'Loading more results...'
12709             })
12710             
12711             this.loading = this.list.select('.loading', true).first();
12712             
12713             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12714             
12715             this.loading.hide();
12716         }
12717         
12718         this.loading.show();
12719         
12720         var _combo = this;
12721         
12722         this.page++;
12723         this.loadNext = true;
12724         
12725         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12726         
12727         return;
12728     },
12729     
12730     addItem : function(o)
12731     {   
12732         var dv = ''; // display value
12733         
12734         if (this.displayField) {
12735             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12736         } else {
12737             // this is an error condition!!!
12738             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12739         }
12740         
12741         if(!dv.length){
12742             return;
12743         }
12744         
12745         var choice = this.choices.createChild({
12746             tag: 'li',
12747             cls: 'select2-search-choice',
12748             cn: [
12749                 {
12750                     tag: 'div',
12751                     html: dv
12752                 },
12753                 {
12754                     tag: 'a',
12755                     href: '#',
12756                     cls: 'select2-search-choice-close',
12757                     tabindex: '-1'
12758                 }
12759             ]
12760             
12761         }, this.searchField);
12762         
12763         var close = choice.select('a.select2-search-choice-close', true).first()
12764         
12765         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12766         
12767         this.item.push(o);
12768         
12769         this.lastData = o;
12770         
12771         this.syncValue();
12772         
12773         this.inputEl().dom.value = '';
12774         
12775         this.validate();
12776     },
12777     
12778     onRemoveItem : function(e, _self, o)
12779     {
12780         e.preventDefault();
12781         
12782         this.lastItem = Roo.apply([], this.item);
12783         
12784         var index = this.item.indexOf(o.data) * 1;
12785         
12786         if( index < 0){
12787             Roo.log('not this item?!');
12788             return;
12789         }
12790         
12791         this.item.splice(index, 1);
12792         o.item.remove();
12793         
12794         this.syncValue();
12795         
12796         this.fireEvent('remove', this, e);
12797         
12798         this.validate();
12799         
12800     },
12801     
12802     syncValue : function()
12803     {
12804         if(!this.item.length){
12805             this.clearValue();
12806             return;
12807         }
12808             
12809         var value = [];
12810         var _this = this;
12811         Roo.each(this.item, function(i){
12812             if(_this.valueField){
12813                 value.push(i[_this.valueField]);
12814                 return;
12815             }
12816
12817             value.push(i);
12818         });
12819
12820         this.value = value.join(',');
12821
12822         if(this.hiddenField){
12823             this.hiddenField.dom.value = this.value;
12824         }
12825         
12826         this.store.fireEvent("datachanged", this.store);
12827     },
12828     
12829     clearItem : function()
12830     {
12831         if(!this.multiple){
12832             return;
12833         }
12834         
12835         this.item = [];
12836         
12837         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12838            c.remove();
12839         });
12840         
12841         this.syncValue();
12842         
12843         this.validate();
12844     },
12845     
12846     inputEl: function ()
12847     {
12848         if(this.tickable){
12849             return this.searchField;
12850         }
12851         return this.el.select('input.form-control',true).first();
12852     },
12853     
12854     
12855     onTickableFooterButtonClick : function(e, btn, el)
12856     {
12857         e.preventDefault();
12858         
12859         this.lastItem = Roo.apply([], this.item);
12860         
12861         if(btn && btn.name == 'cancel'){
12862             this.tickItems = Roo.apply([], this.item);
12863             this.collapse();
12864             return;
12865         }
12866         
12867         this.clearItem();
12868         
12869         var _this = this;
12870         
12871         Roo.each(this.tickItems, function(o){
12872             _this.addItem(o);
12873         });
12874         
12875         this.collapse();
12876         
12877     },
12878     
12879     validate : function()
12880     {
12881         var v = this.getRawValue();
12882         
12883         if(this.multiple){
12884             v = this.getValue();
12885         }
12886         
12887         if(this.disabled || this.allowBlank || v.length){
12888             this.markValid();
12889             return true;
12890         }
12891         
12892         this.markInvalid();
12893         return false;
12894     },
12895     
12896     tickableInputEl : function()
12897     {
12898         if(!this.tickable || !this.editable){
12899             return this.inputEl();
12900         }
12901         
12902         return this.inputEl().select('.select2-search-field-input', true).first();
12903     }
12904     
12905     
12906
12907     /** 
12908     * @cfg {Boolean} grow 
12909     * @hide 
12910     */
12911     /** 
12912     * @cfg {Number} growMin 
12913     * @hide 
12914     */
12915     /** 
12916     * @cfg {Number} growMax 
12917     * @hide 
12918     */
12919     /**
12920      * @hide
12921      * @method autoSize
12922      */
12923 });
12924 /*
12925  * Based on:
12926  * Ext JS Library 1.1.1
12927  * Copyright(c) 2006-2007, Ext JS, LLC.
12928  *
12929  * Originally Released Under LGPL - original licence link has changed is not relivant.
12930  *
12931  * Fork - LGPL
12932  * <script type="text/javascript">
12933  */
12934
12935 /**
12936  * @class Roo.View
12937  * @extends Roo.util.Observable
12938  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12939  * This class also supports single and multi selection modes. <br>
12940  * Create a data model bound view:
12941  <pre><code>
12942  var store = new Roo.data.Store(...);
12943
12944  var view = new Roo.View({
12945     el : "my-element",
12946     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12947  
12948     singleSelect: true,
12949     selectedClass: "ydataview-selected",
12950     store: store
12951  });
12952
12953  // listen for node click?
12954  view.on("click", function(vw, index, node, e){
12955  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12956  });
12957
12958  // load XML data
12959  dataModel.load("foobar.xml");
12960  </code></pre>
12961  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12962  * <br><br>
12963  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12964  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12965  * 
12966  * Note: old style constructor is still suported (container, template, config)
12967  * 
12968  * @constructor
12969  * Create a new View
12970  * @param {Object} config The config object
12971  * 
12972  */
12973 Roo.View = function(config, depreciated_tpl, depreciated_config){
12974     
12975     this.parent = false;
12976     
12977     if (typeof(depreciated_tpl) == 'undefined') {
12978         // new way.. - universal constructor.
12979         Roo.apply(this, config);
12980         this.el  = Roo.get(this.el);
12981     } else {
12982         // old format..
12983         this.el  = Roo.get(config);
12984         this.tpl = depreciated_tpl;
12985         Roo.apply(this, depreciated_config);
12986     }
12987     this.wrapEl  = this.el.wrap().wrap();
12988     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12989     
12990     
12991     if(typeof(this.tpl) == "string"){
12992         this.tpl = new Roo.Template(this.tpl);
12993     } else {
12994         // support xtype ctors..
12995         this.tpl = new Roo.factory(this.tpl, Roo);
12996     }
12997     
12998     
12999     this.tpl.compile();
13000     
13001     /** @private */
13002     this.addEvents({
13003         /**
13004          * @event beforeclick
13005          * Fires before a click is processed. Returns false to cancel the default action.
13006          * @param {Roo.View} this
13007          * @param {Number} index The index of the target node
13008          * @param {HTMLElement} node The target node
13009          * @param {Roo.EventObject} e The raw event object
13010          */
13011             "beforeclick" : true,
13012         /**
13013          * @event click
13014          * Fires when a template node is clicked.
13015          * @param {Roo.View} this
13016          * @param {Number} index The index of the target node
13017          * @param {HTMLElement} node The target node
13018          * @param {Roo.EventObject} e The raw event object
13019          */
13020             "click" : true,
13021         /**
13022          * @event dblclick
13023          * Fires when a template node is double clicked.
13024          * @param {Roo.View} this
13025          * @param {Number} index The index of the target node
13026          * @param {HTMLElement} node The target node
13027          * @param {Roo.EventObject} e The raw event object
13028          */
13029             "dblclick" : true,
13030         /**
13031          * @event contextmenu
13032          * Fires when a template node is right clicked.
13033          * @param {Roo.View} this
13034          * @param {Number} index The index of the target node
13035          * @param {HTMLElement} node The target node
13036          * @param {Roo.EventObject} e The raw event object
13037          */
13038             "contextmenu" : true,
13039         /**
13040          * @event selectionchange
13041          * Fires when the selected nodes change.
13042          * @param {Roo.View} this
13043          * @param {Array} selections Array of the selected nodes
13044          */
13045             "selectionchange" : true,
13046     
13047         /**
13048          * @event beforeselect
13049          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13050          * @param {Roo.View} this
13051          * @param {HTMLElement} node The node to be selected
13052          * @param {Array} selections Array of currently selected nodes
13053          */
13054             "beforeselect" : true,
13055         /**
13056          * @event preparedata
13057          * Fires on every row to render, to allow you to change the data.
13058          * @param {Roo.View} this
13059          * @param {Object} data to be rendered (change this)
13060          */
13061           "preparedata" : true
13062           
13063           
13064         });
13065
13066
13067
13068     this.el.on({
13069         "click": this.onClick,
13070         "dblclick": this.onDblClick,
13071         "contextmenu": this.onContextMenu,
13072         scope:this
13073     });
13074
13075     this.selections = [];
13076     this.nodes = [];
13077     this.cmp = new Roo.CompositeElementLite([]);
13078     if(this.store){
13079         this.store = Roo.factory(this.store, Roo.data);
13080         this.setStore(this.store, true);
13081     }
13082     
13083     if ( this.footer && this.footer.xtype) {
13084            
13085          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13086         
13087         this.footer.dataSource = this.store
13088         this.footer.container = fctr;
13089         this.footer = Roo.factory(this.footer, Roo);
13090         fctr.insertFirst(this.el);
13091         
13092         // this is a bit insane - as the paging toolbar seems to detach the el..
13093 //        dom.parentNode.parentNode.parentNode
13094          // they get detached?
13095     }
13096     
13097     
13098     Roo.View.superclass.constructor.call(this);
13099     
13100     
13101 };
13102
13103 Roo.extend(Roo.View, Roo.util.Observable, {
13104     
13105      /**
13106      * @cfg {Roo.data.Store} store Data store to load data from.
13107      */
13108     store : false,
13109     
13110     /**
13111      * @cfg {String|Roo.Element} el The container element.
13112      */
13113     el : '',
13114     
13115     /**
13116      * @cfg {String|Roo.Template} tpl The template used by this View 
13117      */
13118     tpl : false,
13119     /**
13120      * @cfg {String} dataName the named area of the template to use as the data area
13121      *                          Works with domtemplates roo-name="name"
13122      */
13123     dataName: false,
13124     /**
13125      * @cfg {String} selectedClass The css class to add to selected nodes
13126      */
13127     selectedClass : "x-view-selected",
13128      /**
13129      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13130      */
13131     emptyText : "",
13132     
13133     /**
13134      * @cfg {String} text to display on mask (default Loading)
13135      */
13136     mask : false,
13137     /**
13138      * @cfg {Boolean} multiSelect Allow multiple selection
13139      */
13140     multiSelect : false,
13141     /**
13142      * @cfg {Boolean} singleSelect Allow single selection
13143      */
13144     singleSelect:  false,
13145     
13146     /**
13147      * @cfg {Boolean} toggleSelect - selecting 
13148      */
13149     toggleSelect : false,
13150     
13151     /**
13152      * @cfg {Boolean} tickable - selecting 
13153      */
13154     tickable : false,
13155     
13156     /**
13157      * Returns the element this view is bound to.
13158      * @return {Roo.Element}
13159      */
13160     getEl : function(){
13161         return this.wrapEl;
13162     },
13163     
13164     
13165
13166     /**
13167      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13168      */
13169     refresh : function(){
13170         //Roo.log('refresh');
13171         var t = this.tpl;
13172         
13173         // if we are using something like 'domtemplate', then
13174         // the what gets used is:
13175         // t.applySubtemplate(NAME, data, wrapping data..)
13176         // the outer template then get' applied with
13177         //     the store 'extra data'
13178         // and the body get's added to the
13179         //      roo-name="data" node?
13180         //      <span class='roo-tpl-{name}'></span> ?????
13181         
13182         
13183         
13184         this.clearSelections();
13185         this.el.update("");
13186         var html = [];
13187         var records = this.store.getRange();
13188         if(records.length < 1) {
13189             
13190             // is this valid??  = should it render a template??
13191             
13192             this.el.update(this.emptyText);
13193             return;
13194         }
13195         var el = this.el;
13196         if (this.dataName) {
13197             this.el.update(t.apply(this.store.meta)); //????
13198             el = this.el.child('.roo-tpl-' + this.dataName);
13199         }
13200         
13201         for(var i = 0, len = records.length; i < len; i++){
13202             var data = this.prepareData(records[i].data, i, records[i]);
13203             this.fireEvent("preparedata", this, data, i, records[i]);
13204             
13205             var d = Roo.apply({}, data);
13206             
13207             if(this.tickable){
13208                 Roo.apply(d, {'roo-id' : Roo.id()});
13209                 
13210                 var _this = this;
13211             
13212                 Roo.each(this.parent.item, function(item){
13213                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13214                         return;
13215                     }
13216                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13217                 });
13218             }
13219             
13220             html[html.length] = Roo.util.Format.trim(
13221                 this.dataName ?
13222                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13223                     t.apply(d)
13224             );
13225         }
13226         
13227         
13228         
13229         el.update(html.join(""));
13230         this.nodes = el.dom.childNodes;
13231         this.updateIndexes(0);
13232     },
13233     
13234
13235     /**
13236      * Function to override to reformat the data that is sent to
13237      * the template for each node.
13238      * DEPRICATED - use the preparedata event handler.
13239      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13240      * a JSON object for an UpdateManager bound view).
13241      */
13242     prepareData : function(data, index, record)
13243     {
13244         this.fireEvent("preparedata", this, data, index, record);
13245         return data;
13246     },
13247
13248     onUpdate : function(ds, record){
13249         // Roo.log('on update');   
13250         this.clearSelections();
13251         var index = this.store.indexOf(record);
13252         var n = this.nodes[index];
13253         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13254         n.parentNode.removeChild(n);
13255         this.updateIndexes(index, index);
13256     },
13257
13258     
13259     
13260 // --------- FIXME     
13261     onAdd : function(ds, records, index)
13262     {
13263         //Roo.log(['on Add', ds, records, index] );        
13264         this.clearSelections();
13265         if(this.nodes.length == 0){
13266             this.refresh();
13267             return;
13268         }
13269         var n = this.nodes[index];
13270         for(var i = 0, len = records.length; i < len; i++){
13271             var d = this.prepareData(records[i].data, i, records[i]);
13272             if(n){
13273                 this.tpl.insertBefore(n, d);
13274             }else{
13275                 
13276                 this.tpl.append(this.el, d);
13277             }
13278         }
13279         this.updateIndexes(index);
13280     },
13281
13282     onRemove : function(ds, record, index){
13283        // Roo.log('onRemove');
13284         this.clearSelections();
13285         var el = this.dataName  ?
13286             this.el.child('.roo-tpl-' + this.dataName) :
13287             this.el; 
13288         
13289         el.dom.removeChild(this.nodes[index]);
13290         this.updateIndexes(index);
13291     },
13292
13293     /**
13294      * Refresh an individual node.
13295      * @param {Number} index
13296      */
13297     refreshNode : function(index){
13298         this.onUpdate(this.store, this.store.getAt(index));
13299     },
13300
13301     updateIndexes : function(startIndex, endIndex){
13302         var ns = this.nodes;
13303         startIndex = startIndex || 0;
13304         endIndex = endIndex || ns.length - 1;
13305         for(var i = startIndex; i <= endIndex; i++){
13306             ns[i].nodeIndex = i;
13307         }
13308     },
13309
13310     /**
13311      * Changes the data store this view uses and refresh the view.
13312      * @param {Store} store
13313      */
13314     setStore : function(store, initial){
13315         if(!initial && this.store){
13316             this.store.un("datachanged", this.refresh);
13317             this.store.un("add", this.onAdd);
13318             this.store.un("remove", this.onRemove);
13319             this.store.un("update", this.onUpdate);
13320             this.store.un("clear", this.refresh);
13321             this.store.un("beforeload", this.onBeforeLoad);
13322             this.store.un("load", this.onLoad);
13323             this.store.un("loadexception", this.onLoad);
13324         }
13325         if(store){
13326           
13327             store.on("datachanged", this.refresh, this);
13328             store.on("add", this.onAdd, this);
13329             store.on("remove", this.onRemove, this);
13330             store.on("update", this.onUpdate, this);
13331             store.on("clear", this.refresh, this);
13332             store.on("beforeload", this.onBeforeLoad, this);
13333             store.on("load", this.onLoad, this);
13334             store.on("loadexception", this.onLoad, this);
13335         }
13336         
13337         if(store){
13338             this.refresh();
13339         }
13340     },
13341     /**
13342      * onbeforeLoad - masks the loading area.
13343      *
13344      */
13345     onBeforeLoad : function(store,opts)
13346     {
13347          //Roo.log('onBeforeLoad');   
13348         if (!opts.add) {
13349             this.el.update("");
13350         }
13351         this.el.mask(this.mask ? this.mask : "Loading" ); 
13352     },
13353     onLoad : function ()
13354     {
13355         this.el.unmask();
13356     },
13357     
13358
13359     /**
13360      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13361      * @param {HTMLElement} node
13362      * @return {HTMLElement} The template node
13363      */
13364     findItemFromChild : function(node){
13365         var el = this.dataName  ?
13366             this.el.child('.roo-tpl-' + this.dataName,true) :
13367             this.el.dom; 
13368         
13369         if(!node || node.parentNode == el){
13370                     return node;
13371             }
13372             var p = node.parentNode;
13373             while(p && p != el){
13374             if(p.parentNode == el){
13375                 return p;
13376             }
13377             p = p.parentNode;
13378         }
13379             return null;
13380     },
13381
13382     /** @ignore */
13383     onClick : function(e){
13384         var item = this.findItemFromChild(e.getTarget());
13385         if(item){
13386             var index = this.indexOf(item);
13387             if(this.onItemClick(item, index, e) !== false){
13388                 this.fireEvent("click", this, index, item, e);
13389             }
13390         }else{
13391             this.clearSelections();
13392         }
13393     },
13394
13395     /** @ignore */
13396     onContextMenu : function(e){
13397         var item = this.findItemFromChild(e.getTarget());
13398         if(item){
13399             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13400         }
13401     },
13402
13403     /** @ignore */
13404     onDblClick : function(e){
13405         var item = this.findItemFromChild(e.getTarget());
13406         if(item){
13407             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13408         }
13409     },
13410
13411     onItemClick : function(item, index, e)
13412     {
13413         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13414             return false;
13415         }
13416         if (this.toggleSelect) {
13417             var m = this.isSelected(item) ? 'unselect' : 'select';
13418             //Roo.log(m);
13419             var _t = this;
13420             _t[m](item, true, false);
13421             return true;
13422         }
13423         if(this.multiSelect || this.singleSelect){
13424             if(this.multiSelect && e.shiftKey && this.lastSelection){
13425                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13426             }else{
13427                 this.select(item, this.multiSelect && e.ctrlKey);
13428                 this.lastSelection = item;
13429             }
13430             
13431             if(!this.tickable){
13432                 e.preventDefault();
13433             }
13434             
13435         }
13436         return true;
13437     },
13438
13439     /**
13440      * Get the number of selected nodes.
13441      * @return {Number}
13442      */
13443     getSelectionCount : function(){
13444         return this.selections.length;
13445     },
13446
13447     /**
13448      * Get the currently selected nodes.
13449      * @return {Array} An array of HTMLElements
13450      */
13451     getSelectedNodes : function(){
13452         return this.selections;
13453     },
13454
13455     /**
13456      * Get the indexes of the selected nodes.
13457      * @return {Array}
13458      */
13459     getSelectedIndexes : function(){
13460         var indexes = [], s = this.selections;
13461         for(var i = 0, len = s.length; i < len; i++){
13462             indexes.push(s[i].nodeIndex);
13463         }
13464         return indexes;
13465     },
13466
13467     /**
13468      * Clear all selections
13469      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13470      */
13471     clearSelections : function(suppressEvent){
13472         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13473             this.cmp.elements = this.selections;
13474             this.cmp.removeClass(this.selectedClass);
13475             this.selections = [];
13476             if(!suppressEvent){
13477                 this.fireEvent("selectionchange", this, this.selections);
13478             }
13479         }
13480     },
13481
13482     /**
13483      * Returns true if the passed node is selected
13484      * @param {HTMLElement/Number} node The node or node index
13485      * @return {Boolean}
13486      */
13487     isSelected : function(node){
13488         var s = this.selections;
13489         if(s.length < 1){
13490             return false;
13491         }
13492         node = this.getNode(node);
13493         return s.indexOf(node) !== -1;
13494     },
13495
13496     /**
13497      * Selects nodes.
13498      * @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
13499      * @param {Boolean} keepExisting (optional) true to keep existing selections
13500      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13501      */
13502     select : function(nodeInfo, keepExisting, suppressEvent){
13503         if(nodeInfo instanceof Array){
13504             if(!keepExisting){
13505                 this.clearSelections(true);
13506             }
13507             for(var i = 0, len = nodeInfo.length; i < len; i++){
13508                 this.select(nodeInfo[i], true, true);
13509             }
13510             return;
13511         } 
13512         var node = this.getNode(nodeInfo);
13513         if(!node || this.isSelected(node)){
13514             return; // already selected.
13515         }
13516         if(!keepExisting){
13517             this.clearSelections(true);
13518         }
13519         
13520         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13521             Roo.fly(node).addClass(this.selectedClass);
13522             this.selections.push(node);
13523             if(!suppressEvent){
13524                 this.fireEvent("selectionchange", this, this.selections);
13525             }
13526         }
13527         
13528         
13529     },
13530       /**
13531      * Unselects nodes.
13532      * @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
13533      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13535      */
13536     unselect : function(nodeInfo, keepExisting, suppressEvent)
13537     {
13538         if(nodeInfo instanceof Array){
13539             Roo.each(this.selections, function(s) {
13540                 this.unselect(s, nodeInfo);
13541             }, this);
13542             return;
13543         }
13544         var node = this.getNode(nodeInfo);
13545         if(!node || !this.isSelected(node)){
13546             //Roo.log("not selected");
13547             return; // not selected.
13548         }
13549         // fireevent???
13550         var ns = [];
13551         Roo.each(this.selections, function(s) {
13552             if (s == node ) {
13553                 Roo.fly(node).removeClass(this.selectedClass);
13554
13555                 return;
13556             }
13557             ns.push(s);
13558         },this);
13559         
13560         this.selections= ns;
13561         this.fireEvent("selectionchange", this, this.selections);
13562     },
13563
13564     /**
13565      * Gets a template node.
13566      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13567      * @return {HTMLElement} The node or null if it wasn't found
13568      */
13569     getNode : function(nodeInfo){
13570         if(typeof nodeInfo == "string"){
13571             return document.getElementById(nodeInfo);
13572         }else if(typeof nodeInfo == "number"){
13573             return this.nodes[nodeInfo];
13574         }
13575         return nodeInfo;
13576     },
13577
13578     /**
13579      * Gets a range template nodes.
13580      * @param {Number} startIndex
13581      * @param {Number} endIndex
13582      * @return {Array} An array of nodes
13583      */
13584     getNodes : function(start, end){
13585         var ns = this.nodes;
13586         start = start || 0;
13587         end = typeof end == "undefined" ? ns.length - 1 : end;
13588         var nodes = [];
13589         if(start <= end){
13590             for(var i = start; i <= end; i++){
13591                 nodes.push(ns[i]);
13592             }
13593         } else{
13594             for(var i = start; i >= end; i--){
13595                 nodes.push(ns[i]);
13596             }
13597         }
13598         return nodes;
13599     },
13600
13601     /**
13602      * Finds the index of the passed node
13603      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13604      * @return {Number} The index of the node or -1
13605      */
13606     indexOf : function(node){
13607         node = this.getNode(node);
13608         if(typeof node.nodeIndex == "number"){
13609             return node.nodeIndex;
13610         }
13611         var ns = this.nodes;
13612         for(var i = 0, len = ns.length; i < len; i++){
13613             if(ns[i] == node){
13614                 return i;
13615             }
13616         }
13617         return -1;
13618     }
13619 });
13620 /*
13621  * - LGPL
13622  *
13623  * based on jquery fullcalendar
13624  * 
13625  */
13626
13627 Roo.bootstrap = Roo.bootstrap || {};
13628 /**
13629  * @class Roo.bootstrap.Calendar
13630  * @extends Roo.bootstrap.Component
13631  * Bootstrap Calendar class
13632  * @cfg {Boolean} loadMask (true|false) default false
13633  * @cfg {Object} header generate the user specific header of the calendar, default false
13634
13635  * @constructor
13636  * Create a new Container
13637  * @param {Object} config The config object
13638  */
13639
13640
13641
13642 Roo.bootstrap.Calendar = function(config){
13643     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13644      this.addEvents({
13645         /**
13646              * @event select
13647              * Fires when a date is selected
13648              * @param {DatePicker} this
13649              * @param {Date} date The selected date
13650              */
13651         'select': true,
13652         /**
13653              * @event monthchange
13654              * Fires when the displayed month changes 
13655              * @param {DatePicker} this
13656              * @param {Date} date The selected month
13657              */
13658         'monthchange': true,
13659         /**
13660              * @event evententer
13661              * Fires when mouse over an event
13662              * @param {Calendar} this
13663              * @param {event} Event
13664              */
13665         'evententer': true,
13666         /**
13667              * @event eventleave
13668              * Fires when the mouse leaves an
13669              * @param {Calendar} this
13670              * @param {event}
13671              */
13672         'eventleave': true,
13673         /**
13674              * @event eventclick
13675              * Fires when the mouse click an
13676              * @param {Calendar} this
13677              * @param {event}
13678              */
13679         'eventclick': true
13680         
13681     });
13682
13683 };
13684
13685 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13686     
13687      /**
13688      * @cfg {Number} startDay
13689      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13690      */
13691     startDay : 0,
13692     
13693     loadMask : false,
13694     
13695     header : false,
13696       
13697     getAutoCreate : function(){
13698         
13699         
13700         var fc_button = function(name, corner, style, content ) {
13701             return Roo.apply({},{
13702                 tag : 'span',
13703                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13704                          (corner.length ?
13705                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13706                             ''
13707                         ),
13708                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13709                 unselectable: 'on'
13710             });
13711         };
13712         
13713         var header = {};
13714         
13715         if(!this.header){
13716             header = {
13717                 tag : 'table',
13718                 cls : 'fc-header',
13719                 style : 'width:100%',
13720                 cn : [
13721                     {
13722                         tag: 'tr',
13723                         cn : [
13724                             {
13725                                 tag : 'td',
13726                                 cls : 'fc-header-left',
13727                                 cn : [
13728                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13729                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13730                                     { tag: 'span', cls: 'fc-header-space' },
13731                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13732
13733
13734                                 ]
13735                             },
13736
13737                             {
13738                                 tag : 'td',
13739                                 cls : 'fc-header-center',
13740                                 cn : [
13741                                     {
13742                                         tag: 'span',
13743                                         cls: 'fc-header-title',
13744                                         cn : {
13745                                             tag: 'H2',
13746                                             html : 'month / year'
13747                                         }
13748                                     }
13749
13750                                 ]
13751                             },
13752                             {
13753                                 tag : 'td',
13754                                 cls : 'fc-header-right',
13755                                 cn : [
13756                               /*      fc_button('month', 'left', '', 'month' ),
13757                                     fc_button('week', '', '', 'week' ),
13758                                     fc_button('day', 'right', '', 'day' )
13759                                 */    
13760
13761                                 ]
13762                             }
13763
13764                         ]
13765                     }
13766                 ]
13767             };
13768         }
13769         
13770         header = this.header;
13771         
13772        
13773         var cal_heads = function() {
13774             var ret = [];
13775             // fixme - handle this.
13776             
13777             for (var i =0; i < Date.dayNames.length; i++) {
13778                 var d = Date.dayNames[i];
13779                 ret.push({
13780                     tag: 'th',
13781                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13782                     html : d.substring(0,3)
13783                 });
13784                 
13785             }
13786             ret[0].cls += ' fc-first';
13787             ret[6].cls += ' fc-last';
13788             return ret;
13789         };
13790         var cal_cell = function(n) {
13791             return  {
13792                 tag: 'td',
13793                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13794                 cn : [
13795                     {
13796                         cn : [
13797                             {
13798                                 cls: 'fc-day-number',
13799                                 html: 'D'
13800                             },
13801                             {
13802                                 cls: 'fc-day-content',
13803                              
13804                                 cn : [
13805                                      {
13806                                         style: 'position: relative;' // height: 17px;
13807                                     }
13808                                 ]
13809                             }
13810                             
13811                             
13812                         ]
13813                     }
13814                 ]
13815                 
13816             }
13817         };
13818         var cal_rows = function() {
13819             
13820             var ret = [];
13821             for (var r = 0; r < 6; r++) {
13822                 var row= {
13823                     tag : 'tr',
13824                     cls : 'fc-week',
13825                     cn : []
13826                 };
13827                 
13828                 for (var i =0; i < Date.dayNames.length; i++) {
13829                     var d = Date.dayNames[i];
13830                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13831
13832                 }
13833                 row.cn[0].cls+=' fc-first';
13834                 row.cn[0].cn[0].style = 'min-height:90px';
13835                 row.cn[6].cls+=' fc-last';
13836                 ret.push(row);
13837                 
13838             }
13839             ret[0].cls += ' fc-first';
13840             ret[4].cls += ' fc-prev-last';
13841             ret[5].cls += ' fc-last';
13842             return ret;
13843             
13844         };
13845         
13846         var cal_table = {
13847             tag: 'table',
13848             cls: 'fc-border-separate',
13849             style : 'width:100%',
13850             cellspacing  : 0,
13851             cn : [
13852                 { 
13853                     tag: 'thead',
13854                     cn : [
13855                         { 
13856                             tag: 'tr',
13857                             cls : 'fc-first fc-last',
13858                             cn : cal_heads()
13859                         }
13860                     ]
13861                 },
13862                 { 
13863                     tag: 'tbody',
13864                     cn : cal_rows()
13865                 }
13866                   
13867             ]
13868         };
13869          
13870          var cfg = {
13871             cls : 'fc fc-ltr',
13872             cn : [
13873                 header,
13874                 {
13875                     cls : 'fc-content',
13876                     style : "position: relative;",
13877                     cn : [
13878                         {
13879                             cls : 'fc-view fc-view-month fc-grid',
13880                             style : 'position: relative',
13881                             unselectable : 'on',
13882                             cn : [
13883                                 {
13884                                     cls : 'fc-event-container',
13885                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13886                                 },
13887                                 cal_table
13888                             ]
13889                         }
13890                     ]
13891     
13892                 }
13893            ] 
13894             
13895         };
13896         
13897          
13898         
13899         return cfg;
13900     },
13901     
13902     
13903     initEvents : function()
13904     {
13905         if(!this.store){
13906             throw "can not find store for calendar";
13907         }
13908         
13909         var mark = {
13910             tag: "div",
13911             cls:"x-dlg-mask",
13912             style: "text-align:center",
13913             cn: [
13914                 {
13915                     tag: "div",
13916                     style: "background-color:white;width:50%;margin:250 auto",
13917                     cn: [
13918                         {
13919                             tag: "img",
13920                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13921                         },
13922                         {
13923                             tag: "span",
13924                             html: "Loading"
13925                         }
13926                         
13927                     ]
13928                 }
13929             ]
13930         }
13931         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13932         
13933         var size = this.el.select('.fc-content', true).first().getSize();
13934         this.maskEl.setSize(size.width, size.height);
13935         this.maskEl.enableDisplayMode("block");
13936         if(!this.loadMask){
13937             this.maskEl.hide();
13938         }
13939         
13940         this.store = Roo.factory(this.store, Roo.data);
13941         this.store.on('load', this.onLoad, this);
13942         this.store.on('beforeload', this.onBeforeLoad, this);
13943         
13944         this.resize();
13945         
13946         this.cells = this.el.select('.fc-day',true);
13947         //Roo.log(this.cells);
13948         this.textNodes = this.el.query('.fc-day-number');
13949         this.cells.addClassOnOver('fc-state-hover');
13950         
13951         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13952         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13953         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13954         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13955         
13956         this.on('monthchange', this.onMonthChange, this);
13957         
13958         this.update(new Date().clearTime());
13959     },
13960     
13961     resize : function() {
13962         var sz  = this.el.getSize();
13963         
13964         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13965         this.el.select('.fc-day-content div',true).setHeight(34);
13966     },
13967     
13968     
13969     // private
13970     showPrevMonth : function(e){
13971         this.update(this.activeDate.add("mo", -1));
13972     },
13973     showToday : function(e){
13974         this.update(new Date().clearTime());
13975     },
13976     // private
13977     showNextMonth : function(e){
13978         this.update(this.activeDate.add("mo", 1));
13979     },
13980
13981     // private
13982     showPrevYear : function(){
13983         this.update(this.activeDate.add("y", -1));
13984     },
13985
13986     // private
13987     showNextYear : function(){
13988         this.update(this.activeDate.add("y", 1));
13989     },
13990
13991     
13992    // private
13993     update : function(date)
13994     {
13995         var vd = this.activeDate;
13996         this.activeDate = date;
13997 //        if(vd && this.el){
13998 //            var t = date.getTime();
13999 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14000 //                Roo.log('using add remove');
14001 //                
14002 //                this.fireEvent('monthchange', this, date);
14003 //                
14004 //                this.cells.removeClass("fc-state-highlight");
14005 //                this.cells.each(function(c){
14006 //                   if(c.dateValue == t){
14007 //                       c.addClass("fc-state-highlight");
14008 //                       setTimeout(function(){
14009 //                            try{c.dom.firstChild.focus();}catch(e){}
14010 //                       }, 50);
14011 //                       return false;
14012 //                   }
14013 //                   return true;
14014 //                });
14015 //                return;
14016 //            }
14017 //        }
14018         
14019         var days = date.getDaysInMonth();
14020         
14021         var firstOfMonth = date.getFirstDateOfMonth();
14022         var startingPos = firstOfMonth.getDay()-this.startDay;
14023         
14024         if(startingPos < this.startDay){
14025             startingPos += 7;
14026         }
14027         
14028         var pm = date.add(Date.MONTH, -1);
14029         var prevStart = pm.getDaysInMonth()-startingPos;
14030 //        
14031         this.cells = this.el.select('.fc-day',true);
14032         this.textNodes = this.el.query('.fc-day-number');
14033         this.cells.addClassOnOver('fc-state-hover');
14034         
14035         var cells = this.cells.elements;
14036         var textEls = this.textNodes;
14037         
14038         Roo.each(cells, function(cell){
14039             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14040         });
14041         
14042         days += startingPos;
14043
14044         // convert everything to numbers so it's fast
14045         var day = 86400000;
14046         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14047         //Roo.log(d);
14048         //Roo.log(pm);
14049         //Roo.log(prevStart);
14050         
14051         var today = new Date().clearTime().getTime();
14052         var sel = date.clearTime().getTime();
14053         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14054         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14055         var ddMatch = this.disabledDatesRE;
14056         var ddText = this.disabledDatesText;
14057         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14058         var ddaysText = this.disabledDaysText;
14059         var format = this.format;
14060         
14061         var setCellClass = function(cal, cell){
14062             cell.row = 0;
14063             cell.events = [];
14064             cell.more = [];
14065             //Roo.log('set Cell Class');
14066             cell.title = "";
14067             var t = d.getTime();
14068             
14069             //Roo.log(d);
14070             
14071             cell.dateValue = t;
14072             if(t == today){
14073                 cell.className += " fc-today";
14074                 cell.className += " fc-state-highlight";
14075                 cell.title = cal.todayText;
14076             }
14077             if(t == sel){
14078                 // disable highlight in other month..
14079                 //cell.className += " fc-state-highlight";
14080                 
14081             }
14082             // disabling
14083             if(t < min) {
14084                 cell.className = " fc-state-disabled";
14085                 cell.title = cal.minText;
14086                 return;
14087             }
14088             if(t > max) {
14089                 cell.className = " fc-state-disabled";
14090                 cell.title = cal.maxText;
14091                 return;
14092             }
14093             if(ddays){
14094                 if(ddays.indexOf(d.getDay()) != -1){
14095                     cell.title = ddaysText;
14096                     cell.className = " fc-state-disabled";
14097                 }
14098             }
14099             if(ddMatch && format){
14100                 var fvalue = d.dateFormat(format);
14101                 if(ddMatch.test(fvalue)){
14102                     cell.title = ddText.replace("%0", fvalue);
14103                     cell.className = " fc-state-disabled";
14104                 }
14105             }
14106             
14107             if (!cell.initialClassName) {
14108                 cell.initialClassName = cell.dom.className;
14109             }
14110             
14111             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14112         };
14113
14114         var i = 0;
14115         
14116         for(; i < startingPos; i++) {
14117             textEls[i].innerHTML = (++prevStart);
14118             d.setDate(d.getDate()+1);
14119             
14120             cells[i].className = "fc-past fc-other-month";
14121             setCellClass(this, cells[i]);
14122         }
14123         
14124         var intDay = 0;
14125         
14126         for(; i < days; i++){
14127             intDay = i - startingPos + 1;
14128             textEls[i].innerHTML = (intDay);
14129             d.setDate(d.getDate()+1);
14130             
14131             cells[i].className = ''; // "x-date-active";
14132             setCellClass(this, cells[i]);
14133         }
14134         var extraDays = 0;
14135         
14136         for(; i < 42; i++) {
14137             textEls[i].innerHTML = (++extraDays);
14138             d.setDate(d.getDate()+1);
14139             
14140             cells[i].className = "fc-future fc-other-month";
14141             setCellClass(this, cells[i]);
14142         }
14143         
14144         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14145         
14146         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14147         
14148         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14149         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14150         
14151         if(totalRows != 6){
14152             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14153             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14154         }
14155         
14156         this.fireEvent('monthchange', this, date);
14157         
14158         
14159         /*
14160         if(!this.internalRender){
14161             var main = this.el.dom.firstChild;
14162             var w = main.offsetWidth;
14163             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14164             Roo.fly(main).setWidth(w);
14165             this.internalRender = true;
14166             // opera does not respect the auto grow header center column
14167             // then, after it gets a width opera refuses to recalculate
14168             // without a second pass
14169             if(Roo.isOpera && !this.secondPass){
14170                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14171                 this.secondPass = true;
14172                 this.update.defer(10, this, [date]);
14173             }
14174         }
14175         */
14176         
14177     },
14178     
14179     findCell : function(dt) {
14180         dt = dt.clearTime().getTime();
14181         var ret = false;
14182         this.cells.each(function(c){
14183             //Roo.log("check " +c.dateValue + '?=' + dt);
14184             if(c.dateValue == dt){
14185                 ret = c;
14186                 return false;
14187             }
14188             return true;
14189         });
14190         
14191         return ret;
14192     },
14193     
14194     findCells : function(ev) {
14195         var s = ev.start.clone().clearTime().getTime();
14196        // Roo.log(s);
14197         var e= ev.end.clone().clearTime().getTime();
14198        // Roo.log(e);
14199         var ret = [];
14200         this.cells.each(function(c){
14201              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14202             
14203             if(c.dateValue > e){
14204                 return ;
14205             }
14206             if(c.dateValue < s){
14207                 return ;
14208             }
14209             ret.push(c);
14210         });
14211         
14212         return ret;    
14213     },
14214     
14215 //    findBestRow: function(cells)
14216 //    {
14217 //        var ret = 0;
14218 //        
14219 //        for (var i =0 ; i < cells.length;i++) {
14220 //            ret  = Math.max(cells[i].rows || 0,ret);
14221 //        }
14222 //        return ret;
14223 //        
14224 //    },
14225     
14226     
14227     addItem : function(ev)
14228     {
14229         // look for vertical location slot in
14230         var cells = this.findCells(ev);
14231         
14232 //        ev.row = this.findBestRow(cells);
14233         
14234         // work out the location.
14235         
14236         var crow = false;
14237         var rows = [];
14238         for(var i =0; i < cells.length; i++) {
14239             
14240             cells[i].row = cells[0].row;
14241             
14242             if(i == 0){
14243                 cells[i].row = cells[i].row + 1;
14244             }
14245             
14246             if (!crow) {
14247                 crow = {
14248                     start : cells[i],
14249                     end :  cells[i]
14250                 };
14251                 continue;
14252             }
14253             if (crow.start.getY() == cells[i].getY()) {
14254                 // on same row.
14255                 crow.end = cells[i];
14256                 continue;
14257             }
14258             // different row.
14259             rows.push(crow);
14260             crow = {
14261                 start: cells[i],
14262                 end : cells[i]
14263             };
14264             
14265         }
14266         
14267         rows.push(crow);
14268         ev.els = [];
14269         ev.rows = rows;
14270         ev.cells = cells;
14271         
14272         cells[0].events.push(ev);
14273         
14274         this.calevents.push(ev);
14275     },
14276     
14277     clearEvents: function() {
14278         
14279         if(!this.calevents){
14280             return;
14281         }
14282         
14283         Roo.each(this.cells.elements, function(c){
14284             c.row = 0;
14285             c.events = [];
14286             c.more = [];
14287         });
14288         
14289         Roo.each(this.calevents, function(e) {
14290             Roo.each(e.els, function(el) {
14291                 el.un('mouseenter' ,this.onEventEnter, this);
14292                 el.un('mouseleave' ,this.onEventLeave, this);
14293                 el.remove();
14294             },this);
14295         },this);
14296         
14297         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14298             e.remove();
14299         });
14300         
14301     },
14302     
14303     renderEvents: function()
14304     {   
14305         var _this = this;
14306         
14307         this.cells.each(function(c) {
14308             
14309             if(c.row < 5){
14310                 return;
14311             }
14312             
14313             var ev = c.events;
14314             
14315             var r = 4;
14316             if(c.row != c.events.length){
14317                 r = 4 - (4 - (c.row - c.events.length));
14318             }
14319             
14320             c.events = ev.slice(0, r);
14321             c.more = ev.slice(r);
14322             
14323             if(c.more.length && c.more.length == 1){
14324                 c.events.push(c.more.pop());
14325             }
14326             
14327             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14328             
14329         });
14330             
14331         this.cells.each(function(c) {
14332             
14333             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14334             
14335             
14336             for (var e = 0; e < c.events.length; e++){
14337                 var ev = c.events[e];
14338                 var rows = ev.rows;
14339                 
14340                 for(var i = 0; i < rows.length; i++) {
14341                 
14342                     // how many rows should it span..
14343
14344                     var  cfg = {
14345                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14346                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14347
14348                         unselectable : "on",
14349                         cn : [
14350                             {
14351                                 cls: 'fc-event-inner',
14352                                 cn : [
14353     //                                {
14354     //                                  tag:'span',
14355     //                                  cls: 'fc-event-time',
14356     //                                  html : cells.length > 1 ? '' : ev.time
14357     //                                },
14358                                     {
14359                                       tag:'span',
14360                                       cls: 'fc-event-title',
14361                                       html : String.format('{0}', ev.title)
14362                                     }
14363
14364
14365                                 ]
14366                             },
14367                             {
14368                                 cls: 'ui-resizable-handle ui-resizable-e',
14369                                 html : '&nbsp;&nbsp;&nbsp'
14370                             }
14371
14372                         ]
14373                     };
14374
14375                     if (i == 0) {
14376                         cfg.cls += ' fc-event-start';
14377                     }
14378                     if ((i+1) == rows.length) {
14379                         cfg.cls += ' fc-event-end';
14380                     }
14381
14382                     var ctr = _this.el.select('.fc-event-container',true).first();
14383                     var cg = ctr.createChild(cfg);
14384
14385                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14386                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14387
14388                     var r = (c.more.length) ? 1 : 0;
14389                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14390                     cg.setWidth(ebox.right - sbox.x -2);
14391
14392                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14393                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14394                     cg.on('click', _this.onEventClick, _this, ev);
14395
14396                     ev.els.push(cg);
14397                     
14398                 }
14399                 
14400             }
14401             
14402             
14403             if(c.more.length){
14404                 var  cfg = {
14405                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14406                     style : 'position: absolute',
14407                     unselectable : "on",
14408                     cn : [
14409                         {
14410                             cls: 'fc-event-inner',
14411                             cn : [
14412                                 {
14413                                   tag:'span',
14414                                   cls: 'fc-event-title',
14415                                   html : 'More'
14416                                 }
14417
14418
14419                             ]
14420                         },
14421                         {
14422                             cls: 'ui-resizable-handle ui-resizable-e',
14423                             html : '&nbsp;&nbsp;&nbsp'
14424                         }
14425
14426                     ]
14427                 };
14428
14429                 var ctr = _this.el.select('.fc-event-container',true).first();
14430                 var cg = ctr.createChild(cfg);
14431
14432                 var sbox = c.select('.fc-day-content',true).first().getBox();
14433                 var ebox = c.select('.fc-day-content',true).first().getBox();
14434                 //Roo.log(cg);
14435                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14436                 cg.setWidth(ebox.right - sbox.x -2);
14437
14438                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14439                 
14440             }
14441             
14442         });
14443         
14444         
14445         
14446     },
14447     
14448     onEventEnter: function (e, el,event,d) {
14449         this.fireEvent('evententer', this, el, event);
14450     },
14451     
14452     onEventLeave: function (e, el,event,d) {
14453         this.fireEvent('eventleave', this, el, event);
14454     },
14455     
14456     onEventClick: function (e, el,event,d) {
14457         this.fireEvent('eventclick', this, el, event);
14458     },
14459     
14460     onMonthChange: function () {
14461         this.store.load();
14462     },
14463     
14464     onMoreEventClick: function(e, el, more)
14465     {
14466         var _this = this;
14467         
14468         this.calpopover.placement = 'right';
14469         this.calpopover.setTitle('More');
14470         
14471         this.calpopover.setContent('');
14472         
14473         var ctr = this.calpopover.el.select('.popover-content', true).first();
14474         
14475         Roo.each(more, function(m){
14476             var cfg = {
14477                 cls : 'fc-event-hori fc-event-draggable',
14478                 html : m.title
14479             }
14480             var cg = ctr.createChild(cfg);
14481             
14482             cg.on('click', _this.onEventClick, _this, m);
14483         });
14484         
14485         this.calpopover.show(el);
14486         
14487         
14488     },
14489     
14490     onLoad: function () 
14491     {   
14492         this.calevents = [];
14493         var cal = this;
14494         
14495         if(this.store.getCount() > 0){
14496             this.store.data.each(function(d){
14497                cal.addItem({
14498                     id : d.data.id,
14499                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14500                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14501                     time : d.data.start_time,
14502                     title : d.data.title,
14503                     description : d.data.description,
14504                     venue : d.data.venue
14505                 });
14506             });
14507         }
14508         
14509         this.renderEvents();
14510         
14511         if(this.calevents.length && this.loadMask){
14512             this.maskEl.hide();
14513         }
14514     },
14515     
14516     onBeforeLoad: function()
14517     {
14518         this.clearEvents();
14519         if(this.loadMask){
14520             this.maskEl.show();
14521         }
14522     }
14523 });
14524
14525  
14526  /*
14527  * - LGPL
14528  *
14529  * element
14530  * 
14531  */
14532
14533 /**
14534  * @class Roo.bootstrap.Popover
14535  * @extends Roo.bootstrap.Component
14536  * Bootstrap Popover class
14537  * @cfg {String} html contents of the popover   (or false to use children..)
14538  * @cfg {String} title of popover (or false to hide)
14539  * @cfg {String} placement how it is placed
14540  * @cfg {String} trigger click || hover (or false to trigger manually)
14541  * @cfg {String} over what (parent or false to trigger manually.)
14542  * @cfg {Number} delay - delay before showing
14543  
14544  * @constructor
14545  * Create a new Popover
14546  * @param {Object} config The config object
14547  */
14548
14549 Roo.bootstrap.Popover = function(config){
14550     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14551 };
14552
14553 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14554     
14555     title: 'Fill in a title',
14556     html: false,
14557     
14558     placement : 'right',
14559     trigger : 'hover', // hover
14560     
14561     delay : 0,
14562     
14563     over: 'parent',
14564     
14565     can_build_overlaid : false,
14566     
14567     getChildContainer : function()
14568     {
14569         return this.el.select('.popover-content',true).first();
14570     },
14571     
14572     getAutoCreate : function(){
14573          Roo.log('make popover?');
14574         var cfg = {
14575            cls : 'popover roo-dynamic',
14576            style: 'display:block',
14577            cn : [
14578                 {
14579                     cls : 'arrow'
14580                 },
14581                 {
14582                     cls : 'popover-inner',
14583                     cn : [
14584                         {
14585                             tag: 'h3',
14586                             cls: 'popover-title',
14587                             html : this.title
14588                         },
14589                         {
14590                             cls : 'popover-content',
14591                             html : this.html
14592                         }
14593                     ]
14594                     
14595                 }
14596            ]
14597         };
14598         
14599         return cfg;
14600     },
14601     setTitle: function(str)
14602     {
14603         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14604     },
14605     setContent: function(str)
14606     {
14607         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14608     },
14609     // as it get's added to the bottom of the page.
14610     onRender : function(ct, position)
14611     {
14612         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14613         if(!this.el){
14614             var cfg = Roo.apply({},  this.getAutoCreate());
14615             cfg.id = Roo.id();
14616             
14617             if (this.cls) {
14618                 cfg.cls += ' ' + this.cls;
14619             }
14620             if (this.style) {
14621                 cfg.style = this.style;
14622             }
14623             Roo.log("adding to ")
14624             this.el = Roo.get(document.body).createChild(cfg, position);
14625             Roo.log(this.el);
14626         }
14627         this.initEvents();
14628     },
14629     
14630     initEvents : function()
14631     {
14632         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14633         this.el.enableDisplayMode('block');
14634         this.el.hide();
14635         if (this.over === false) {
14636             return; 
14637         }
14638         if (this.triggers === false) {
14639             return;
14640         }
14641         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14642         var triggers = this.trigger ? this.trigger.split(' ') : [];
14643         Roo.each(triggers, function(trigger) {
14644         
14645             if (trigger == 'click') {
14646                 on_el.on('click', this.toggle, this);
14647             } else if (trigger != 'manual') {
14648                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14649                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14650       
14651                 on_el.on(eventIn  ,this.enter, this);
14652                 on_el.on(eventOut, this.leave, this);
14653             }
14654         }, this);
14655         
14656     },
14657     
14658     
14659     // private
14660     timeout : null,
14661     hoverState : null,
14662     
14663     toggle : function () {
14664         this.hoverState == 'in' ? this.leave() : this.enter();
14665     },
14666     
14667     enter : function () {
14668        
14669     
14670         clearTimeout(this.timeout);
14671     
14672         this.hoverState = 'in';
14673     
14674         if (!this.delay || !this.delay.show) {
14675             this.show();
14676             return;
14677         }
14678         var _t = this;
14679         this.timeout = setTimeout(function () {
14680             if (_t.hoverState == 'in') {
14681                 _t.show();
14682             }
14683         }, this.delay.show)
14684     },
14685     leave : function() {
14686         clearTimeout(this.timeout);
14687     
14688         this.hoverState = 'out';
14689     
14690         if (!this.delay || !this.delay.hide) {
14691             this.hide();
14692             return;
14693         }
14694         var _t = this;
14695         this.timeout = setTimeout(function () {
14696             if (_t.hoverState == 'out') {
14697                 _t.hide();
14698             }
14699         }, this.delay.hide)
14700     },
14701     
14702     show : function (on_el)
14703     {
14704         if (!on_el) {
14705             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14706         }
14707         // set content.
14708         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14709         if (this.html !== false) {
14710             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14711         }
14712         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14713         if (!this.title.length) {
14714             this.el.select('.popover-title',true).hide();
14715         }
14716         
14717         var placement = typeof this.placement == 'function' ?
14718             this.placement.call(this, this.el, on_el) :
14719             this.placement;
14720             
14721         var autoToken = /\s?auto?\s?/i;
14722         var autoPlace = autoToken.test(placement);
14723         if (autoPlace) {
14724             placement = placement.replace(autoToken, '') || 'top';
14725         }
14726         
14727         //this.el.detach()
14728         //this.el.setXY([0,0]);
14729         this.el.show();
14730         this.el.dom.style.display='block';
14731         this.el.addClass(placement);
14732         
14733         //this.el.appendTo(on_el);
14734         
14735         var p = this.getPosition();
14736         var box = this.el.getBox();
14737         
14738         if (autoPlace) {
14739             // fixme..
14740         }
14741         var align = Roo.bootstrap.Popover.alignment[placement];
14742         this.el.alignTo(on_el, align[0],align[1]);
14743         //var arrow = this.el.select('.arrow',true).first();
14744         //arrow.set(align[2], 
14745         
14746         this.el.addClass('in');
14747         this.hoverState = null;
14748         
14749         if (this.el.hasClass('fade')) {
14750             // fade it?
14751         }
14752         
14753     },
14754     hide : function()
14755     {
14756         this.el.setXY([0,0]);
14757         this.el.removeClass('in');
14758         this.el.hide();
14759         
14760     }
14761     
14762 });
14763
14764 Roo.bootstrap.Popover.alignment = {
14765     'left' : ['r-l', [-10,0], 'right'],
14766     'right' : ['l-r', [10,0], 'left'],
14767     'bottom' : ['t-b', [0,10], 'top'],
14768     'top' : [ 'b-t', [0,-10], 'bottom']
14769 };
14770
14771  /*
14772  * - LGPL
14773  *
14774  * Progress
14775  * 
14776  */
14777
14778 /**
14779  * @class Roo.bootstrap.Progress
14780  * @extends Roo.bootstrap.Component
14781  * Bootstrap Progress class
14782  * @cfg {Boolean} striped striped of the progress bar
14783  * @cfg {Boolean} active animated of the progress bar
14784  * 
14785  * 
14786  * @constructor
14787  * Create a new Progress
14788  * @param {Object} config The config object
14789  */
14790
14791 Roo.bootstrap.Progress = function(config){
14792     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14793 };
14794
14795 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14796     
14797     striped : false,
14798     active: false,
14799     
14800     getAutoCreate : function(){
14801         var cfg = {
14802             tag: 'div',
14803             cls: 'progress'
14804         };
14805         
14806         
14807         if(this.striped){
14808             cfg.cls += ' progress-striped';
14809         }
14810       
14811         if(this.active){
14812             cfg.cls += ' active';
14813         }
14814         
14815         
14816         return cfg;
14817     }
14818    
14819 });
14820
14821  
14822
14823  /*
14824  * - LGPL
14825  *
14826  * ProgressBar
14827  * 
14828  */
14829
14830 /**
14831  * @class Roo.bootstrap.ProgressBar
14832  * @extends Roo.bootstrap.Component
14833  * Bootstrap ProgressBar class
14834  * @cfg {Number} aria_valuenow aria-value now
14835  * @cfg {Number} aria_valuemin aria-value min
14836  * @cfg {Number} aria_valuemax aria-value max
14837  * @cfg {String} label label for the progress bar
14838  * @cfg {String} panel (success | info | warning | danger )
14839  * @cfg {String} role role of the progress bar
14840  * @cfg {String} sr_only text
14841  * 
14842  * 
14843  * @constructor
14844  * Create a new ProgressBar
14845  * @param {Object} config The config object
14846  */
14847
14848 Roo.bootstrap.ProgressBar = function(config){
14849     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14850 };
14851
14852 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14853     
14854     aria_valuenow : 0,
14855     aria_valuemin : 0,
14856     aria_valuemax : 100,
14857     label : false,
14858     panel : false,
14859     role : false,
14860     sr_only: false,
14861     
14862     getAutoCreate : function()
14863     {
14864         
14865         var cfg = {
14866             tag: 'div',
14867             cls: 'progress-bar',
14868             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14869         };
14870         
14871         if(this.sr_only){
14872             cfg.cn = {
14873                 tag: 'span',
14874                 cls: 'sr-only',
14875                 html: this.sr_only
14876             }
14877         }
14878         
14879         if(this.role){
14880             cfg.role = this.role;
14881         }
14882         
14883         if(this.aria_valuenow){
14884             cfg['aria-valuenow'] = this.aria_valuenow;
14885         }
14886         
14887         if(this.aria_valuemin){
14888             cfg['aria-valuemin'] = this.aria_valuemin;
14889         }
14890         
14891         if(this.aria_valuemax){
14892             cfg['aria-valuemax'] = this.aria_valuemax;
14893         }
14894         
14895         if(this.label && !this.sr_only){
14896             cfg.html = this.label;
14897         }
14898         
14899         if(this.panel){
14900             cfg.cls += ' progress-bar-' + this.panel;
14901         }
14902         
14903         return cfg;
14904     },
14905     
14906     update : function(aria_valuenow)
14907     {
14908         this.aria_valuenow = aria_valuenow;
14909         
14910         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14911     }
14912    
14913 });
14914
14915  
14916
14917  /*
14918  * - LGPL
14919  *
14920  * column
14921  * 
14922  */
14923
14924 /**
14925  * @class Roo.bootstrap.TabGroup
14926  * @extends Roo.bootstrap.Column
14927  * Bootstrap Column class
14928  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14929  * @cfg {Boolean} carousel true to make the group behave like a carousel
14930  * @cfg {Number} bullets show the panel pointer.. default 0
14931  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14932  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14933  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14934  * 
14935  * @constructor
14936  * Create a new TabGroup
14937  * @param {Object} config The config object
14938  */
14939
14940 Roo.bootstrap.TabGroup = function(config){
14941     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14942     if (!this.navId) {
14943         this.navId = Roo.id();
14944     }
14945     this.tabs = [];
14946     Roo.bootstrap.TabGroup.register(this);
14947     
14948 };
14949
14950 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14951     
14952     carousel : false,
14953     transition : false,
14954     bullets : 0,
14955     timer : 0,
14956     autoslide : false,
14957     slideFn : false,
14958     slideOnTouch : false,
14959     
14960     getAutoCreate : function()
14961     {
14962         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14963         
14964         cfg.cls += ' tab-content';
14965         
14966         Roo.log('get auto create...............');
14967         
14968         if (this.carousel) {
14969             cfg.cls += ' carousel slide';
14970             
14971             cfg.cn = [{
14972                cls : 'carousel-inner'
14973             }];
14974         
14975             if(this.bullets > 0 && !Roo.isTouch){
14976                 
14977                 var bullets = {
14978                     cls : 'carousel-bullets',
14979                     cn : []
14980                 };
14981                 
14982                 if(this.bullets_cls){
14983                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14984                 }
14985                 
14986                 for (var i = 0; i < this.bullets; i++){
14987                     bullets.cn.push({
14988                         cls : 'bullet bullet-' + i
14989                     });
14990                 }
14991                 
14992                 bullets.cn.push({
14993                     cls : 'clear'
14994                 });
14995                 
14996                 cfg.cn[0].cn = bullets;
14997             }
14998         }
14999         
15000         return cfg;
15001     },
15002     
15003     initEvents:  function()
15004     {
15005         Roo.log('-------- init events on tab group ---------');
15006         
15007         if(this.bullets > 0 && !Roo.isTouch){
15008             this.initBullet();
15009         }
15010         
15011         Roo.log(this);
15012         
15013         if(Roo.isTouch && this.slideOnTouch){
15014             this.el.on("touchstart", this.onTouchStart, this);
15015         }
15016         
15017         if(this.autoslide){
15018             var _this = this;
15019             
15020             this.slideFn = window.setInterval(function() {
15021                 _this.showPanelNext();
15022             }, this.timer);
15023         }
15024         
15025     },
15026     
15027     onTouchStart : function(e, el, o)
15028     {
15029         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15030             return;
15031         }
15032         
15033         this.showPanelNext();
15034     },
15035     
15036     getChildContainer : function()
15037     {
15038         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15039     },
15040     
15041     /**
15042     * register a Navigation item
15043     * @param {Roo.bootstrap.NavItem} the navitem to add
15044     */
15045     register : function(item)
15046     {
15047         this.tabs.push( item);
15048         item.navId = this.navId; // not really needed..
15049     
15050     },
15051     
15052     getActivePanel : function()
15053     {
15054         var r = false;
15055         Roo.each(this.tabs, function(t) {
15056             if (t.active) {
15057                 r = t;
15058                 return false;
15059             }
15060             return null;
15061         });
15062         return r;
15063         
15064     },
15065     getPanelByName : function(n)
15066     {
15067         var r = false;
15068         Roo.each(this.tabs, function(t) {
15069             if (t.tabId == n) {
15070                 r = t;
15071                 return false;
15072             }
15073             return null;
15074         });
15075         return r;
15076     },
15077     indexOfPanel : function(p)
15078     {
15079         var r = false;
15080         Roo.each(this.tabs, function(t,i) {
15081             if (t.tabId == p.tabId) {
15082                 r = i;
15083                 return false;
15084             }
15085             return null;
15086         });
15087         return r;
15088     },
15089     /**
15090      * show a specific panel
15091      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15092      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15093      */
15094     showPanel : function (pan)
15095     {
15096         if(this.transition){
15097             Roo.log("waiting for the transitionend");
15098             return;
15099         }
15100         
15101         if (typeof(pan) == 'number') {
15102             pan = this.tabs[pan];
15103         }
15104         if (typeof(pan) == 'string') {
15105             pan = this.getPanelByName(pan);
15106         }
15107         if (pan.tabId == this.getActivePanel().tabId) {
15108             return true;
15109         }
15110         var cur = this.getActivePanel();
15111         
15112         if (false === cur.fireEvent('beforedeactivate')) {
15113             return false;
15114         }
15115         
15116         if(this.bullets > 0 && !Roo.isTouch){
15117             this.setActiveBullet(this.indexOfPanel(pan));
15118         }
15119         
15120         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15121             
15122             this.transition = true;
15123             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15124             var lr = dir == 'next' ? 'left' : 'right';
15125             pan.el.addClass(dir); // or prev
15126             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15127             cur.el.addClass(lr); // or right
15128             pan.el.addClass(lr);
15129             
15130             var _this = this;
15131             cur.el.on('transitionend', function() {
15132                 Roo.log("trans end?");
15133                 
15134                 pan.el.removeClass([lr,dir]);
15135                 pan.setActive(true);
15136                 
15137                 cur.el.removeClass([lr]);
15138                 cur.setActive(false);
15139                 
15140                 _this.transition = false;
15141                 
15142             }, this, { single:  true } );
15143             
15144             return true;
15145         }
15146         
15147         cur.setActive(false);
15148         pan.setActive(true);
15149         
15150         return true;
15151         
15152     },
15153     showPanelNext : function()
15154     {
15155         var i = this.indexOfPanel(this.getActivePanel());
15156         
15157         if (i >= this.tabs.length - 1 && !this.autoslide) {
15158             return;
15159         }
15160         
15161         if (i >= this.tabs.length - 1 && this.autoslide) {
15162             i = -1;
15163         }
15164         
15165         this.showPanel(this.tabs[i+1]);
15166     },
15167     
15168     showPanelPrev : function()
15169     {
15170         var i = this.indexOfPanel(this.getActivePanel());
15171         
15172         if (i  < 1 && !this.autoslide) {
15173             return;
15174         }
15175         
15176         if (i < 1 && this.autoslide) {
15177             i = this.tabs.length;
15178         }
15179         
15180         this.showPanel(this.tabs[i-1]);
15181     },
15182     
15183     initBullet : function()
15184     {
15185         if(Roo.isTouch){
15186             return;
15187         }
15188         
15189         var _this = this;
15190         
15191         for (var i = 0; i < this.bullets; i++){
15192             var bullet = this.el.select('.bullet-' + i, true).first();
15193
15194             if(!bullet){
15195                 continue;
15196             }
15197
15198             bullet.on('click', (function(e, el, o, ii, t){
15199
15200                 e.preventDefault();
15201
15202                 _this.showPanel(ii);
15203
15204                 if(_this.autoslide && _this.slideFn){
15205                     clearInterval(_this.slideFn);
15206                     _this.slideFn = window.setInterval(function() {
15207                         _this.showPanelNext();
15208                     }, _this.timer);
15209                 }
15210
15211             }).createDelegate(this, [i, bullet], true));
15212         }
15213     },
15214     
15215     setActiveBullet : function(i)
15216     {
15217         if(Roo.isTouch){
15218             return;
15219         }
15220         
15221         Roo.each(this.el.select('.bullet', true).elements, function(el){
15222             el.removeClass('selected');
15223         });
15224
15225         var bullet = this.el.select('.bullet-' + i, true).first();
15226         
15227         if(!bullet){
15228             return;
15229         }
15230         
15231         bullet.addClass('selected');
15232     }
15233     
15234     
15235   
15236 });
15237
15238  
15239
15240  
15241  
15242 Roo.apply(Roo.bootstrap.TabGroup, {
15243     
15244     groups: {},
15245      /**
15246     * register a Navigation Group
15247     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15248     */
15249     register : function(navgrp)
15250     {
15251         this.groups[navgrp.navId] = navgrp;
15252         
15253     },
15254     /**
15255     * fetch a Navigation Group based on the navigation ID
15256     * if one does not exist , it will get created.
15257     * @param {string} the navgroup to add
15258     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15259     */
15260     get: function(navId) {
15261         if (typeof(this.groups[navId]) == 'undefined') {
15262             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15263         }
15264         return this.groups[navId] ;
15265     }
15266     
15267     
15268     
15269 });
15270
15271  /*
15272  * - LGPL
15273  *
15274  * TabPanel
15275  * 
15276  */
15277
15278 /**
15279  * @class Roo.bootstrap.TabPanel
15280  * @extends Roo.bootstrap.Component
15281  * Bootstrap TabPanel class
15282  * @cfg {Boolean} active panel active
15283  * @cfg {String} html panel content
15284  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15285  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15286  * 
15287  * 
15288  * @constructor
15289  * Create a new TabPanel
15290  * @param {Object} config The config object
15291  */
15292
15293 Roo.bootstrap.TabPanel = function(config){
15294     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15295     this.addEvents({
15296         /**
15297              * @event changed
15298              * Fires when the active status changes
15299              * @param {Roo.bootstrap.TabPanel} this
15300              * @param {Boolean} state the new state
15301             
15302          */
15303         'changed': true,
15304         /**
15305              * @event beforedeactivate
15306              * Fires before a tab is de-activated - can be used to do validation on a form.
15307              * @param {Roo.bootstrap.TabPanel} this
15308              * @return {Boolean} false if there is an error
15309             
15310          */
15311         'beforedeactivate': true
15312      });
15313     
15314     this.tabId = this.tabId || Roo.id();
15315   
15316 };
15317
15318 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15319     
15320     active: false,
15321     html: false,
15322     tabId: false,
15323     navId : false,
15324     
15325     getAutoCreate : function(){
15326         var cfg = {
15327             tag: 'div',
15328             // item is needed for carousel - not sure if it has any effect otherwise
15329             cls: 'tab-pane item',
15330             html: this.html || ''
15331         };
15332         
15333         if(this.active){
15334             cfg.cls += ' active';
15335         }
15336         
15337         if(this.tabId){
15338             cfg.tabId = this.tabId;
15339         }
15340         
15341         
15342         return cfg;
15343     },
15344     
15345     initEvents:  function()
15346     {
15347         Roo.log('-------- init events on tab panel ---------');
15348         
15349         var p = this.parent();
15350         this.navId = this.navId || p.navId;
15351         
15352         if (typeof(this.navId) != 'undefined') {
15353             // not really needed.. but just in case.. parent should be a NavGroup.
15354             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15355             Roo.log(['register', tg, this]);
15356             tg.register(this);
15357             
15358             var i = tg.tabs.length - 1;
15359             
15360             if(this.active && tg.bullets > 0 && i < tg.bullets){
15361                 tg.setActiveBullet(i);
15362             }
15363         }
15364         
15365     },
15366     
15367     
15368     onRender : function(ct, position)
15369     {
15370        // Roo.log("Call onRender: " + this.xtype);
15371         
15372         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15373         
15374         
15375         
15376         
15377         
15378     },
15379     
15380     setActive: function(state)
15381     {
15382         Roo.log("panel - set active " + this.tabId + "=" + state);
15383         
15384         this.active = state;
15385         if (!state) {
15386             this.el.removeClass('active');
15387             
15388         } else  if (!this.el.hasClass('active')) {
15389             this.el.addClass('active');
15390         }
15391         
15392         this.fireEvent('changed', this, state);
15393     }
15394     
15395     
15396 });
15397  
15398
15399  
15400
15401  /*
15402  * - LGPL
15403  *
15404  * DateField
15405  * 
15406  */
15407
15408 /**
15409  * @class Roo.bootstrap.DateField
15410  * @extends Roo.bootstrap.Input
15411  * Bootstrap DateField class
15412  * @cfg {Number} weekStart default 0
15413  * @cfg {String} viewMode default empty, (months|years)
15414  * @cfg {String} minViewMode default empty, (months|years)
15415  * @cfg {Number} startDate default -Infinity
15416  * @cfg {Number} endDate default Infinity
15417  * @cfg {Boolean} todayHighlight default false
15418  * @cfg {Boolean} todayBtn default false
15419  * @cfg {Boolean} calendarWeeks default false
15420  * @cfg {Object} daysOfWeekDisabled default empty
15421  * @cfg {Boolean} singleMode default false (true | false)
15422  * 
15423  * @cfg {Boolean} keyboardNavigation default true
15424  * @cfg {String} language default en
15425  * 
15426  * @constructor
15427  * Create a new DateField
15428  * @param {Object} config The config object
15429  */
15430
15431 Roo.bootstrap.DateField = function(config){
15432     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15433      this.addEvents({
15434             /**
15435              * @event show
15436              * Fires when this field show.
15437              * @param {Roo.bootstrap.DateField} this
15438              * @param {Mixed} date The date value
15439              */
15440             show : true,
15441             /**
15442              * @event show
15443              * Fires when this field hide.
15444              * @param {Roo.bootstrap.DateField} this
15445              * @param {Mixed} date The date value
15446              */
15447             hide : true,
15448             /**
15449              * @event select
15450              * Fires when select a date.
15451              * @param {Roo.bootstrap.DateField} this
15452              * @param {Mixed} date The date value
15453              */
15454             select : true
15455         });
15456 };
15457
15458 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15459     
15460     /**
15461      * @cfg {String} format
15462      * The default date format string which can be overriden for localization support.  The format must be
15463      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15464      */
15465     format : "m/d/y",
15466     /**
15467      * @cfg {String} altFormats
15468      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15469      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15470      */
15471     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15472     
15473     weekStart : 0,
15474     
15475     viewMode : '',
15476     
15477     minViewMode : '',
15478     
15479     todayHighlight : false,
15480     
15481     todayBtn: false,
15482     
15483     language: 'en',
15484     
15485     keyboardNavigation: true,
15486     
15487     calendarWeeks: false,
15488     
15489     startDate: -Infinity,
15490     
15491     endDate: Infinity,
15492     
15493     daysOfWeekDisabled: [],
15494     
15495     _events: [],
15496     
15497     singleMode : false,
15498     
15499     UTCDate: function()
15500     {
15501         return new Date(Date.UTC.apply(Date, arguments));
15502     },
15503     
15504     UTCToday: function()
15505     {
15506         var today = new Date();
15507         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15508     },
15509     
15510     getDate: function() {
15511             var d = this.getUTCDate();
15512             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15513     },
15514     
15515     getUTCDate: function() {
15516             return this.date;
15517     },
15518     
15519     setDate: function(d) {
15520             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15521     },
15522     
15523     setUTCDate: function(d) {
15524             this.date = d;
15525             this.setValue(this.formatDate(this.date));
15526     },
15527         
15528     onRender: function(ct, position)
15529     {
15530         
15531         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15532         
15533         this.language = this.language || 'en';
15534         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15535         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15536         
15537         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15538         this.format = this.format || 'm/d/y';
15539         this.isInline = false;
15540         this.isInput = true;
15541         this.component = this.el.select('.add-on', true).first() || false;
15542         this.component = (this.component && this.component.length === 0) ? false : this.component;
15543         this.hasInput = this.component && this.inputEL().length;
15544         
15545         if (typeof(this.minViewMode === 'string')) {
15546             switch (this.minViewMode) {
15547                 case 'months':
15548                     this.minViewMode = 1;
15549                     break;
15550                 case 'years':
15551                     this.minViewMode = 2;
15552                     break;
15553                 default:
15554                     this.minViewMode = 0;
15555                     break;
15556             }
15557         }
15558         
15559         if (typeof(this.viewMode === 'string')) {
15560             switch (this.viewMode) {
15561                 case 'months':
15562                     this.viewMode = 1;
15563                     break;
15564                 case 'years':
15565                     this.viewMode = 2;
15566                     break;
15567                 default:
15568                     this.viewMode = 0;
15569                     break;
15570             }
15571         }
15572                 
15573         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15574         
15575 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15576         
15577         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15578         
15579         this.picker().on('mousedown', this.onMousedown, this);
15580         this.picker().on('click', this.onClick, this);
15581         
15582         this.picker().addClass('datepicker-dropdown');
15583         
15584         this.startViewMode = this.viewMode;
15585         
15586         if(this.singleMode){
15587             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15588                 v.setVisibilityMode(Roo.Element.DISPLAY)
15589                 v.hide();
15590             });
15591             
15592             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15593                 v.setStyle('width', '189px');
15594             });
15595         }
15596         
15597         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15598             if(!this.calendarWeeks){
15599                 v.remove();
15600                 return;
15601             }
15602             
15603             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15604             v.attr('colspan', function(i, val){
15605                 return parseInt(val) + 1;
15606             });
15607         })
15608                         
15609         
15610         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15611         
15612         this.setStartDate(this.startDate);
15613         this.setEndDate(this.endDate);
15614         
15615         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15616         
15617         this.fillDow();
15618         this.fillMonths();
15619         this.update();
15620         this.showMode();
15621         
15622         if(this.isInline) {
15623             this.show();
15624         }
15625     },
15626     
15627     picker : function()
15628     {
15629         return this.pickerEl;
15630 //        return this.el.select('.datepicker', true).first();
15631     },
15632     
15633     fillDow: function()
15634     {
15635         var dowCnt = this.weekStart;
15636         
15637         var dow = {
15638             tag: 'tr',
15639             cn: [
15640                 
15641             ]
15642         };
15643         
15644         if(this.calendarWeeks){
15645             dow.cn.push({
15646                 tag: 'th',
15647                 cls: 'cw',
15648                 html: '&nbsp;'
15649             })
15650         }
15651         
15652         while (dowCnt < this.weekStart + 7) {
15653             dow.cn.push({
15654                 tag: 'th',
15655                 cls: 'dow',
15656                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15657             });
15658         }
15659         
15660         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15661     },
15662     
15663     fillMonths: function()
15664     {    
15665         var i = 0;
15666         var months = this.picker().select('>.datepicker-months td', true).first();
15667         
15668         months.dom.innerHTML = '';
15669         
15670         while (i < 12) {
15671             var month = {
15672                 tag: 'span',
15673                 cls: 'month',
15674                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15675             }
15676             
15677             months.createChild(month);
15678         }
15679         
15680     },
15681     
15682     update: function()
15683     {
15684         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;
15685         
15686         if (this.date < this.startDate) {
15687             this.viewDate = new Date(this.startDate);
15688         } else if (this.date > this.endDate) {
15689             this.viewDate = new Date(this.endDate);
15690         } else {
15691             this.viewDate = new Date(this.date);
15692         }
15693         
15694         this.fill();
15695     },
15696     
15697     fill: function() 
15698     {
15699         var d = new Date(this.viewDate),
15700                 year = d.getUTCFullYear(),
15701                 month = d.getUTCMonth(),
15702                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15703                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15704                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15705                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15706                 currentDate = this.date && this.date.valueOf(),
15707                 today = this.UTCToday();
15708         
15709         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15710         
15711 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15712         
15713 //        this.picker.select('>tfoot th.today').
15714 //                                              .text(dates[this.language].today)
15715 //                                              .toggle(this.todayBtn !== false);
15716     
15717         this.updateNavArrows();
15718         this.fillMonths();
15719                                                 
15720         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15721         
15722         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15723          
15724         prevMonth.setUTCDate(day);
15725         
15726         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15727         
15728         var nextMonth = new Date(prevMonth);
15729         
15730         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15731         
15732         nextMonth = nextMonth.valueOf();
15733         
15734         var fillMonths = false;
15735         
15736         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15737         
15738         while(prevMonth.valueOf() < nextMonth) {
15739             var clsName = '';
15740             
15741             if (prevMonth.getUTCDay() === this.weekStart) {
15742                 if(fillMonths){
15743                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15744                 }
15745                     
15746                 fillMonths = {
15747                     tag: 'tr',
15748                     cn: []
15749                 };
15750                 
15751                 if(this.calendarWeeks){
15752                     // ISO 8601: First week contains first thursday.
15753                     // ISO also states week starts on Monday, but we can be more abstract here.
15754                     var
15755                     // Start of current week: based on weekstart/current date
15756                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15757                     // Thursday of this week
15758                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15759                     // First Thursday of year, year from thursday
15760                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15761                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15762                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15763                     
15764                     fillMonths.cn.push({
15765                         tag: 'td',
15766                         cls: 'cw',
15767                         html: calWeek
15768                     });
15769                 }
15770             }
15771             
15772             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15773                 clsName += ' old';
15774             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15775                 clsName += ' new';
15776             }
15777             if (this.todayHighlight &&
15778                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15779                 prevMonth.getUTCMonth() == today.getMonth() &&
15780                 prevMonth.getUTCDate() == today.getDate()) {
15781                 clsName += ' today';
15782             }
15783             
15784             if (currentDate && prevMonth.valueOf() === currentDate) {
15785                 clsName += ' active';
15786             }
15787             
15788             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15789                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15790                     clsName += ' disabled';
15791             }
15792             
15793             fillMonths.cn.push({
15794                 tag: 'td',
15795                 cls: 'day ' + clsName,
15796                 html: prevMonth.getDate()
15797             })
15798             
15799             prevMonth.setDate(prevMonth.getDate()+1);
15800         }
15801           
15802         var currentYear = this.date && this.date.getUTCFullYear();
15803         var currentMonth = this.date && this.date.getUTCMonth();
15804         
15805         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15806         
15807         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15808             v.removeClass('active');
15809             
15810             if(currentYear === year && k === currentMonth){
15811                 v.addClass('active');
15812             }
15813             
15814             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15815                 v.addClass('disabled');
15816             }
15817             
15818         });
15819         
15820         
15821         year = parseInt(year/10, 10) * 10;
15822         
15823         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15824         
15825         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15826         
15827         year -= 1;
15828         for (var i = -1; i < 11; i++) {
15829             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15830                 tag: 'span',
15831                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15832                 html: year
15833             })
15834             
15835             year += 1;
15836         }
15837     },
15838     
15839     showMode: function(dir) 
15840     {
15841         if (dir) {
15842             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15843         }
15844         
15845         Roo.each(this.picker().select('>div',true).elements, function(v){
15846             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15847             v.hide();
15848         });
15849         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15850     },
15851     
15852     place: function()
15853     {
15854         if(this.isInline) return;
15855         
15856         this.picker().removeClass(['bottom', 'top']);
15857         
15858         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15859             /*
15860              * place to the top of element!
15861              *
15862              */
15863             
15864             this.picker().addClass('top');
15865             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15866             
15867             return;
15868         }
15869         
15870         this.picker().addClass('bottom');
15871         
15872         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15873     },
15874     
15875     parseDate : function(value)
15876     {
15877         if(!value || value instanceof Date){
15878             return value;
15879         }
15880         var v = Date.parseDate(value, this.format);
15881         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15882             v = Date.parseDate(value, 'Y-m-d');
15883         }
15884         if(!v && this.altFormats){
15885             if(!this.altFormatsArray){
15886                 this.altFormatsArray = this.altFormats.split("|");
15887             }
15888             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15889                 v = Date.parseDate(value, this.altFormatsArray[i]);
15890             }
15891         }
15892         return v;
15893     },
15894     
15895     formatDate : function(date, fmt)
15896     {   
15897         return (!date || !(date instanceof Date)) ?
15898         date : date.dateFormat(fmt || this.format);
15899     },
15900     
15901     onFocus : function()
15902     {
15903         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15904         this.show();
15905     },
15906     
15907     onBlur : function()
15908     {
15909         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15910         
15911         var d = this.inputEl().getValue();
15912         
15913         this.setValue(d);
15914                 
15915         this.hide();
15916     },
15917     
15918     show : function()
15919     {
15920         this.picker().show();
15921         this.update();
15922         this.place();
15923         
15924         this.fireEvent('show', this, this.date);
15925     },
15926     
15927     hide : function()
15928     {
15929         if(this.isInline) return;
15930         this.picker().hide();
15931         this.viewMode = this.startViewMode;
15932         this.showMode();
15933         
15934         this.fireEvent('hide', this, this.date);
15935         
15936     },
15937     
15938     onMousedown: function(e)
15939     {
15940         e.stopPropagation();
15941         e.preventDefault();
15942     },
15943     
15944     keyup: function(e)
15945     {
15946         Roo.bootstrap.DateField.superclass.keyup.call(this);
15947         this.update();
15948     },
15949
15950     setValue: function(v)
15951     {
15952         
15953         // v can be a string or a date..
15954         
15955         
15956         var d = new Date(this.parseDate(v) ).clearTime();
15957         
15958         if(isNaN(d.getTime())){
15959             this.date = this.viewDate = '';
15960             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15961             return;
15962         }
15963         
15964         v = this.formatDate(d);
15965         
15966         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15967         
15968         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15969      
15970         this.update();
15971
15972         this.fireEvent('select', this, this.date);
15973         
15974     },
15975     
15976     getValue: function()
15977     {
15978         return this.formatDate(this.date);
15979     },
15980     
15981     fireKey: function(e)
15982     {
15983         if (!this.picker().isVisible()){
15984             if (e.keyCode == 27) // allow escape to hide and re-show picker
15985                 this.show();
15986             return;
15987         }
15988         
15989         var dateChanged = false,
15990         dir, day, month,
15991         newDate, newViewDate;
15992         
15993         switch(e.keyCode){
15994             case 27: // escape
15995                 this.hide();
15996                 e.preventDefault();
15997                 break;
15998             case 37: // left
15999             case 39: // right
16000                 if (!this.keyboardNavigation) break;
16001                 dir = e.keyCode == 37 ? -1 : 1;
16002                 
16003                 if (e.ctrlKey){
16004                     newDate = this.moveYear(this.date, dir);
16005                     newViewDate = this.moveYear(this.viewDate, dir);
16006                 } else if (e.shiftKey){
16007                     newDate = this.moveMonth(this.date, dir);
16008                     newViewDate = this.moveMonth(this.viewDate, dir);
16009                 } else {
16010                     newDate = new Date(this.date);
16011                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16012                     newViewDate = new Date(this.viewDate);
16013                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16014                 }
16015                 if (this.dateWithinRange(newDate)){
16016                     this.date = newDate;
16017                     this.viewDate = newViewDate;
16018                     this.setValue(this.formatDate(this.date));
16019 //                    this.update();
16020                     e.preventDefault();
16021                     dateChanged = true;
16022                 }
16023                 break;
16024             case 38: // up
16025             case 40: // down
16026                 if (!this.keyboardNavigation) break;
16027                 dir = e.keyCode == 38 ? -1 : 1;
16028                 if (e.ctrlKey){
16029                     newDate = this.moveYear(this.date, dir);
16030                     newViewDate = this.moveYear(this.viewDate, dir);
16031                 } else if (e.shiftKey){
16032                     newDate = this.moveMonth(this.date, dir);
16033                     newViewDate = this.moveMonth(this.viewDate, dir);
16034                 } else {
16035                     newDate = new Date(this.date);
16036                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16037                     newViewDate = new Date(this.viewDate);
16038                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16039                 }
16040                 if (this.dateWithinRange(newDate)){
16041                     this.date = newDate;
16042                     this.viewDate = newViewDate;
16043                     this.setValue(this.formatDate(this.date));
16044 //                    this.update();
16045                     e.preventDefault();
16046                     dateChanged = true;
16047                 }
16048                 break;
16049             case 13: // enter
16050                 this.setValue(this.formatDate(this.date));
16051                 this.hide();
16052                 e.preventDefault();
16053                 break;
16054             case 9: // tab
16055                 this.setValue(this.formatDate(this.date));
16056                 this.hide();
16057                 break;
16058             case 16: // shift
16059             case 17: // ctrl
16060             case 18: // alt
16061                 break;
16062             default :
16063                 this.hide();
16064                 
16065         }
16066     },
16067     
16068     
16069     onClick: function(e) 
16070     {
16071         e.stopPropagation();
16072         e.preventDefault();
16073         
16074         var target = e.getTarget();
16075         
16076         if(target.nodeName.toLowerCase() === 'i'){
16077             target = Roo.get(target).dom.parentNode;
16078         }
16079         
16080         var nodeName = target.nodeName;
16081         var className = target.className;
16082         var html = target.innerHTML;
16083         //Roo.log(nodeName);
16084         
16085         switch(nodeName.toLowerCase()) {
16086             case 'th':
16087                 switch(className) {
16088                     case 'switch':
16089                         this.showMode(1);
16090                         break;
16091                     case 'prev':
16092                     case 'next':
16093                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16094                         switch(this.viewMode){
16095                                 case 0:
16096                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16097                                         break;
16098                                 case 1:
16099                                 case 2:
16100                                         this.viewDate = this.moveYear(this.viewDate, dir);
16101                                         break;
16102                         }
16103                         this.fill();
16104                         break;
16105                     case 'today':
16106                         var date = new Date();
16107                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16108 //                        this.fill()
16109                         this.setValue(this.formatDate(this.date));
16110                         
16111                         this.hide();
16112                         break;
16113                 }
16114                 break;
16115             case 'span':
16116                 if (className.indexOf('disabled') < 0) {
16117                     this.viewDate.setUTCDate(1);
16118                     if (className.indexOf('month') > -1) {
16119                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16120                     } else {
16121                         var year = parseInt(html, 10) || 0;
16122                         this.viewDate.setUTCFullYear(year);
16123                         
16124                     }
16125                     
16126                     if(this.singleMode){
16127                         this.setValue(this.formatDate(this.viewDate));
16128                         this.hide();
16129                         return;
16130                     }
16131                     
16132                     this.showMode(-1);
16133                     this.fill();
16134                 }
16135                 break;
16136                 
16137             case 'td':
16138                 //Roo.log(className);
16139                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16140                     var day = parseInt(html, 10) || 1;
16141                     var year = this.viewDate.getUTCFullYear(),
16142                         month = this.viewDate.getUTCMonth();
16143
16144                     if (className.indexOf('old') > -1) {
16145                         if(month === 0 ){
16146                             month = 11;
16147                             year -= 1;
16148                         }else{
16149                             month -= 1;
16150                         }
16151                     } else if (className.indexOf('new') > -1) {
16152                         if (month == 11) {
16153                             month = 0;
16154                             year += 1;
16155                         } else {
16156                             month += 1;
16157                         }
16158                     }
16159                     //Roo.log([year,month,day]);
16160                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16161                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16162 //                    this.fill();
16163                     //Roo.log(this.formatDate(this.date));
16164                     this.setValue(this.formatDate(this.date));
16165                     this.hide();
16166                 }
16167                 break;
16168         }
16169     },
16170     
16171     setStartDate: function(startDate)
16172     {
16173         this.startDate = startDate || -Infinity;
16174         if (this.startDate !== -Infinity) {
16175             this.startDate = this.parseDate(this.startDate);
16176         }
16177         this.update();
16178         this.updateNavArrows();
16179     },
16180
16181     setEndDate: function(endDate)
16182     {
16183         this.endDate = endDate || Infinity;
16184         if (this.endDate !== Infinity) {
16185             this.endDate = this.parseDate(this.endDate);
16186         }
16187         this.update();
16188         this.updateNavArrows();
16189     },
16190     
16191     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16192     {
16193         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16194         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16195             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16196         }
16197         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16198             return parseInt(d, 10);
16199         });
16200         this.update();
16201         this.updateNavArrows();
16202     },
16203     
16204     updateNavArrows: function() 
16205     {
16206         if(this.singleMode){
16207             return;
16208         }
16209         
16210         var d = new Date(this.viewDate),
16211         year = d.getUTCFullYear(),
16212         month = d.getUTCMonth();
16213         
16214         Roo.each(this.picker().select('.prev', true).elements, function(v){
16215             v.show();
16216             switch (this.viewMode) {
16217                 case 0:
16218
16219                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16220                         v.hide();
16221                     }
16222                     break;
16223                 case 1:
16224                 case 2:
16225                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16226                         v.hide();
16227                     }
16228                     break;
16229             }
16230         });
16231         
16232         Roo.each(this.picker().select('.next', true).elements, function(v){
16233             v.show();
16234             switch (this.viewMode) {
16235                 case 0:
16236
16237                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16238                         v.hide();
16239                     }
16240                     break;
16241                 case 1:
16242                 case 2:
16243                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16244                         v.hide();
16245                     }
16246                     break;
16247             }
16248         })
16249     },
16250     
16251     moveMonth: function(date, dir)
16252     {
16253         if (!dir) return date;
16254         var new_date = new Date(date.valueOf()),
16255         day = new_date.getUTCDate(),
16256         month = new_date.getUTCMonth(),
16257         mag = Math.abs(dir),
16258         new_month, test;
16259         dir = dir > 0 ? 1 : -1;
16260         if (mag == 1){
16261             test = dir == -1
16262             // If going back one month, make sure month is not current month
16263             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16264             ? function(){
16265                 return new_date.getUTCMonth() == month;
16266             }
16267             // If going forward one month, make sure month is as expected
16268             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16269             : function(){
16270                 return new_date.getUTCMonth() != new_month;
16271             };
16272             new_month = month + dir;
16273             new_date.setUTCMonth(new_month);
16274             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16275             if (new_month < 0 || new_month > 11)
16276                 new_month = (new_month + 12) % 12;
16277         } else {
16278             // For magnitudes >1, move one month at a time...
16279             for (var i=0; i<mag; i++)
16280                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16281                 new_date = this.moveMonth(new_date, dir);
16282             // ...then reset the day, keeping it in the new month
16283             new_month = new_date.getUTCMonth();
16284             new_date.setUTCDate(day);
16285             test = function(){
16286                 return new_month != new_date.getUTCMonth();
16287             };
16288         }
16289         // Common date-resetting loop -- if date is beyond end of month, make it
16290         // end of month
16291         while (test()){
16292             new_date.setUTCDate(--day);
16293             new_date.setUTCMonth(new_month);
16294         }
16295         return new_date;
16296     },
16297
16298     moveYear: function(date, dir)
16299     {
16300         return this.moveMonth(date, dir*12);
16301     },
16302
16303     dateWithinRange: function(date)
16304     {
16305         return date >= this.startDate && date <= this.endDate;
16306     },
16307
16308     
16309     remove: function() 
16310     {
16311         this.picker().remove();
16312     }
16313    
16314 });
16315
16316 Roo.apply(Roo.bootstrap.DateField,  {
16317     
16318     head : {
16319         tag: 'thead',
16320         cn: [
16321         {
16322             tag: 'tr',
16323             cn: [
16324             {
16325                 tag: 'th',
16326                 cls: 'prev',
16327                 html: '<i class="fa fa-arrow-left"/>'
16328             },
16329             {
16330                 tag: 'th',
16331                 cls: 'switch',
16332                 colspan: '5'
16333             },
16334             {
16335                 tag: 'th',
16336                 cls: 'next',
16337                 html: '<i class="fa fa-arrow-right"/>'
16338             }
16339
16340             ]
16341         }
16342         ]
16343     },
16344     
16345     content : {
16346         tag: 'tbody',
16347         cn: [
16348         {
16349             tag: 'tr',
16350             cn: [
16351             {
16352                 tag: 'td',
16353                 colspan: '7'
16354             }
16355             ]
16356         }
16357         ]
16358     },
16359     
16360     footer : {
16361         tag: 'tfoot',
16362         cn: [
16363         {
16364             tag: 'tr',
16365             cn: [
16366             {
16367                 tag: 'th',
16368                 colspan: '7',
16369                 cls: 'today'
16370             }
16371                     
16372             ]
16373         }
16374         ]
16375     },
16376     
16377     dates:{
16378         en: {
16379             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16380             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16381             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16382             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16383             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16384             today: "Today"
16385         }
16386     },
16387     
16388     modes: [
16389     {
16390         clsName: 'days',
16391         navFnc: 'Month',
16392         navStep: 1
16393     },
16394     {
16395         clsName: 'months',
16396         navFnc: 'FullYear',
16397         navStep: 1
16398     },
16399     {
16400         clsName: 'years',
16401         navFnc: 'FullYear',
16402         navStep: 10
16403     }]
16404 });
16405
16406 Roo.apply(Roo.bootstrap.DateField,  {
16407   
16408     template : {
16409         tag: 'div',
16410         cls: 'datepicker dropdown-menu roo-dynamic',
16411         cn: [
16412         {
16413             tag: 'div',
16414             cls: 'datepicker-days',
16415             cn: [
16416             {
16417                 tag: 'table',
16418                 cls: 'table-condensed',
16419                 cn:[
16420                 Roo.bootstrap.DateField.head,
16421                 {
16422                     tag: 'tbody'
16423                 },
16424                 Roo.bootstrap.DateField.footer
16425                 ]
16426             }
16427             ]
16428         },
16429         {
16430             tag: 'div',
16431             cls: 'datepicker-months',
16432             cn: [
16433             {
16434                 tag: 'table',
16435                 cls: 'table-condensed',
16436                 cn:[
16437                 Roo.bootstrap.DateField.head,
16438                 Roo.bootstrap.DateField.content,
16439                 Roo.bootstrap.DateField.footer
16440                 ]
16441             }
16442             ]
16443         },
16444         {
16445             tag: 'div',
16446             cls: 'datepicker-years',
16447             cn: [
16448             {
16449                 tag: 'table',
16450                 cls: 'table-condensed',
16451                 cn:[
16452                 Roo.bootstrap.DateField.head,
16453                 Roo.bootstrap.DateField.content,
16454                 Roo.bootstrap.DateField.footer
16455                 ]
16456             }
16457             ]
16458         }
16459         ]
16460     }
16461 });
16462
16463  
16464
16465  /*
16466  * - LGPL
16467  *
16468  * TimeField
16469  * 
16470  */
16471
16472 /**
16473  * @class Roo.bootstrap.TimeField
16474  * @extends Roo.bootstrap.Input
16475  * Bootstrap DateField class
16476  * 
16477  * 
16478  * @constructor
16479  * Create a new TimeField
16480  * @param {Object} config The config object
16481  */
16482
16483 Roo.bootstrap.TimeField = function(config){
16484     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16485     this.addEvents({
16486             /**
16487              * @event show
16488              * Fires when this field show.
16489              * @param {Roo.bootstrap.DateField} thisthis
16490              * @param {Mixed} date The date value
16491              */
16492             show : true,
16493             /**
16494              * @event show
16495              * Fires when this field hide.
16496              * @param {Roo.bootstrap.DateField} this
16497              * @param {Mixed} date The date value
16498              */
16499             hide : true,
16500             /**
16501              * @event select
16502              * Fires when select a date.
16503              * @param {Roo.bootstrap.DateField} this
16504              * @param {Mixed} date The date value
16505              */
16506             select : true
16507         });
16508 };
16509
16510 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16511     
16512     /**
16513      * @cfg {String} format
16514      * The default time format string which can be overriden for localization support.  The format must be
16515      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16516      */
16517     format : "H:i",
16518        
16519     onRender: function(ct, position)
16520     {
16521         
16522         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16523                 
16524         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16525         
16526         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16527         
16528         this.pop = this.picker().select('>.datepicker-time',true).first();
16529         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16530         
16531         this.picker().on('mousedown', this.onMousedown, this);
16532         this.picker().on('click', this.onClick, this);
16533         
16534         this.picker().addClass('datepicker-dropdown');
16535     
16536         this.fillTime();
16537         this.update();
16538             
16539         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16540         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16541         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16542         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16543         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16544         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16545
16546     },
16547     
16548     fireKey: function(e){
16549         if (!this.picker().isVisible()){
16550             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16551                 this.show();
16552             }
16553             return;
16554         }
16555
16556         e.preventDefault();
16557         
16558         switch(e.keyCode){
16559             case 27: // escape
16560                 this.hide();
16561                 break;
16562             case 37: // left
16563             case 39: // right
16564                 this.onTogglePeriod();
16565                 break;
16566             case 38: // up
16567                 this.onIncrementMinutes();
16568                 break;
16569             case 40: // down
16570                 this.onDecrementMinutes();
16571                 break;
16572             case 13: // enter
16573             case 9: // tab
16574                 this.setTime();
16575                 break;
16576         }
16577     },
16578     
16579     onClick: function(e) {
16580         e.stopPropagation();
16581         e.preventDefault();
16582     },
16583     
16584     picker : function()
16585     {
16586         return this.el.select('.datepicker', true).first();
16587     },
16588     
16589     fillTime: function()
16590     {    
16591         var time = this.pop.select('tbody', true).first();
16592         
16593         time.dom.innerHTML = '';
16594         
16595         time.createChild({
16596             tag: 'tr',
16597             cn: [
16598                 {
16599                     tag: 'td',
16600                     cn: [
16601                         {
16602                             tag: 'a',
16603                             href: '#',
16604                             cls: 'btn',
16605                             cn: [
16606                                 {
16607                                     tag: 'span',
16608                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16609                                 }
16610                             ]
16611                         } 
16612                     ]
16613                 },
16614                 {
16615                     tag: 'td',
16616                     cls: 'separator'
16617                 },
16618                 {
16619                     tag: 'td',
16620                     cn: [
16621                         {
16622                             tag: 'a',
16623                             href: '#',
16624                             cls: 'btn',
16625                             cn: [
16626                                 {
16627                                     tag: 'span',
16628                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16629                                 }
16630                             ]
16631                         }
16632                     ]
16633                 },
16634                 {
16635                     tag: 'td',
16636                     cls: 'separator'
16637                 }
16638             ]
16639         });
16640         
16641         time.createChild({
16642             tag: 'tr',
16643             cn: [
16644                 {
16645                     tag: 'td',
16646                     cn: [
16647                         {
16648                             tag: 'span',
16649                             cls: 'timepicker-hour',
16650                             html: '00'
16651                         }  
16652                     ]
16653                 },
16654                 {
16655                     tag: 'td',
16656                     cls: 'separator',
16657                     html: ':'
16658                 },
16659                 {
16660                     tag: 'td',
16661                     cn: [
16662                         {
16663                             tag: 'span',
16664                             cls: 'timepicker-minute',
16665                             html: '00'
16666                         }  
16667                     ]
16668                 },
16669                 {
16670                     tag: 'td',
16671                     cls: 'separator'
16672                 },
16673                 {
16674                     tag: 'td',
16675                     cn: [
16676                         {
16677                             tag: 'button',
16678                             type: 'button',
16679                             cls: 'btn btn-primary period',
16680                             html: 'AM'
16681                             
16682                         }
16683                     ]
16684                 }
16685             ]
16686         });
16687         
16688         time.createChild({
16689             tag: 'tr',
16690             cn: [
16691                 {
16692                     tag: 'td',
16693                     cn: [
16694                         {
16695                             tag: 'a',
16696                             href: '#',
16697                             cls: 'btn',
16698                             cn: [
16699                                 {
16700                                     tag: 'span',
16701                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16702                                 }
16703                             ]
16704                         }
16705                     ]
16706                 },
16707                 {
16708                     tag: 'td',
16709                     cls: 'separator'
16710                 },
16711                 {
16712                     tag: 'td',
16713                     cn: [
16714                         {
16715                             tag: 'a',
16716                             href: '#',
16717                             cls: 'btn',
16718                             cn: [
16719                                 {
16720                                     tag: 'span',
16721                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16722                                 }
16723                             ]
16724                         }
16725                     ]
16726                 },
16727                 {
16728                     tag: 'td',
16729                     cls: 'separator'
16730                 }
16731             ]
16732         });
16733         
16734     },
16735     
16736     update: function()
16737     {
16738         
16739         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16740         
16741         this.fill();
16742     },
16743     
16744     fill: function() 
16745     {
16746         var hours = this.time.getHours();
16747         var minutes = this.time.getMinutes();
16748         var period = 'AM';
16749         
16750         if(hours > 11){
16751             period = 'PM';
16752         }
16753         
16754         if(hours == 0){
16755             hours = 12;
16756         }
16757         
16758         
16759         if(hours > 12){
16760             hours = hours - 12;
16761         }
16762         
16763         if(hours < 10){
16764             hours = '0' + hours;
16765         }
16766         
16767         if(minutes < 10){
16768             minutes = '0' + minutes;
16769         }
16770         
16771         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16772         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16773         this.pop.select('button', true).first().dom.innerHTML = period;
16774         
16775     },
16776     
16777     place: function()
16778     {   
16779         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16780         
16781         var cls = ['bottom'];
16782         
16783         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16784             cls.pop();
16785             cls.push('top');
16786         }
16787         
16788         cls.push('right');
16789         
16790         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16791             cls.pop();
16792             cls.push('left');
16793         }
16794         
16795         this.picker().addClass(cls.join('-'));
16796         
16797         var _this = this;
16798         
16799         Roo.each(cls, function(c){
16800             if(c == 'bottom'){
16801                 _this.picker().setTop(_this.inputEl().getHeight());
16802                 return;
16803             }
16804             if(c == 'top'){
16805                 _this.picker().setTop(0 - _this.picker().getHeight());
16806                 return;
16807             }
16808             
16809             if(c == 'left'){
16810                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16811                 return;
16812             }
16813             if(c == 'right'){
16814                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16815                 return;
16816             }
16817         });
16818         
16819     },
16820   
16821     onFocus : function()
16822     {
16823         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16824         this.show();
16825     },
16826     
16827     onBlur : function()
16828     {
16829         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16830         this.hide();
16831     },
16832     
16833     show : function()
16834     {
16835         this.picker().show();
16836         this.pop.show();
16837         this.update();
16838         this.place();
16839         
16840         this.fireEvent('show', this, this.date);
16841     },
16842     
16843     hide : function()
16844     {
16845         this.picker().hide();
16846         this.pop.hide();
16847         
16848         this.fireEvent('hide', this, this.date);
16849     },
16850     
16851     setTime : function()
16852     {
16853         this.hide();
16854         this.setValue(this.time.format(this.format));
16855         
16856         this.fireEvent('select', this, this.date);
16857         
16858         
16859     },
16860     
16861     onMousedown: function(e){
16862         e.stopPropagation();
16863         e.preventDefault();
16864     },
16865     
16866     onIncrementHours: function()
16867     {
16868         Roo.log('onIncrementHours');
16869         this.time = this.time.add(Date.HOUR, 1);
16870         this.update();
16871         
16872     },
16873     
16874     onDecrementHours: function()
16875     {
16876         Roo.log('onDecrementHours');
16877         this.time = this.time.add(Date.HOUR, -1);
16878         this.update();
16879     },
16880     
16881     onIncrementMinutes: function()
16882     {
16883         Roo.log('onIncrementMinutes');
16884         this.time = this.time.add(Date.MINUTE, 1);
16885         this.update();
16886     },
16887     
16888     onDecrementMinutes: function()
16889     {
16890         Roo.log('onDecrementMinutes');
16891         this.time = this.time.add(Date.MINUTE, -1);
16892         this.update();
16893     },
16894     
16895     onTogglePeriod: function()
16896     {
16897         Roo.log('onTogglePeriod');
16898         this.time = this.time.add(Date.HOUR, 12);
16899         this.update();
16900     }
16901     
16902    
16903 });
16904
16905 Roo.apply(Roo.bootstrap.TimeField,  {
16906     
16907     content : {
16908         tag: 'tbody',
16909         cn: [
16910             {
16911                 tag: 'tr',
16912                 cn: [
16913                 {
16914                     tag: 'td',
16915                     colspan: '7'
16916                 }
16917                 ]
16918             }
16919         ]
16920     },
16921     
16922     footer : {
16923         tag: 'tfoot',
16924         cn: [
16925             {
16926                 tag: 'tr',
16927                 cn: [
16928                 {
16929                     tag: 'th',
16930                     colspan: '7',
16931                     cls: '',
16932                     cn: [
16933                         {
16934                             tag: 'button',
16935                             cls: 'btn btn-info ok',
16936                             html: 'OK'
16937                         }
16938                     ]
16939                 }
16940
16941                 ]
16942             }
16943         ]
16944     }
16945 });
16946
16947 Roo.apply(Roo.bootstrap.TimeField,  {
16948   
16949     template : {
16950         tag: 'div',
16951         cls: 'datepicker dropdown-menu',
16952         cn: [
16953             {
16954                 tag: 'div',
16955                 cls: 'datepicker-time',
16956                 cn: [
16957                 {
16958                     tag: 'table',
16959                     cls: 'table-condensed',
16960                     cn:[
16961                     Roo.bootstrap.TimeField.content,
16962                     Roo.bootstrap.TimeField.footer
16963                     ]
16964                 }
16965                 ]
16966             }
16967         ]
16968     }
16969 });
16970
16971  
16972
16973  /*
16974  * - LGPL
16975  *
16976  * MonthField
16977  * 
16978  */
16979
16980 /**
16981  * @class Roo.bootstrap.MonthField
16982  * @extends Roo.bootstrap.Input
16983  * Bootstrap MonthField class
16984  * 
16985  * @cfg {String} language default en
16986  * 
16987  * @constructor
16988  * Create a new MonthField
16989  * @param {Object} config The config object
16990  */
16991
16992 Roo.bootstrap.MonthField = function(config){
16993     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16994     
16995     this.addEvents({
16996         /**
16997          * @event show
16998          * Fires when this field show.
16999          * @param {Roo.bootstrap.MonthField} this
17000          * @param {Mixed} date The date value
17001          */
17002         show : true,
17003         /**
17004          * @event show
17005          * Fires when this field hide.
17006          * @param {Roo.bootstrap.MonthField} this
17007          * @param {Mixed} date The date value
17008          */
17009         hide : true,
17010         /**
17011          * @event select
17012          * Fires when select a date.
17013          * @param {Roo.bootstrap.MonthField} this
17014          * @param {String} oldvalue The old value
17015          * @param {String} newvalue The new value
17016          */
17017         select : true
17018     });
17019 };
17020
17021 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17022     
17023     onRender: function(ct, position)
17024     {
17025         
17026         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17027         
17028         this.language = this.language || 'en';
17029         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17030         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17031         
17032         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17033         this.isInline = false;
17034         this.isInput = true;
17035         this.component = this.el.select('.add-on', true).first() || false;
17036         this.component = (this.component && this.component.length === 0) ? false : this.component;
17037         this.hasInput = this.component && this.inputEL().length;
17038         
17039         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17040         
17041         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17042         
17043         this.picker().on('mousedown', this.onMousedown, this);
17044         this.picker().on('click', this.onClick, this);
17045         
17046         this.picker().addClass('datepicker-dropdown');
17047         
17048         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17049             v.setStyle('width', '189px');
17050         });
17051         
17052         this.fillMonths();
17053         
17054         this.update();
17055         
17056         if(this.isInline) {
17057             this.show();
17058         }
17059         
17060     },
17061     
17062     setValue: function(v, suppressEvent)
17063     {   
17064         var o = this.getValue();
17065         
17066         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17067         
17068         this.update();
17069
17070         if(suppressEvent !== true){
17071             this.fireEvent('select', this, o, v);
17072         }
17073         
17074     },
17075     
17076     getValue: function()
17077     {
17078         return this.value;
17079     },
17080     
17081     onClick: function(e) 
17082     {
17083         e.stopPropagation();
17084         e.preventDefault();
17085         
17086         var target = e.getTarget();
17087         
17088         if(target.nodeName.toLowerCase() === 'i'){
17089             target = Roo.get(target).dom.parentNode;
17090         }
17091         
17092         var nodeName = target.nodeName;
17093         var className = target.className;
17094         var html = target.innerHTML;
17095         
17096         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17097             return;
17098         }
17099         
17100         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17101         
17102         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17103         
17104         this.hide();
17105                         
17106     },
17107     
17108     picker : function()
17109     {
17110         return this.pickerEl;
17111     },
17112     
17113     fillMonths: function()
17114     {    
17115         var i = 0;
17116         var months = this.picker().select('>.datepicker-months td', true).first();
17117         
17118         months.dom.innerHTML = '';
17119         
17120         while (i < 12) {
17121             var month = {
17122                 tag: 'span',
17123                 cls: 'month',
17124                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17125             }
17126             
17127             months.createChild(month);
17128         }
17129         
17130     },
17131     
17132     update: function()
17133     {
17134         var _this = this;
17135         
17136         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17137             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17138         }
17139         
17140         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17141             e.removeClass('active');
17142             
17143             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17144                 e.addClass('active');
17145             }
17146         })
17147     },
17148     
17149     place: function()
17150     {
17151         if(this.isInline) return;
17152         
17153         this.picker().removeClass(['bottom', 'top']);
17154         
17155         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17156             /*
17157              * place to the top of element!
17158              *
17159              */
17160             
17161             this.picker().addClass('top');
17162             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17163             
17164             return;
17165         }
17166         
17167         this.picker().addClass('bottom');
17168         
17169         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17170     },
17171     
17172     onFocus : function()
17173     {
17174         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17175         this.show();
17176     },
17177     
17178     onBlur : function()
17179     {
17180         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17181         
17182         var d = this.inputEl().getValue();
17183         
17184         this.setValue(d);
17185                 
17186         this.hide();
17187     },
17188     
17189     show : function()
17190     {
17191         this.picker().show();
17192         this.picker().select('>.datepicker-months', true).first().show();
17193         this.update();
17194         this.place();
17195         
17196         this.fireEvent('show', this, this.date);
17197     },
17198     
17199     hide : function()
17200     {
17201         if(this.isInline) return;
17202         this.picker().hide();
17203         this.fireEvent('hide', this, this.date);
17204         
17205     },
17206     
17207     onMousedown: function(e)
17208     {
17209         e.stopPropagation();
17210         e.preventDefault();
17211     },
17212     
17213     keyup: function(e)
17214     {
17215         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17216         this.update();
17217     },
17218
17219     fireKey: function(e)
17220     {
17221         if (!this.picker().isVisible()){
17222             if (e.keyCode == 27) // allow escape to hide and re-show picker
17223                 this.show();
17224             return;
17225         }
17226         
17227         var dir;
17228         
17229         switch(e.keyCode){
17230             case 27: // escape
17231                 this.hide();
17232                 e.preventDefault();
17233                 break;
17234             case 37: // left
17235             case 39: // right
17236                 dir = e.keyCode == 37 ? -1 : 1;
17237                 
17238                 this.vIndex = this.vIndex + dir;
17239                 
17240                 if(this.vIndex < 0){
17241                     this.vIndex = 0;
17242                 }
17243                 
17244                 if(this.vIndex > 11){
17245                     this.vIndex = 11;
17246                 }
17247                 
17248                 if(isNaN(this.vIndex)){
17249                     this.vIndex = 0;
17250                 }
17251                 
17252                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17253                 
17254                 break;
17255             case 38: // up
17256             case 40: // down
17257                 
17258                 dir = e.keyCode == 38 ? -1 : 1;
17259                 
17260                 this.vIndex = this.vIndex + dir * 4;
17261                 
17262                 if(this.vIndex < 0){
17263                     this.vIndex = 0;
17264                 }
17265                 
17266                 if(this.vIndex > 11){
17267                     this.vIndex = 11;
17268                 }
17269                 
17270                 if(isNaN(this.vIndex)){
17271                     this.vIndex = 0;
17272                 }
17273                 
17274                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17275                 break;
17276                 
17277             case 13: // enter
17278                 
17279                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17280                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17281                 }
17282                 
17283                 this.hide();
17284                 e.preventDefault();
17285                 break;
17286             case 9: // tab
17287                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17288                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17289                 }
17290                 this.hide();
17291                 break;
17292             case 16: // shift
17293             case 17: // ctrl
17294             case 18: // alt
17295                 break;
17296             default :
17297                 this.hide();
17298                 
17299         }
17300     },
17301     
17302     remove: function() 
17303     {
17304         this.picker().remove();
17305     }
17306    
17307 });
17308
17309 Roo.apply(Roo.bootstrap.MonthField,  {
17310     
17311     content : {
17312         tag: 'tbody',
17313         cn: [
17314         {
17315             tag: 'tr',
17316             cn: [
17317             {
17318                 tag: 'td',
17319                 colspan: '7'
17320             }
17321             ]
17322         }
17323         ]
17324     },
17325     
17326     dates:{
17327         en: {
17328             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17329             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17330         }
17331     }
17332 });
17333
17334 Roo.apply(Roo.bootstrap.MonthField,  {
17335   
17336     template : {
17337         tag: 'div',
17338         cls: 'datepicker dropdown-menu roo-dynamic',
17339         cn: [
17340             {
17341                 tag: 'div',
17342                 cls: 'datepicker-months',
17343                 cn: [
17344                 {
17345                     tag: 'table',
17346                     cls: 'table-condensed',
17347                     cn:[
17348                         Roo.bootstrap.DateField.content
17349                     ]
17350                 }
17351                 ]
17352             }
17353         ]
17354     }
17355 });
17356
17357  
17358
17359  
17360  /*
17361  * - LGPL
17362  *
17363  * CheckBox
17364  * 
17365  */
17366
17367 /**
17368  * @class Roo.bootstrap.CheckBox
17369  * @extends Roo.bootstrap.Input
17370  * Bootstrap CheckBox class
17371  * 
17372  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17373  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17374  * @cfg {String} boxLabel The text that appears beside the checkbox
17375  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17376  * @cfg {Boolean} checked initnal the element
17377  * @cfg {Boolean} inline inline the element (default false)
17378  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17379  * 
17380  * @constructor
17381  * Create a new CheckBox
17382  * @param {Object} config The config object
17383  */
17384
17385 Roo.bootstrap.CheckBox = function(config){
17386     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17387    
17388     this.addEvents({
17389         /**
17390         * @event check
17391         * Fires when the element is checked or unchecked.
17392         * @param {Roo.bootstrap.CheckBox} this This input
17393         * @param {Boolean} checked The new checked value
17394         */
17395        check : true
17396     });
17397     
17398 };
17399
17400 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17401   
17402     inputType: 'checkbox',
17403     inputValue: 1,
17404     valueOff: 0,
17405     boxLabel: false,
17406     checked: false,
17407     weight : false,
17408     inline: false,
17409     
17410     getAutoCreate : function()
17411     {
17412         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17413         
17414         var id = Roo.id();
17415         
17416         var cfg = {};
17417         
17418         cfg.cls = 'form-group ' + this.inputType; //input-group
17419         
17420         if(this.inline){
17421             cfg.cls += ' ' + this.inputType + '-inline';
17422         }
17423         
17424         var input =  {
17425             tag: 'input',
17426             id : id,
17427             type : this.inputType,
17428             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17429             cls : 'roo-' + this.inputType, //'form-box',
17430             placeholder : this.placeholder || ''
17431             
17432         };
17433         
17434         if (this.weight) { // Validity check?
17435             cfg.cls += " " + this.inputType + "-" + this.weight;
17436         }
17437         
17438         if (this.disabled) {
17439             input.disabled=true;
17440         }
17441         
17442         if(this.checked){
17443             input.checked = this.checked;
17444         }
17445         
17446         if (this.name) {
17447             input.name = this.name;
17448         }
17449         
17450         if (this.size) {
17451             input.cls += ' input-' + this.size;
17452         }
17453         
17454         var settings=this;
17455         
17456         ['xs','sm','md','lg'].map(function(size){
17457             if (settings[size]) {
17458                 cfg.cls += ' col-' + size + '-' + settings[size];
17459             }
17460         });
17461         
17462         var inputblock = input;
17463          
17464         if (this.before || this.after) {
17465             
17466             inputblock = {
17467                 cls : 'input-group',
17468                 cn :  [] 
17469             };
17470             
17471             if (this.before) {
17472                 inputblock.cn.push({
17473                     tag :'span',
17474                     cls : 'input-group-addon',
17475                     html : this.before
17476                 });
17477             }
17478             
17479             inputblock.cn.push(input);
17480             
17481             if (this.after) {
17482                 inputblock.cn.push({
17483                     tag :'span',
17484                     cls : 'input-group-addon',
17485                     html : this.after
17486                 });
17487             }
17488             
17489         }
17490         
17491         if (align ==='left' && this.fieldLabel.length) {
17492                 Roo.log("left and has label");
17493                 cfg.cn = [
17494                     
17495                     {
17496                         tag: 'label',
17497                         'for' :  id,
17498                         cls : 'control-label col-md-' + this.labelWidth,
17499                         html : this.fieldLabel
17500                         
17501                     },
17502                     {
17503                         cls : "col-md-" + (12 - this.labelWidth), 
17504                         cn: [
17505                             inputblock
17506                         ]
17507                     }
17508                     
17509                 ];
17510         } else if ( this.fieldLabel.length) {
17511                 Roo.log(" label");
17512                 cfg.cn = [
17513                    
17514                     {
17515                         tag: this.boxLabel ? 'span' : 'label',
17516                         'for': id,
17517                         cls: 'control-label box-input-label',
17518                         //cls : 'input-group-addon',
17519                         html : this.fieldLabel
17520                         
17521                     },
17522                     
17523                     inputblock
17524                     
17525                 ];
17526
17527         } else {
17528             
17529                 Roo.log(" no label && no align");
17530                 cfg.cn = [  inputblock ] ;
17531                 
17532                 
17533         }
17534         if(this.boxLabel){
17535              var boxLabelCfg = {
17536                 tag: 'label',
17537                 //'for': id, // box label is handled by onclick - so no for...
17538                 cls: 'box-label',
17539                 html: this.boxLabel
17540             }
17541             
17542             if(this.tooltip){
17543                 boxLabelCfg.tooltip = this.tooltip;
17544             }
17545              
17546             cfg.cn.push(boxLabelCfg);
17547         }
17548         
17549         
17550        
17551         return cfg;
17552         
17553     },
17554     
17555     /**
17556      * return the real input element.
17557      */
17558     inputEl: function ()
17559     {
17560         return this.el.select('input.roo-' + this.inputType,true).first();
17561     },
17562     
17563     labelEl: function()
17564     {
17565         return this.el.select('label.control-label',true).first();
17566     },
17567     /* depricated... */
17568     
17569     label: function()
17570     {
17571         return this.labelEl();
17572     },
17573     
17574     initEvents : function()
17575     {
17576 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17577         
17578         this.inputEl().on('click', this.onClick,  this);
17579         
17580         if (this.boxLabel) { 
17581             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17582         }
17583         
17584         this.startValue = this.getValue();
17585         
17586         if(this.groupId){
17587             Roo.bootstrap.CheckBox.register(this);
17588         }
17589     },
17590     
17591     onClick : function()
17592     {   
17593         this.setChecked(!this.checked);
17594     },
17595     
17596     setChecked : function(state,suppressEvent)
17597     {
17598         this.startValue = this.getValue();
17599         
17600         if(this.inputType == 'radio'){
17601             
17602             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17603                 e.dom.checked = false;
17604             });
17605             
17606             this.inputEl().dom.checked = true;
17607             
17608             this.inputEl().dom.value = this.inputValue;
17609             
17610             if(suppressEvent !== true){
17611                 this.fireEvent('check', this, true);
17612             }
17613             
17614             this.validate();
17615             
17616             return;
17617         }
17618         
17619         this.checked = state;
17620         
17621         this.inputEl().dom.checked = state;
17622         
17623         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17624         
17625         if(suppressEvent !== true){
17626             this.fireEvent('check', this, state);
17627         }
17628         
17629         this.validate();
17630     },
17631     
17632     getValue : function()
17633     {
17634         if(this.inputType == 'radio'){
17635             return this.getGroupValue();
17636         }
17637         
17638         return this.inputEl().getValue();
17639         
17640     },
17641     
17642     getGroupValue : function()
17643     {
17644         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17645             return '';
17646         }
17647         
17648         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17649     },
17650     
17651     setValue : function(v,suppressEvent)
17652     {
17653         if(this.inputType == 'radio'){
17654             this.setGroupValue(v, suppressEvent);
17655             return;
17656         }
17657         
17658         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17659         
17660         this.validate();
17661     },
17662     
17663     setGroupValue : function(v, suppressEvent)
17664     {
17665         this.startValue = this.getValue();
17666         
17667         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17668             e.dom.checked = false;
17669             
17670             if(e.dom.value == v){
17671                 e.dom.checked = true;
17672             }
17673         });
17674         
17675         if(suppressEvent !== true){
17676             this.fireEvent('check', this, true);
17677         }
17678
17679         this.validate();
17680         
17681         return;
17682     },
17683     
17684     validate : function()
17685     {
17686         if(
17687                 this.disabled || 
17688                 (this.inputType == 'radio' && this.validateRadio()) ||
17689                 (this.inputType == 'checkbox' && this.validateCheckbox())
17690         ){
17691             this.markValid();
17692             return true;
17693         }
17694         
17695         this.markInvalid();
17696         return false;
17697     },
17698     
17699     validateRadio : function()
17700     {
17701         var valid = false;
17702         
17703         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17704             if(!e.dom.checked){
17705                 return;
17706             }
17707             
17708             valid = true;
17709             
17710             return false;
17711         });
17712         
17713         return valid;
17714     },
17715     
17716     validateCheckbox : function()
17717     {
17718         if(!this.groupId){
17719             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17720         }
17721         
17722         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17723         
17724         if(!group){
17725             return false;
17726         }
17727         
17728         var r = false;
17729         
17730         for(var i in group){
17731             if(r){
17732                 break;
17733             }
17734             
17735             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17736         }
17737         
17738         return r;
17739     },
17740     
17741     /**
17742      * Mark this field as valid
17743      */
17744     markValid : function()
17745     {
17746         if(this.allowBlank){
17747             return;
17748         }
17749         
17750         var _this = this;
17751         
17752         this.fireEvent('valid', this);
17753         
17754         if(this.inputType == 'radio'){
17755             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17756                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17757                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17758             });
17759             
17760             return;
17761         }
17762         
17763         if(!this.groupId){
17764             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17765             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17766             return;
17767         }
17768         
17769         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17770             
17771         if(!group){
17772             return;
17773         }
17774         
17775         for(var i in group){
17776             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17777             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17778         }
17779     },
17780     
17781      /**
17782      * Mark this field as invalid
17783      * @param {String} msg The validation message
17784      */
17785     markInvalid : function(msg)
17786     {
17787         if(this.allowBlank){
17788             return;
17789         }
17790         
17791         var _this = this;
17792         
17793         this.fireEvent('invalid', this, msg);
17794         
17795         if(this.inputType == 'radio'){
17796             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17797                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17798                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17799             });
17800             
17801             return;
17802         }
17803         
17804         if(!this.groupId){
17805             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17806             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17807             return;
17808         }
17809         
17810         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17811             
17812         if(!group){
17813             return;
17814         }
17815         
17816         for(var i in group){
17817             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17818             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17819         }
17820         
17821     }
17822     
17823 });
17824
17825 Roo.apply(Roo.bootstrap.CheckBox, {
17826     
17827     groups: {},
17828     
17829      /**
17830     * register a CheckBox Group
17831     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17832     */
17833     register : function(checkbox)
17834     {
17835         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17836             this.groups[checkbox.groupId] = {};
17837         }
17838         
17839         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17840             return;
17841         }
17842         
17843         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17844         
17845     },
17846     /**
17847     * fetch a CheckBox Group based on the group ID
17848     * @param {string} the group ID
17849     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17850     */
17851     get: function(groupId) {
17852         if (typeof(this.groups[groupId]) == 'undefined') {
17853             return false;
17854         }
17855         
17856         return this.groups[groupId] ;
17857     }
17858     
17859     
17860 });
17861 /*
17862  * - LGPL
17863  *
17864  * Radio
17865  *
17866  *
17867  * not inline
17868  *<div class="radio">
17869   <label>
17870     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17871     Option one is this and that&mdash;be sure to include why it's great
17872   </label>
17873 </div>
17874  *
17875  *
17876  *inline
17877  *<span>
17878  *<label class="radio-inline">fieldLabel</label>
17879  *<label class="radio-inline">
17880   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17881 </label>
17882 <span>
17883  * 
17884  * 
17885  */
17886
17887 /**
17888  * @class Roo.bootstrap.Radio
17889  * @extends Roo.bootstrap.CheckBox
17890  * Bootstrap Radio class
17891
17892  * @constructor
17893  * Create a new Radio
17894  * @param {Object} config The config object
17895  */
17896
17897 Roo.bootstrap.Radio = function(config){
17898     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17899    
17900 };
17901
17902 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17903     
17904     inputType: 'radio',
17905     inputValue: '',
17906     valueOff: '',
17907     
17908     getAutoCreate : function()
17909     {
17910         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17911         align = align || 'left'; // default...
17912         
17913         
17914         
17915         var id = Roo.id();
17916         
17917         var cfg = {
17918                 tag : this.inline ? 'span' : 'div',
17919                 cls : '',
17920                 cn : []
17921         };
17922         
17923         var inline = this.inline ? ' radio-inline' : '';
17924         
17925         var lbl = {
17926                 tag: 'label' ,
17927                 // does not need for, as we wrap the input with it..
17928                 'for' : id,
17929                 cls : 'control-label box-label' + inline,
17930                 cn : []
17931         };
17932         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17933         
17934         var fieldLabel = {
17935             tag: 'label' ,
17936             //cls : 'control-label' + inline,
17937             html : this.fieldLabel,
17938             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17939         };
17940         
17941  
17942         
17943         
17944         var input =  {
17945             tag: 'input',
17946             id : id,
17947             type : this.inputType,
17948             //value : (!this.checked) ? this.valueOff : this.inputValue,
17949             value : this.inputValue,
17950             cls : 'roo-radio',
17951             placeholder : this.placeholder || '' // ?? needed????
17952             
17953         };
17954         if (this.weight) { // Validity check?
17955             input.cls += " radio-" + this.weight;
17956         }
17957         if (this.disabled) {
17958             input.disabled=true;
17959         }
17960         
17961         if(this.checked){
17962             input.checked = this.checked;
17963         }
17964         
17965         if (this.name) {
17966             input.name = this.name;
17967         }
17968         
17969         if (this.size) {
17970             input.cls += ' input-' + this.size;
17971         }
17972         
17973         //?? can span's inline have a width??
17974         
17975         var settings=this;
17976         ['xs','sm','md','lg'].map(function(size){
17977             if (settings[size]) {
17978                 cfg.cls += ' col-' + size + '-' + settings[size];
17979             }
17980         });
17981         
17982         var inputblock = input;
17983         
17984         if (this.before || this.after) {
17985             
17986             inputblock = {
17987                 cls : 'input-group',
17988                 tag : 'span',
17989                 cn :  [] 
17990             };
17991             if (this.before) {
17992                 inputblock.cn.push({
17993                     tag :'span',
17994                     cls : 'input-group-addon',
17995                     html : this.before
17996                 });
17997             }
17998             inputblock.cn.push(input);
17999             if (this.after) {
18000                 inputblock.cn.push({
18001                     tag :'span',
18002                     cls : 'input-group-addon',
18003                     html : this.after
18004                 });
18005             }
18006             
18007         };
18008         
18009         
18010         if (this.fieldLabel && this.fieldLabel.length) {
18011             cfg.cn.push(fieldLabel);
18012         }
18013        
18014         // normal bootstrap puts the input inside the label.
18015         // however with our styled version - it has to go after the input.
18016        
18017         //lbl.cn.push(inputblock);
18018         
18019         var lblwrap =  {
18020             tag: 'span',
18021             cls: 'radio' + inline,
18022             cn: [
18023                 inputblock,
18024                 lbl
18025             ]
18026         };
18027         
18028         cfg.cn.push( lblwrap);
18029         
18030         if(this.boxLabel){
18031             lbl.cn.push({
18032                 tag: 'span',
18033                 html: this.boxLabel
18034             })
18035         }
18036          
18037         
18038         return cfg;
18039         
18040     },
18041     
18042     initEvents : function()
18043     {
18044 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18045         
18046         this.inputEl().on('click', this.onClick,  this);
18047         if (this.boxLabel) {
18048             Roo.log('find label')
18049             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18050         }
18051         
18052     },
18053     
18054     inputEl: function ()
18055     {
18056         return this.el.select('input.roo-radio',true).first();
18057     },
18058     onClick : function()
18059     {   
18060         Roo.log("click");
18061         this.setChecked(true);
18062     },
18063     
18064     setChecked : function(state,suppressEvent)
18065     {
18066         if(state){
18067             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18068                 v.dom.checked = false;
18069             });
18070         }
18071         Roo.log(this.inputEl().dom);
18072         this.checked = state;
18073         this.inputEl().dom.checked = state;
18074         
18075         if(suppressEvent !== true){
18076             this.fireEvent('check', this, state);
18077         }
18078         
18079         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18080         
18081     },
18082     
18083     getGroupValue : function()
18084     {
18085         var value = '';
18086         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18087             if(v.dom.checked == true){
18088                 value = v.dom.value;
18089             }
18090         });
18091         
18092         return value;
18093     },
18094     
18095     /**
18096      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18097      * @return {Mixed} value The field value
18098      */
18099     getValue : function(){
18100         return this.getGroupValue();
18101     }
18102     
18103 });
18104
18105  
18106 //<script type="text/javascript">
18107
18108 /*
18109  * Based  Ext JS Library 1.1.1
18110  * Copyright(c) 2006-2007, Ext JS, LLC.
18111  * LGPL
18112  *
18113  */
18114  
18115 /**
18116  * @class Roo.HtmlEditorCore
18117  * @extends Roo.Component
18118  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18119  *
18120  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18121  */
18122
18123 Roo.HtmlEditorCore = function(config){
18124     
18125     
18126     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18127     
18128     
18129     this.addEvents({
18130         /**
18131          * @event initialize
18132          * Fires when the editor is fully initialized (including the iframe)
18133          * @param {Roo.HtmlEditorCore} this
18134          */
18135         initialize: true,
18136         /**
18137          * @event activate
18138          * Fires when the editor is first receives the focus. Any insertion must wait
18139          * until after this event.
18140          * @param {Roo.HtmlEditorCore} this
18141          */
18142         activate: true,
18143          /**
18144          * @event beforesync
18145          * Fires before the textarea is updated with content from the editor iframe. Return false
18146          * to cancel the sync.
18147          * @param {Roo.HtmlEditorCore} this
18148          * @param {String} html
18149          */
18150         beforesync: true,
18151          /**
18152          * @event beforepush
18153          * Fires before the iframe editor is updated with content from the textarea. Return false
18154          * to cancel the push.
18155          * @param {Roo.HtmlEditorCore} this
18156          * @param {String} html
18157          */
18158         beforepush: true,
18159          /**
18160          * @event sync
18161          * Fires when the textarea is updated with content from the editor iframe.
18162          * @param {Roo.HtmlEditorCore} this
18163          * @param {String} html
18164          */
18165         sync: true,
18166          /**
18167          * @event push
18168          * Fires when the iframe editor is updated with content from the textarea.
18169          * @param {Roo.HtmlEditorCore} this
18170          * @param {String} html
18171          */
18172         push: true,
18173         
18174         /**
18175          * @event editorevent
18176          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18177          * @param {Roo.HtmlEditorCore} this
18178          */
18179         editorevent: true
18180         
18181     });
18182     
18183     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18184     
18185     // defaults : white / black...
18186     this.applyBlacklists();
18187     
18188     
18189     
18190 };
18191
18192
18193 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18194
18195
18196      /**
18197      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18198      */
18199     
18200     owner : false,
18201     
18202      /**
18203      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18204      *                        Roo.resizable.
18205      */
18206     resizable : false,
18207      /**
18208      * @cfg {Number} height (in pixels)
18209      */   
18210     height: 300,
18211    /**
18212      * @cfg {Number} width (in pixels)
18213      */   
18214     width: 500,
18215     
18216     /**
18217      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18218      * 
18219      */
18220     stylesheets: false,
18221     
18222     // id of frame..
18223     frameId: false,
18224     
18225     // private properties
18226     validationEvent : false,
18227     deferHeight: true,
18228     initialized : false,
18229     activated : false,
18230     sourceEditMode : false,
18231     onFocus : Roo.emptyFn,
18232     iframePad:3,
18233     hideMode:'offsets',
18234     
18235     clearUp: true,
18236     
18237     // blacklist + whitelisted elements..
18238     black: false,
18239     white: false,
18240      
18241     
18242
18243     /**
18244      * Protected method that will not generally be called directly. It
18245      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18246      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18247      */
18248     getDocMarkup : function(){
18249         // body styles..
18250         var st = '';
18251         
18252         // inherit styels from page...?? 
18253         if (this.stylesheets === false) {
18254             
18255             Roo.get(document.head).select('style').each(function(node) {
18256                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18257             });
18258             
18259             Roo.get(document.head).select('link').each(function(node) { 
18260                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18261             });
18262             
18263         } else if (!this.stylesheets.length) {
18264                 // simple..
18265                 st = '<style type="text/css">' +
18266                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18267                    '</style>';
18268         } else { 
18269             
18270         }
18271         
18272         st +=  '<style type="text/css">' +
18273             'IMG { cursor: pointer } ' +
18274         '</style>';
18275
18276         
18277         return '<html><head>' + st  +
18278             //<style type="text/css">' +
18279             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18280             //'</style>' +
18281             ' </head><body class="roo-htmleditor-body"></body></html>';
18282     },
18283
18284     // private
18285     onRender : function(ct, position)
18286     {
18287         var _t = this;
18288         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18289         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18290         
18291         
18292         this.el.dom.style.border = '0 none';
18293         this.el.dom.setAttribute('tabIndex', -1);
18294         this.el.addClass('x-hidden hide');
18295         
18296         
18297         
18298         if(Roo.isIE){ // fix IE 1px bogus margin
18299             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18300         }
18301        
18302         
18303         this.frameId = Roo.id();
18304         
18305          
18306         
18307         var iframe = this.owner.wrap.createChild({
18308             tag: 'iframe',
18309             cls: 'form-control', // bootstrap..
18310             id: this.frameId,
18311             name: this.frameId,
18312             frameBorder : 'no',
18313             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18314         }, this.el
18315         );
18316         
18317         
18318         this.iframe = iframe.dom;
18319
18320          this.assignDocWin();
18321         
18322         this.doc.designMode = 'on';
18323        
18324         this.doc.open();
18325         this.doc.write(this.getDocMarkup());
18326         this.doc.close();
18327
18328         
18329         var task = { // must defer to wait for browser to be ready
18330             run : function(){
18331                 //console.log("run task?" + this.doc.readyState);
18332                 this.assignDocWin();
18333                 if(this.doc.body || this.doc.readyState == 'complete'){
18334                     try {
18335                         this.doc.designMode="on";
18336                     } catch (e) {
18337                         return;
18338                     }
18339                     Roo.TaskMgr.stop(task);
18340                     this.initEditor.defer(10, this);
18341                 }
18342             },
18343             interval : 10,
18344             duration: 10000,
18345             scope: this
18346         };
18347         Roo.TaskMgr.start(task);
18348
18349     },
18350
18351     // private
18352     onResize : function(w, h)
18353     {
18354          Roo.log('resize: ' +w + ',' + h );
18355         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18356         if(!this.iframe){
18357             return;
18358         }
18359         if(typeof w == 'number'){
18360             
18361             this.iframe.style.width = w + 'px';
18362         }
18363         if(typeof h == 'number'){
18364             
18365             this.iframe.style.height = h + 'px';
18366             if(this.doc){
18367                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18368             }
18369         }
18370         
18371     },
18372
18373     /**
18374      * Toggles the editor between standard and source edit mode.
18375      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18376      */
18377     toggleSourceEdit : function(sourceEditMode){
18378         
18379         this.sourceEditMode = sourceEditMode === true;
18380         
18381         if(this.sourceEditMode){
18382  
18383             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18384             
18385         }else{
18386             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18387             //this.iframe.className = '';
18388             this.deferFocus();
18389         }
18390         //this.setSize(this.owner.wrap.getSize());
18391         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18392     },
18393
18394     
18395   
18396
18397     /**
18398      * Protected method that will not generally be called directly. If you need/want
18399      * custom HTML cleanup, this is the method you should override.
18400      * @param {String} html The HTML to be cleaned
18401      * return {String} The cleaned HTML
18402      */
18403     cleanHtml : function(html){
18404         html = String(html);
18405         if(html.length > 5){
18406             if(Roo.isSafari){ // strip safari nonsense
18407                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18408             }
18409         }
18410         if(html == '&nbsp;'){
18411             html = '';
18412         }
18413         return html;
18414     },
18415
18416     /**
18417      * HTML Editor -> Textarea
18418      * Protected method that will not generally be called directly. Syncs the contents
18419      * of the editor iframe with the textarea.
18420      */
18421     syncValue : function(){
18422         if(this.initialized){
18423             var bd = (this.doc.body || this.doc.documentElement);
18424             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18425             var html = bd.innerHTML;
18426             if(Roo.isSafari){
18427                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18428                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18429                 if(m && m[1]){
18430                     html = '<div style="'+m[0]+'">' + html + '</div>';
18431                 }
18432             }
18433             html = this.cleanHtml(html);
18434             // fix up the special chars.. normaly like back quotes in word...
18435             // however we do not want to do this with chinese..
18436             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18437                 var cc = b.charCodeAt();
18438                 if (
18439                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18440                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18441                     (cc >= 0xf900 && cc < 0xfb00 )
18442                 ) {
18443                         return b;
18444                 }
18445                 return "&#"+cc+";" 
18446             });
18447             if(this.owner.fireEvent('beforesync', this, html) !== false){
18448                 this.el.dom.value = html;
18449                 this.owner.fireEvent('sync', this, html);
18450             }
18451         }
18452     },
18453
18454     /**
18455      * Protected method that will not generally be called directly. Pushes the value of the textarea
18456      * into the iframe editor.
18457      */
18458     pushValue : function(){
18459         if(this.initialized){
18460             var v = this.el.dom.value.trim();
18461             
18462 //            if(v.length < 1){
18463 //                v = '&#160;';
18464 //            }
18465             
18466             if(this.owner.fireEvent('beforepush', this, v) !== false){
18467                 var d = (this.doc.body || this.doc.documentElement);
18468                 d.innerHTML = v;
18469                 this.cleanUpPaste();
18470                 this.el.dom.value = d.innerHTML;
18471                 this.owner.fireEvent('push', this, v);
18472             }
18473         }
18474     },
18475
18476     // private
18477     deferFocus : function(){
18478         this.focus.defer(10, this);
18479     },
18480
18481     // doc'ed in Field
18482     focus : function(){
18483         if(this.win && !this.sourceEditMode){
18484             this.win.focus();
18485         }else{
18486             this.el.focus();
18487         }
18488     },
18489     
18490     assignDocWin: function()
18491     {
18492         var iframe = this.iframe;
18493         
18494          if(Roo.isIE){
18495             this.doc = iframe.contentWindow.document;
18496             this.win = iframe.contentWindow;
18497         } else {
18498 //            if (!Roo.get(this.frameId)) {
18499 //                return;
18500 //            }
18501 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18502 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18503             
18504             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18505                 return;
18506             }
18507             
18508             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18509             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18510         }
18511     },
18512     
18513     // private
18514     initEditor : function(){
18515         //console.log("INIT EDITOR");
18516         this.assignDocWin();
18517         
18518         
18519         
18520         this.doc.designMode="on";
18521         this.doc.open();
18522         this.doc.write(this.getDocMarkup());
18523         this.doc.close();
18524         
18525         var dbody = (this.doc.body || this.doc.documentElement);
18526         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18527         // this copies styles from the containing element into thsi one..
18528         // not sure why we need all of this..
18529         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18530         
18531         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18532         //ss['background-attachment'] = 'fixed'; // w3c
18533         dbody.bgProperties = 'fixed'; // ie
18534         //Roo.DomHelper.applyStyles(dbody, ss);
18535         Roo.EventManager.on(this.doc, {
18536             //'mousedown': this.onEditorEvent,
18537             'mouseup': this.onEditorEvent,
18538             'dblclick': this.onEditorEvent,
18539             'click': this.onEditorEvent,
18540             'keyup': this.onEditorEvent,
18541             buffer:100,
18542             scope: this
18543         });
18544         if(Roo.isGecko){
18545             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18546         }
18547         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18548             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18549         }
18550         this.initialized = true;
18551
18552         this.owner.fireEvent('initialize', this);
18553         this.pushValue();
18554     },
18555
18556     // private
18557     onDestroy : function(){
18558         
18559         
18560         
18561         if(this.rendered){
18562             
18563             //for (var i =0; i < this.toolbars.length;i++) {
18564             //    // fixme - ask toolbars for heights?
18565             //    this.toolbars[i].onDestroy();
18566            // }
18567             
18568             //this.wrap.dom.innerHTML = '';
18569             //this.wrap.remove();
18570         }
18571     },
18572
18573     // private
18574     onFirstFocus : function(){
18575         
18576         this.assignDocWin();
18577         
18578         
18579         this.activated = true;
18580          
18581     
18582         if(Roo.isGecko){ // prevent silly gecko errors
18583             this.win.focus();
18584             var s = this.win.getSelection();
18585             if(!s.focusNode || s.focusNode.nodeType != 3){
18586                 var r = s.getRangeAt(0);
18587                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18588                 r.collapse(true);
18589                 this.deferFocus();
18590             }
18591             try{
18592                 this.execCmd('useCSS', true);
18593                 this.execCmd('styleWithCSS', false);
18594             }catch(e){}
18595         }
18596         this.owner.fireEvent('activate', this);
18597     },
18598
18599     // private
18600     adjustFont: function(btn){
18601         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18602         //if(Roo.isSafari){ // safari
18603         //    adjust *= 2;
18604        // }
18605         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18606         if(Roo.isSafari){ // safari
18607             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18608             v =  (v < 10) ? 10 : v;
18609             v =  (v > 48) ? 48 : v;
18610             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18611             
18612         }
18613         
18614         
18615         v = Math.max(1, v+adjust);
18616         
18617         this.execCmd('FontSize', v  );
18618     },
18619
18620     onEditorEvent : function(e)
18621     {
18622         this.owner.fireEvent('editorevent', this, e);
18623       //  this.updateToolbar();
18624         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18625     },
18626
18627     insertTag : function(tg)
18628     {
18629         // could be a bit smarter... -> wrap the current selected tRoo..
18630         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18631             
18632             range = this.createRange(this.getSelection());
18633             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18634             wrappingNode.appendChild(range.extractContents());
18635             range.insertNode(wrappingNode);
18636
18637             return;
18638             
18639             
18640             
18641         }
18642         this.execCmd("formatblock",   tg);
18643         
18644     },
18645     
18646     insertText : function(txt)
18647     {
18648         
18649         
18650         var range = this.createRange();
18651         range.deleteContents();
18652                //alert(Sender.getAttribute('label'));
18653                
18654         range.insertNode(this.doc.createTextNode(txt));
18655     } ,
18656     
18657      
18658
18659     /**
18660      * Executes a Midas editor command on the editor document and performs necessary focus and
18661      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18662      * @param {String} cmd The Midas command
18663      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18664      */
18665     relayCmd : function(cmd, value){
18666         this.win.focus();
18667         this.execCmd(cmd, value);
18668         this.owner.fireEvent('editorevent', this);
18669         //this.updateToolbar();
18670         this.owner.deferFocus();
18671     },
18672
18673     /**
18674      * Executes a Midas editor command directly on the editor document.
18675      * For visual commands, you should use {@link #relayCmd} instead.
18676      * <b>This should only be called after the editor is initialized.</b>
18677      * @param {String} cmd The Midas command
18678      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18679      */
18680     execCmd : function(cmd, value){
18681         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18682         this.syncValue();
18683     },
18684  
18685  
18686    
18687     /**
18688      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18689      * to insert tRoo.
18690      * @param {String} text | dom node.. 
18691      */
18692     insertAtCursor : function(text)
18693     {
18694         
18695         
18696         
18697         if(!this.activated){
18698             return;
18699         }
18700         /*
18701         if(Roo.isIE){
18702             this.win.focus();
18703             var r = this.doc.selection.createRange();
18704             if(r){
18705                 r.collapse(true);
18706                 r.pasteHTML(text);
18707                 this.syncValue();
18708                 this.deferFocus();
18709             
18710             }
18711             return;
18712         }
18713         */
18714         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18715             this.win.focus();
18716             
18717             
18718             // from jquery ui (MIT licenced)
18719             var range, node;
18720             var win = this.win;
18721             
18722             if (win.getSelection && win.getSelection().getRangeAt) {
18723                 range = win.getSelection().getRangeAt(0);
18724                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18725                 range.insertNode(node);
18726             } else if (win.document.selection && win.document.selection.createRange) {
18727                 // no firefox support
18728                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18729                 win.document.selection.createRange().pasteHTML(txt);
18730             } else {
18731                 // no firefox support
18732                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18733                 this.execCmd('InsertHTML', txt);
18734             } 
18735             
18736             this.syncValue();
18737             
18738             this.deferFocus();
18739         }
18740     },
18741  // private
18742     mozKeyPress : function(e){
18743         if(e.ctrlKey){
18744             var c = e.getCharCode(), cmd;
18745           
18746             if(c > 0){
18747                 c = String.fromCharCode(c).toLowerCase();
18748                 switch(c){
18749                     case 'b':
18750                         cmd = 'bold';
18751                         break;
18752                     case 'i':
18753                         cmd = 'italic';
18754                         break;
18755                     
18756                     case 'u':
18757                         cmd = 'underline';
18758                         break;
18759                     
18760                     case 'v':
18761                         this.cleanUpPaste.defer(100, this);
18762                         return;
18763                         
18764                 }
18765                 if(cmd){
18766                     this.win.focus();
18767                     this.execCmd(cmd);
18768                     this.deferFocus();
18769                     e.preventDefault();
18770                 }
18771                 
18772             }
18773         }
18774     },
18775
18776     // private
18777     fixKeys : function(){ // load time branching for fastest keydown performance
18778         if(Roo.isIE){
18779             return function(e){
18780                 var k = e.getKey(), r;
18781                 if(k == e.TAB){
18782                     e.stopEvent();
18783                     r = this.doc.selection.createRange();
18784                     if(r){
18785                         r.collapse(true);
18786                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18787                         this.deferFocus();
18788                     }
18789                     return;
18790                 }
18791                 
18792                 if(k == e.ENTER){
18793                     r = this.doc.selection.createRange();
18794                     if(r){
18795                         var target = r.parentElement();
18796                         if(!target || target.tagName.toLowerCase() != 'li'){
18797                             e.stopEvent();
18798                             r.pasteHTML('<br />');
18799                             r.collapse(false);
18800                             r.select();
18801                         }
18802                     }
18803                 }
18804                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18805                     this.cleanUpPaste.defer(100, this);
18806                     return;
18807                 }
18808                 
18809                 
18810             };
18811         }else if(Roo.isOpera){
18812             return function(e){
18813                 var k = e.getKey();
18814                 if(k == e.TAB){
18815                     e.stopEvent();
18816                     this.win.focus();
18817                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18818                     this.deferFocus();
18819                 }
18820                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18821                     this.cleanUpPaste.defer(100, this);
18822                     return;
18823                 }
18824                 
18825             };
18826         }else if(Roo.isSafari){
18827             return function(e){
18828                 var k = e.getKey();
18829                 
18830                 if(k == e.TAB){
18831                     e.stopEvent();
18832                     this.execCmd('InsertText','\t');
18833                     this.deferFocus();
18834                     return;
18835                 }
18836                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18837                     this.cleanUpPaste.defer(100, this);
18838                     return;
18839                 }
18840                 
18841              };
18842         }
18843     }(),
18844     
18845     getAllAncestors: function()
18846     {
18847         var p = this.getSelectedNode();
18848         var a = [];
18849         if (!p) {
18850             a.push(p); // push blank onto stack..
18851             p = this.getParentElement();
18852         }
18853         
18854         
18855         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18856             a.push(p);
18857             p = p.parentNode;
18858         }
18859         a.push(this.doc.body);
18860         return a;
18861     },
18862     lastSel : false,
18863     lastSelNode : false,
18864     
18865     
18866     getSelection : function() 
18867     {
18868         this.assignDocWin();
18869         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18870     },
18871     
18872     getSelectedNode: function() 
18873     {
18874         // this may only work on Gecko!!!
18875         
18876         // should we cache this!!!!
18877         
18878         
18879         
18880          
18881         var range = this.createRange(this.getSelection()).cloneRange();
18882         
18883         if (Roo.isIE) {
18884             var parent = range.parentElement();
18885             while (true) {
18886                 var testRange = range.duplicate();
18887                 testRange.moveToElementText(parent);
18888                 if (testRange.inRange(range)) {
18889                     break;
18890                 }
18891                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18892                     break;
18893                 }
18894                 parent = parent.parentElement;
18895             }
18896             return parent;
18897         }
18898         
18899         // is ancestor a text element.
18900         var ac =  range.commonAncestorContainer;
18901         if (ac.nodeType == 3) {
18902             ac = ac.parentNode;
18903         }
18904         
18905         var ar = ac.childNodes;
18906          
18907         var nodes = [];
18908         var other_nodes = [];
18909         var has_other_nodes = false;
18910         for (var i=0;i<ar.length;i++) {
18911             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18912                 continue;
18913             }
18914             // fullly contained node.
18915             
18916             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18917                 nodes.push(ar[i]);
18918                 continue;
18919             }
18920             
18921             // probably selected..
18922             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18923                 other_nodes.push(ar[i]);
18924                 continue;
18925             }
18926             // outer..
18927             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18928                 continue;
18929             }
18930             
18931             
18932             has_other_nodes = true;
18933         }
18934         if (!nodes.length && other_nodes.length) {
18935             nodes= other_nodes;
18936         }
18937         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18938             return false;
18939         }
18940         
18941         return nodes[0];
18942     },
18943     createRange: function(sel)
18944     {
18945         // this has strange effects when using with 
18946         // top toolbar - not sure if it's a great idea.
18947         //this.editor.contentWindow.focus();
18948         if (typeof sel != "undefined") {
18949             try {
18950                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18951             } catch(e) {
18952                 return this.doc.createRange();
18953             }
18954         } else {
18955             return this.doc.createRange();
18956         }
18957     },
18958     getParentElement: function()
18959     {
18960         
18961         this.assignDocWin();
18962         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18963         
18964         var range = this.createRange(sel);
18965          
18966         try {
18967             var p = range.commonAncestorContainer;
18968             while (p.nodeType == 3) { // text node
18969                 p = p.parentNode;
18970             }
18971             return p;
18972         } catch (e) {
18973             return null;
18974         }
18975     
18976     },
18977     /***
18978      *
18979      * Range intersection.. the hard stuff...
18980      *  '-1' = before
18981      *  '0' = hits..
18982      *  '1' = after.
18983      *         [ -- selected range --- ]
18984      *   [fail]                        [fail]
18985      *
18986      *    basically..
18987      *      if end is before start or  hits it. fail.
18988      *      if start is after end or hits it fail.
18989      *
18990      *   if either hits (but other is outside. - then it's not 
18991      *   
18992      *    
18993      **/
18994     
18995     
18996     // @see http://www.thismuchiknow.co.uk/?p=64.
18997     rangeIntersectsNode : function(range, node)
18998     {
18999         var nodeRange = node.ownerDocument.createRange();
19000         try {
19001             nodeRange.selectNode(node);
19002         } catch (e) {
19003             nodeRange.selectNodeContents(node);
19004         }
19005     
19006         var rangeStartRange = range.cloneRange();
19007         rangeStartRange.collapse(true);
19008     
19009         var rangeEndRange = range.cloneRange();
19010         rangeEndRange.collapse(false);
19011     
19012         var nodeStartRange = nodeRange.cloneRange();
19013         nodeStartRange.collapse(true);
19014     
19015         var nodeEndRange = nodeRange.cloneRange();
19016         nodeEndRange.collapse(false);
19017     
19018         return rangeStartRange.compareBoundaryPoints(
19019                  Range.START_TO_START, nodeEndRange) == -1 &&
19020                rangeEndRange.compareBoundaryPoints(
19021                  Range.START_TO_START, nodeStartRange) == 1;
19022         
19023          
19024     },
19025     rangeCompareNode : function(range, node)
19026     {
19027         var nodeRange = node.ownerDocument.createRange();
19028         try {
19029             nodeRange.selectNode(node);
19030         } catch (e) {
19031             nodeRange.selectNodeContents(node);
19032         }
19033         
19034         
19035         range.collapse(true);
19036     
19037         nodeRange.collapse(true);
19038      
19039         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19040         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19041          
19042         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19043         
19044         var nodeIsBefore   =  ss == 1;
19045         var nodeIsAfter    = ee == -1;
19046         
19047         if (nodeIsBefore && nodeIsAfter)
19048             return 0; // outer
19049         if (!nodeIsBefore && nodeIsAfter)
19050             return 1; //right trailed.
19051         
19052         if (nodeIsBefore && !nodeIsAfter)
19053             return 2;  // left trailed.
19054         // fully contined.
19055         return 3;
19056     },
19057
19058     // private? - in a new class?
19059     cleanUpPaste :  function()
19060     {
19061         // cleans up the whole document..
19062         Roo.log('cleanuppaste');
19063         
19064         this.cleanUpChildren(this.doc.body);
19065         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19066         if (clean != this.doc.body.innerHTML) {
19067             this.doc.body.innerHTML = clean;
19068         }
19069         
19070     },
19071     
19072     cleanWordChars : function(input) {// change the chars to hex code
19073         var he = Roo.HtmlEditorCore;
19074         
19075         var output = input;
19076         Roo.each(he.swapCodes, function(sw) { 
19077             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19078             
19079             output = output.replace(swapper, sw[1]);
19080         });
19081         
19082         return output;
19083     },
19084     
19085     
19086     cleanUpChildren : function (n)
19087     {
19088         if (!n.childNodes.length) {
19089             return;
19090         }
19091         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19092            this.cleanUpChild(n.childNodes[i]);
19093         }
19094     },
19095     
19096     
19097         
19098     
19099     cleanUpChild : function (node)
19100     {
19101         var ed = this;
19102         //console.log(node);
19103         if (node.nodeName == "#text") {
19104             // clean up silly Windows -- stuff?
19105             return; 
19106         }
19107         if (node.nodeName == "#comment") {
19108             node.parentNode.removeChild(node);
19109             // clean up silly Windows -- stuff?
19110             return; 
19111         }
19112         var lcname = node.tagName.toLowerCase();
19113         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19114         // whitelist of tags..
19115         
19116         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19117             // remove node.
19118             node.parentNode.removeChild(node);
19119             return;
19120             
19121         }
19122         
19123         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19124         
19125         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19126         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19127         
19128         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19129         //    remove_keep_children = true;
19130         //}
19131         
19132         if (remove_keep_children) {
19133             this.cleanUpChildren(node);
19134             // inserts everything just before this node...
19135             while (node.childNodes.length) {
19136                 var cn = node.childNodes[0];
19137                 node.removeChild(cn);
19138                 node.parentNode.insertBefore(cn, node);
19139             }
19140             node.parentNode.removeChild(node);
19141             return;
19142         }
19143         
19144         if (!node.attributes || !node.attributes.length) {
19145             this.cleanUpChildren(node);
19146             return;
19147         }
19148         
19149         function cleanAttr(n,v)
19150         {
19151             
19152             if (v.match(/^\./) || v.match(/^\//)) {
19153                 return;
19154             }
19155             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19156                 return;
19157             }
19158             if (v.match(/^#/)) {
19159                 return;
19160             }
19161 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19162             node.removeAttribute(n);
19163             
19164         }
19165         
19166         var cwhite = this.cwhite;
19167         var cblack = this.cblack;
19168             
19169         function cleanStyle(n,v)
19170         {
19171             if (v.match(/expression/)) { //XSS?? should we even bother..
19172                 node.removeAttribute(n);
19173                 return;
19174             }
19175             
19176             var parts = v.split(/;/);
19177             var clean = [];
19178             
19179             Roo.each(parts, function(p) {
19180                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19181                 if (!p.length) {
19182                     return true;
19183                 }
19184                 var l = p.split(':').shift().replace(/\s+/g,'');
19185                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19186                 
19187                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19188 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19189                     //node.removeAttribute(n);
19190                     return true;
19191                 }
19192                 //Roo.log()
19193                 // only allow 'c whitelisted system attributes'
19194                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19195 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19196                     //node.removeAttribute(n);
19197                     return true;
19198                 }
19199                 
19200                 
19201                  
19202                 
19203                 clean.push(p);
19204                 return true;
19205             });
19206             if (clean.length) { 
19207                 node.setAttribute(n, clean.join(';'));
19208             } else {
19209                 node.removeAttribute(n);
19210             }
19211             
19212         }
19213         
19214         
19215         for (var i = node.attributes.length-1; i > -1 ; i--) {
19216             var a = node.attributes[i];
19217             //console.log(a);
19218             
19219             if (a.name.toLowerCase().substr(0,2)=='on')  {
19220                 node.removeAttribute(a.name);
19221                 continue;
19222             }
19223             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19224                 node.removeAttribute(a.name);
19225                 continue;
19226             }
19227             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19228                 cleanAttr(a.name,a.value); // fixme..
19229                 continue;
19230             }
19231             if (a.name == 'style') {
19232                 cleanStyle(a.name,a.value);
19233                 continue;
19234             }
19235             /// clean up MS crap..
19236             // tecnically this should be a list of valid class'es..
19237             
19238             
19239             if (a.name == 'class') {
19240                 if (a.value.match(/^Mso/)) {
19241                     node.className = '';
19242                 }
19243                 
19244                 if (a.value.match(/body/)) {
19245                     node.className = '';
19246                 }
19247                 continue;
19248             }
19249             
19250             // style cleanup!?
19251             // class cleanup?
19252             
19253         }
19254         
19255         
19256         this.cleanUpChildren(node);
19257         
19258         
19259     },
19260     
19261     /**
19262      * Clean up MS wordisms...
19263      */
19264     cleanWord : function(node)
19265     {
19266         
19267         
19268         if (!node) {
19269             this.cleanWord(this.doc.body);
19270             return;
19271         }
19272         if (node.nodeName == "#text") {
19273             // clean up silly Windows -- stuff?
19274             return; 
19275         }
19276         if (node.nodeName == "#comment") {
19277             node.parentNode.removeChild(node);
19278             // clean up silly Windows -- stuff?
19279             return; 
19280         }
19281         
19282         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19283             node.parentNode.removeChild(node);
19284             return;
19285         }
19286         
19287         // remove - but keep children..
19288         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19289             while (node.childNodes.length) {
19290                 var cn = node.childNodes[0];
19291                 node.removeChild(cn);
19292                 node.parentNode.insertBefore(cn, node);
19293             }
19294             node.parentNode.removeChild(node);
19295             this.iterateChildren(node, this.cleanWord);
19296             return;
19297         }
19298         // clean styles
19299         if (node.className.length) {
19300             
19301             var cn = node.className.split(/\W+/);
19302             var cna = [];
19303             Roo.each(cn, function(cls) {
19304                 if (cls.match(/Mso[a-zA-Z]+/)) {
19305                     return;
19306                 }
19307                 cna.push(cls);
19308             });
19309             node.className = cna.length ? cna.join(' ') : '';
19310             if (!cna.length) {
19311                 node.removeAttribute("class");
19312             }
19313         }
19314         
19315         if (node.hasAttribute("lang")) {
19316             node.removeAttribute("lang");
19317         }
19318         
19319         if (node.hasAttribute("style")) {
19320             
19321             var styles = node.getAttribute("style").split(";");
19322             var nstyle = [];
19323             Roo.each(styles, function(s) {
19324                 if (!s.match(/:/)) {
19325                     return;
19326                 }
19327                 var kv = s.split(":");
19328                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19329                     return;
19330                 }
19331                 // what ever is left... we allow.
19332                 nstyle.push(s);
19333             });
19334             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19335             if (!nstyle.length) {
19336                 node.removeAttribute('style');
19337             }
19338         }
19339         this.iterateChildren(node, this.cleanWord);
19340         
19341         
19342         
19343     },
19344     /**
19345      * iterateChildren of a Node, calling fn each time, using this as the scole..
19346      * @param {DomNode} node node to iterate children of.
19347      * @param {Function} fn method of this class to call on each item.
19348      */
19349     iterateChildren : function(node, fn)
19350     {
19351         if (!node.childNodes.length) {
19352                 return;
19353         }
19354         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19355            fn.call(this, node.childNodes[i])
19356         }
19357     },
19358     
19359     
19360     /**
19361      * cleanTableWidths.
19362      *
19363      * Quite often pasting from word etc.. results in tables with column and widths.
19364      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19365      *
19366      */
19367     cleanTableWidths : function(node)
19368     {
19369          
19370          
19371         if (!node) {
19372             this.cleanTableWidths(this.doc.body);
19373             return;
19374         }
19375         
19376         // ignore list...
19377         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19378             return; 
19379         }
19380         Roo.log(node.tagName);
19381         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19382             this.iterateChildren(node, this.cleanTableWidths);
19383             return;
19384         }
19385         if (node.hasAttribute('width')) {
19386             node.removeAttribute('width');
19387         }
19388         
19389          
19390         if (node.hasAttribute("style")) {
19391             // pretty basic...
19392             
19393             var styles = node.getAttribute("style").split(";");
19394             var nstyle = [];
19395             Roo.each(styles, function(s) {
19396                 if (!s.match(/:/)) {
19397                     return;
19398                 }
19399                 var kv = s.split(":");
19400                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
19401                     return;
19402                 }
19403                 // what ever is left... we allow.
19404                 nstyle.push(s);
19405             });
19406             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19407             if (!nstyle.length) {
19408                 node.removeAttribute('style');
19409             }
19410         }
19411         
19412         this.iterateChildren(node, this.cleanTableWidths);
19413         
19414         
19415     },
19416     
19417     
19418     
19419     
19420     domToHTML : function(currentElement, depth, nopadtext) {
19421         
19422         depth = depth || 0;
19423         nopadtext = nopadtext || false;
19424     
19425         if (!currentElement) {
19426             return this.domToHTML(this.doc.body);
19427         }
19428         
19429         //Roo.log(currentElement);
19430         var j;
19431         var allText = false;
19432         var nodeName = currentElement.nodeName;
19433         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19434         
19435         if  (nodeName == '#text') {
19436             
19437             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19438         }
19439         
19440         
19441         var ret = '';
19442         if (nodeName != 'BODY') {
19443              
19444             var i = 0;
19445             // Prints the node tagName, such as <A>, <IMG>, etc
19446             if (tagName) {
19447                 var attr = [];
19448                 for(i = 0; i < currentElement.attributes.length;i++) {
19449                     // quoting?
19450                     var aname = currentElement.attributes.item(i).name;
19451                     if (!currentElement.attributes.item(i).value.length) {
19452                         continue;
19453                     }
19454                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19455                 }
19456                 
19457                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19458             } 
19459             else {
19460                 
19461                 // eack
19462             }
19463         } else {
19464             tagName = false;
19465         }
19466         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19467             return ret;
19468         }
19469         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19470             nopadtext = true;
19471         }
19472         
19473         
19474         // Traverse the tree
19475         i = 0;
19476         var currentElementChild = currentElement.childNodes.item(i);
19477         var allText = true;
19478         var innerHTML  = '';
19479         lastnode = '';
19480         while (currentElementChild) {
19481             // Formatting code (indent the tree so it looks nice on the screen)
19482             var nopad = nopadtext;
19483             if (lastnode == 'SPAN') {
19484                 nopad  = true;
19485             }
19486             // text
19487             if  (currentElementChild.nodeName == '#text') {
19488                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19489                 toadd = nopadtext ? toadd : toadd.trim();
19490                 if (!nopad && toadd.length > 80) {
19491                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19492                 }
19493                 innerHTML  += toadd;
19494                 
19495                 i++;
19496                 currentElementChild = currentElement.childNodes.item(i);
19497                 lastNode = '';
19498                 continue;
19499             }
19500             allText = false;
19501             
19502             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19503                 
19504             // Recursively traverse the tree structure of the child node
19505             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19506             lastnode = currentElementChild.nodeName;
19507             i++;
19508             currentElementChild=currentElement.childNodes.item(i);
19509         }
19510         
19511         ret += innerHTML;
19512         
19513         if (!allText) {
19514                 // The remaining code is mostly for formatting the tree
19515             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19516         }
19517         
19518         
19519         if (tagName) {
19520             ret+= "</"+tagName+">";
19521         }
19522         return ret;
19523         
19524     },
19525         
19526     applyBlacklists : function()
19527     {
19528         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19529         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19530         
19531         this.white = [];
19532         this.black = [];
19533         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19534             if (b.indexOf(tag) > -1) {
19535                 return;
19536             }
19537             this.white.push(tag);
19538             
19539         }, this);
19540         
19541         Roo.each(w, function(tag) {
19542             if (b.indexOf(tag) > -1) {
19543                 return;
19544             }
19545             if (this.white.indexOf(tag) > -1) {
19546                 return;
19547             }
19548             this.white.push(tag);
19549             
19550         }, this);
19551         
19552         
19553         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19554             if (w.indexOf(tag) > -1) {
19555                 return;
19556             }
19557             this.black.push(tag);
19558             
19559         }, this);
19560         
19561         Roo.each(b, function(tag) {
19562             if (w.indexOf(tag) > -1) {
19563                 return;
19564             }
19565             if (this.black.indexOf(tag) > -1) {
19566                 return;
19567             }
19568             this.black.push(tag);
19569             
19570         }, this);
19571         
19572         
19573         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19574         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19575         
19576         this.cwhite = [];
19577         this.cblack = [];
19578         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19579             if (b.indexOf(tag) > -1) {
19580                 return;
19581             }
19582             this.cwhite.push(tag);
19583             
19584         }, this);
19585         
19586         Roo.each(w, function(tag) {
19587             if (b.indexOf(tag) > -1) {
19588                 return;
19589             }
19590             if (this.cwhite.indexOf(tag) > -1) {
19591                 return;
19592             }
19593             this.cwhite.push(tag);
19594             
19595         }, this);
19596         
19597         
19598         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19599             if (w.indexOf(tag) > -1) {
19600                 return;
19601             }
19602             this.cblack.push(tag);
19603             
19604         }, this);
19605         
19606         Roo.each(b, function(tag) {
19607             if (w.indexOf(tag) > -1) {
19608                 return;
19609             }
19610             if (this.cblack.indexOf(tag) > -1) {
19611                 return;
19612             }
19613             this.cblack.push(tag);
19614             
19615         }, this);
19616     },
19617     
19618     setStylesheets : function(stylesheets)
19619     {
19620         if(typeof(stylesheets) == 'string'){
19621             Roo.get(this.iframe.contentDocument.head).createChild({
19622                 tag : 'link',
19623                 rel : 'stylesheet',
19624                 type : 'text/css',
19625                 href : stylesheets
19626             });
19627             
19628             return;
19629         }
19630         var _this = this;
19631      
19632         Roo.each(stylesheets, function(s) {
19633             if(!s.length){
19634                 return;
19635             }
19636             
19637             Roo.get(_this.iframe.contentDocument.head).createChild({
19638                 tag : 'link',
19639                 rel : 'stylesheet',
19640                 type : 'text/css',
19641                 href : s
19642             });
19643         });
19644
19645         
19646     },
19647     
19648     removeStylesheets : function()
19649     {
19650         var _this = this;
19651         
19652         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19653             s.remove();
19654         });
19655     }
19656     
19657     // hide stuff that is not compatible
19658     /**
19659      * @event blur
19660      * @hide
19661      */
19662     /**
19663      * @event change
19664      * @hide
19665      */
19666     /**
19667      * @event focus
19668      * @hide
19669      */
19670     /**
19671      * @event specialkey
19672      * @hide
19673      */
19674     /**
19675      * @cfg {String} fieldClass @hide
19676      */
19677     /**
19678      * @cfg {String} focusClass @hide
19679      */
19680     /**
19681      * @cfg {String} autoCreate @hide
19682      */
19683     /**
19684      * @cfg {String} inputType @hide
19685      */
19686     /**
19687      * @cfg {String} invalidClass @hide
19688      */
19689     /**
19690      * @cfg {String} invalidText @hide
19691      */
19692     /**
19693      * @cfg {String} msgFx @hide
19694      */
19695     /**
19696      * @cfg {String} validateOnBlur @hide
19697      */
19698 });
19699
19700 Roo.HtmlEditorCore.white = [
19701         'area', 'br', 'img', 'input', 'hr', 'wbr',
19702         
19703        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19704        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19705        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19706        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19707        'table',   'ul',         'xmp', 
19708        
19709        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19710       'thead',   'tr', 
19711      
19712       'dir', 'menu', 'ol', 'ul', 'dl',
19713        
19714       'embed',  'object'
19715 ];
19716
19717
19718 Roo.HtmlEditorCore.black = [
19719     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19720         'applet', // 
19721         'base',   'basefont', 'bgsound', 'blink',  'body', 
19722         'frame',  'frameset', 'head',    'html',   'ilayer', 
19723         'iframe', 'layer',  'link',     'meta',    'object',   
19724         'script', 'style' ,'title',  'xml' // clean later..
19725 ];
19726 Roo.HtmlEditorCore.clean = [
19727     'script', 'style', 'title', 'xml'
19728 ];
19729 Roo.HtmlEditorCore.remove = [
19730     'font'
19731 ];
19732 // attributes..
19733
19734 Roo.HtmlEditorCore.ablack = [
19735     'on'
19736 ];
19737     
19738 Roo.HtmlEditorCore.aclean = [ 
19739     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19740 ];
19741
19742 // protocols..
19743 Roo.HtmlEditorCore.pwhite= [
19744         'http',  'https',  'mailto'
19745 ];
19746
19747 // white listed style attributes.
19748 Roo.HtmlEditorCore.cwhite= [
19749       //  'text-align', /// default is to allow most things..
19750       
19751          
19752 //        'font-size'//??
19753 ];
19754
19755 // black listed style attributes.
19756 Roo.HtmlEditorCore.cblack= [
19757       //  'font-size' -- this can be set by the project 
19758 ];
19759
19760
19761 Roo.HtmlEditorCore.swapCodes   =[ 
19762     [    8211, "--" ], 
19763     [    8212, "--" ], 
19764     [    8216,  "'" ],  
19765     [    8217, "'" ],  
19766     [    8220, '"' ],  
19767     [    8221, '"' ],  
19768     [    8226, "*" ],  
19769     [    8230, "..." ]
19770 ]; 
19771
19772     /*
19773  * - LGPL
19774  *
19775  * HtmlEditor
19776  * 
19777  */
19778
19779 /**
19780  * @class Roo.bootstrap.HtmlEditor
19781  * @extends Roo.bootstrap.TextArea
19782  * Bootstrap HtmlEditor class
19783
19784  * @constructor
19785  * Create a new HtmlEditor
19786  * @param {Object} config The config object
19787  */
19788
19789 Roo.bootstrap.HtmlEditor = function(config){
19790     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19791     if (!this.toolbars) {
19792         this.toolbars = [];
19793     }
19794     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19795     this.addEvents({
19796             /**
19797              * @event initialize
19798              * Fires when the editor is fully initialized (including the iframe)
19799              * @param {HtmlEditor} this
19800              */
19801             initialize: true,
19802             /**
19803              * @event activate
19804              * Fires when the editor is first receives the focus. Any insertion must wait
19805              * until after this event.
19806              * @param {HtmlEditor} this
19807              */
19808             activate: true,
19809              /**
19810              * @event beforesync
19811              * Fires before the textarea is updated with content from the editor iframe. Return false
19812              * to cancel the sync.
19813              * @param {HtmlEditor} this
19814              * @param {String} html
19815              */
19816             beforesync: true,
19817              /**
19818              * @event beforepush
19819              * Fires before the iframe editor is updated with content from the textarea. Return false
19820              * to cancel the push.
19821              * @param {HtmlEditor} this
19822              * @param {String} html
19823              */
19824             beforepush: true,
19825              /**
19826              * @event sync
19827              * Fires when the textarea is updated with content from the editor iframe.
19828              * @param {HtmlEditor} this
19829              * @param {String} html
19830              */
19831             sync: true,
19832              /**
19833              * @event push
19834              * Fires when the iframe editor is updated with content from the textarea.
19835              * @param {HtmlEditor} this
19836              * @param {String} html
19837              */
19838             push: true,
19839              /**
19840              * @event editmodechange
19841              * Fires when the editor switches edit modes
19842              * @param {HtmlEditor} this
19843              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19844              */
19845             editmodechange: true,
19846             /**
19847              * @event editorevent
19848              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19849              * @param {HtmlEditor} this
19850              */
19851             editorevent: true,
19852             /**
19853              * @event firstfocus
19854              * Fires when on first focus - needed by toolbars..
19855              * @param {HtmlEditor} this
19856              */
19857             firstfocus: true,
19858             /**
19859              * @event autosave
19860              * Auto save the htmlEditor value as a file into Events
19861              * @param {HtmlEditor} this
19862              */
19863             autosave: true,
19864             /**
19865              * @event savedpreview
19866              * preview the saved version of htmlEditor
19867              * @param {HtmlEditor} this
19868              */
19869             savedpreview: true
19870         });
19871 };
19872
19873
19874 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19875     
19876     
19877       /**
19878      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19879      */
19880     toolbars : false,
19881    
19882      /**
19883      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19884      *                        Roo.resizable.
19885      */
19886     resizable : false,
19887      /**
19888      * @cfg {Number} height (in pixels)
19889      */   
19890     height: 300,
19891    /**
19892      * @cfg {Number} width (in pixels)
19893      */   
19894     width: false,
19895     
19896     /**
19897      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19898      * 
19899      */
19900     stylesheets: false,
19901     
19902     // id of frame..
19903     frameId: false,
19904     
19905     // private properties
19906     validationEvent : false,
19907     deferHeight: true,
19908     initialized : false,
19909     activated : false,
19910     
19911     onFocus : Roo.emptyFn,
19912     iframePad:3,
19913     hideMode:'offsets',
19914     
19915     
19916     tbContainer : false,
19917     
19918     toolbarContainer :function() {
19919         return this.wrap.select('.x-html-editor-tb',true).first();
19920     },
19921
19922     /**
19923      * Protected method that will not generally be called directly. It
19924      * is called when the editor creates its toolbar. Override this method if you need to
19925      * add custom toolbar buttons.
19926      * @param {HtmlEditor} editor
19927      */
19928     createToolbar : function(){
19929         
19930         Roo.log("create toolbars");
19931         
19932         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19933         this.toolbars[0].render(this.toolbarContainer());
19934         
19935         return;
19936         
19937 //        if (!editor.toolbars || !editor.toolbars.length) {
19938 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19939 //        }
19940 //        
19941 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19942 //            editor.toolbars[i] = Roo.factory(
19943 //                    typeof(editor.toolbars[i]) == 'string' ?
19944 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19945 //                Roo.bootstrap.HtmlEditor);
19946 //            editor.toolbars[i].init(editor);
19947 //        }
19948     },
19949
19950      
19951     // private
19952     onRender : function(ct, position)
19953     {
19954        // Roo.log("Call onRender: " + this.xtype);
19955         var _t = this;
19956         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19957       
19958         this.wrap = this.inputEl().wrap({
19959             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19960         });
19961         
19962         this.editorcore.onRender(ct, position);
19963          
19964         if (this.resizable) {
19965             this.resizeEl = new Roo.Resizable(this.wrap, {
19966                 pinned : true,
19967                 wrap: true,
19968                 dynamic : true,
19969                 minHeight : this.height,
19970                 height: this.height,
19971                 handles : this.resizable,
19972                 width: this.width,
19973                 listeners : {
19974                     resize : function(r, w, h) {
19975                         _t.onResize(w,h); // -something
19976                     }
19977                 }
19978             });
19979             
19980         }
19981         this.createToolbar(this);
19982        
19983         
19984         if(!this.width && this.resizable){
19985             this.setSize(this.wrap.getSize());
19986         }
19987         if (this.resizeEl) {
19988             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19989             // should trigger onReize..
19990         }
19991         
19992     },
19993
19994     // private
19995     onResize : function(w, h)
19996     {
19997         Roo.log('resize: ' +w + ',' + h );
19998         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19999         var ew = false;
20000         var eh = false;
20001         
20002         if(this.inputEl() ){
20003             if(typeof w == 'number'){
20004                 var aw = w - this.wrap.getFrameWidth('lr');
20005                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20006                 ew = aw;
20007             }
20008             if(typeof h == 'number'){
20009                  var tbh = -11;  // fixme it needs to tool bar size!
20010                 for (var i =0; i < this.toolbars.length;i++) {
20011                     // fixme - ask toolbars for heights?
20012                     tbh += this.toolbars[i].el.getHeight();
20013                     //if (this.toolbars[i].footer) {
20014                     //    tbh += this.toolbars[i].footer.el.getHeight();
20015                     //}
20016                 }
20017               
20018                 
20019                 
20020                 
20021                 
20022                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20023                 ah -= 5; // knock a few pixes off for look..
20024                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20025                 var eh = ah;
20026             }
20027         }
20028         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20029         this.editorcore.onResize(ew,eh);
20030         
20031     },
20032
20033     /**
20034      * Toggles the editor between standard and source edit mode.
20035      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20036      */
20037     toggleSourceEdit : function(sourceEditMode)
20038     {
20039         this.editorcore.toggleSourceEdit(sourceEditMode);
20040         
20041         if(this.editorcore.sourceEditMode){
20042             Roo.log('editor - showing textarea');
20043             
20044 //            Roo.log('in');
20045 //            Roo.log(this.syncValue());
20046             this.syncValue();
20047             this.inputEl().removeClass(['hide', 'x-hidden']);
20048             this.inputEl().dom.removeAttribute('tabIndex');
20049             this.inputEl().focus();
20050         }else{
20051             Roo.log('editor - hiding textarea');
20052 //            Roo.log('out')
20053 //            Roo.log(this.pushValue()); 
20054             this.pushValue();
20055             
20056             this.inputEl().addClass(['hide', 'x-hidden']);
20057             this.inputEl().dom.setAttribute('tabIndex', -1);
20058             //this.deferFocus();
20059         }
20060          
20061         if(this.resizable){
20062             this.setSize(this.wrap.getSize());
20063         }
20064         
20065         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20066     },
20067  
20068     // private (for BoxComponent)
20069     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20070
20071     // private (for BoxComponent)
20072     getResizeEl : function(){
20073         return this.wrap;
20074     },
20075
20076     // private (for BoxComponent)
20077     getPositionEl : function(){
20078         return this.wrap;
20079     },
20080
20081     // private
20082     initEvents : function(){
20083         this.originalValue = this.getValue();
20084     },
20085
20086 //    /**
20087 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20088 //     * @method
20089 //     */
20090 //    markInvalid : Roo.emptyFn,
20091 //    /**
20092 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20093 //     * @method
20094 //     */
20095 //    clearInvalid : Roo.emptyFn,
20096
20097     setValue : function(v){
20098         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20099         this.editorcore.pushValue();
20100     },
20101
20102      
20103     // private
20104     deferFocus : function(){
20105         this.focus.defer(10, this);
20106     },
20107
20108     // doc'ed in Field
20109     focus : function(){
20110         this.editorcore.focus();
20111         
20112     },
20113       
20114
20115     // private
20116     onDestroy : function(){
20117         
20118         
20119         
20120         if(this.rendered){
20121             
20122             for (var i =0; i < this.toolbars.length;i++) {
20123                 // fixme - ask toolbars for heights?
20124                 this.toolbars[i].onDestroy();
20125             }
20126             
20127             this.wrap.dom.innerHTML = '';
20128             this.wrap.remove();
20129         }
20130     },
20131
20132     // private
20133     onFirstFocus : function(){
20134         //Roo.log("onFirstFocus");
20135         this.editorcore.onFirstFocus();
20136          for (var i =0; i < this.toolbars.length;i++) {
20137             this.toolbars[i].onFirstFocus();
20138         }
20139         
20140     },
20141     
20142     // private
20143     syncValue : function()
20144     {   
20145         this.editorcore.syncValue();
20146     },
20147     
20148     pushValue : function()
20149     {   
20150         this.editorcore.pushValue();
20151     }
20152      
20153     
20154     // hide stuff that is not compatible
20155     /**
20156      * @event blur
20157      * @hide
20158      */
20159     /**
20160      * @event change
20161      * @hide
20162      */
20163     /**
20164      * @event focus
20165      * @hide
20166      */
20167     /**
20168      * @event specialkey
20169      * @hide
20170      */
20171     /**
20172      * @cfg {String} fieldClass @hide
20173      */
20174     /**
20175      * @cfg {String} focusClass @hide
20176      */
20177     /**
20178      * @cfg {String} autoCreate @hide
20179      */
20180     /**
20181      * @cfg {String} inputType @hide
20182      */
20183     /**
20184      * @cfg {String} invalidClass @hide
20185      */
20186     /**
20187      * @cfg {String} invalidText @hide
20188      */
20189     /**
20190      * @cfg {String} msgFx @hide
20191      */
20192     /**
20193      * @cfg {String} validateOnBlur @hide
20194      */
20195 });
20196  
20197     
20198    
20199    
20200    
20201       
20202 Roo.namespace('Roo.bootstrap.htmleditor');
20203 /**
20204  * @class Roo.bootstrap.HtmlEditorToolbar1
20205  * Basic Toolbar
20206  * 
20207  * Usage:
20208  *
20209  new Roo.bootstrap.HtmlEditor({
20210     ....
20211     toolbars : [
20212         new Roo.bootstrap.HtmlEditorToolbar1({
20213             disable : { fonts: 1 , format: 1, ..., ... , ...],
20214             btns : [ .... ]
20215         })
20216     }
20217      
20218  * 
20219  * @cfg {Object} disable List of elements to disable..
20220  * @cfg {Array} btns List of additional buttons.
20221  * 
20222  * 
20223  * NEEDS Extra CSS? 
20224  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20225  */
20226  
20227 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20228 {
20229     
20230     Roo.apply(this, config);
20231     
20232     // default disabled, based on 'good practice'..
20233     this.disable = this.disable || {};
20234     Roo.applyIf(this.disable, {
20235         fontSize : true,
20236         colors : true,
20237         specialElements : true
20238     });
20239     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20240     
20241     this.editor = config.editor;
20242     this.editorcore = config.editor.editorcore;
20243     
20244     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20245     
20246     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20247     // dont call parent... till later.
20248 }
20249 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20250      
20251     bar : true,
20252     
20253     editor : false,
20254     editorcore : false,
20255     
20256     
20257     formats : [
20258         "p" ,  
20259         "h1","h2","h3","h4","h5","h6", 
20260         "pre", "code", 
20261         "abbr", "acronym", "address", "cite", "samp", "var",
20262         'div','span'
20263     ],
20264     
20265     onRender : function(ct, position)
20266     {
20267        // Roo.log("Call onRender: " + this.xtype);
20268         
20269        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20270        Roo.log(this.el);
20271        this.el.dom.style.marginBottom = '0';
20272        var _this = this;
20273        var editorcore = this.editorcore;
20274        var editor= this.editor;
20275        
20276        var children = [];
20277        var btn = function(id,cmd , toggle, handler){
20278        
20279             var  event = toggle ? 'toggle' : 'click';
20280        
20281             var a = {
20282                 size : 'sm',
20283                 xtype: 'Button',
20284                 xns: Roo.bootstrap,
20285                 glyphicon : id,
20286                 cmd : id || cmd,
20287                 enableToggle:toggle !== false,
20288                 //html : 'submit'
20289                 pressed : toggle ? false : null,
20290                 listeners : {}
20291             }
20292             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20293                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20294             }
20295             children.push(a);
20296             return a;
20297        }
20298         
20299         var style = {
20300                 xtype: 'Button',
20301                 size : 'sm',
20302                 xns: Roo.bootstrap,
20303                 glyphicon : 'font',
20304                 //html : 'submit'
20305                 menu : {
20306                     xtype: 'Menu',
20307                     xns: Roo.bootstrap,
20308                     items:  []
20309                 }
20310         };
20311         Roo.each(this.formats, function(f) {
20312             style.menu.items.push({
20313                 xtype :'MenuItem',
20314                 xns: Roo.bootstrap,
20315                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20316                 tagname : f,
20317                 listeners : {
20318                     click : function()
20319                     {
20320                         editorcore.insertTag(this.tagname);
20321                         editor.focus();
20322                     }
20323                 }
20324                 
20325             });
20326         });
20327          children.push(style);   
20328             
20329             
20330         btn('bold',false,true);
20331         btn('italic',false,true);
20332         btn('align-left', 'justifyleft',true);
20333         btn('align-center', 'justifycenter',true);
20334         btn('align-right' , 'justifyright',true);
20335         btn('link', false, false, function(btn) {
20336             //Roo.log("create link?");
20337             var url = prompt(this.createLinkText, this.defaultLinkValue);
20338             if(url && url != 'http:/'+'/'){
20339                 this.editorcore.relayCmd('createlink', url);
20340             }
20341         }),
20342         btn('list','insertunorderedlist',true);
20343         btn('pencil', false,true, function(btn){
20344                 Roo.log(this);
20345                 
20346                 this.toggleSourceEdit(btn.pressed);
20347         });
20348         /*
20349         var cog = {
20350                 xtype: 'Button',
20351                 size : 'sm',
20352                 xns: Roo.bootstrap,
20353                 glyphicon : 'cog',
20354                 //html : 'submit'
20355                 menu : {
20356                     xtype: 'Menu',
20357                     xns: Roo.bootstrap,
20358                     items:  []
20359                 }
20360         };
20361         
20362         cog.menu.items.push({
20363             xtype :'MenuItem',
20364             xns: Roo.bootstrap,
20365             html : Clean styles,
20366             tagname : f,
20367             listeners : {
20368                 click : function()
20369                 {
20370                     editorcore.insertTag(this.tagname);
20371                     editor.focus();
20372                 }
20373             }
20374             
20375         });
20376        */
20377         
20378          
20379        this.xtype = 'NavSimplebar';
20380         
20381         for(var i=0;i< children.length;i++) {
20382             
20383             this.buttons.add(this.addxtypeChild(children[i]));
20384             
20385         }
20386         
20387         editor.on('editorevent', this.updateToolbar, this);
20388     },
20389     onBtnClick : function(id)
20390     {
20391        this.editorcore.relayCmd(id);
20392        this.editorcore.focus();
20393     },
20394     
20395     /**
20396      * Protected method that will not generally be called directly. It triggers
20397      * a toolbar update by reading the markup state of the current selection in the editor.
20398      */
20399     updateToolbar: function(){
20400
20401         if(!this.editorcore.activated){
20402             this.editor.onFirstFocus(); // is this neeed?
20403             return;
20404         }
20405
20406         var btns = this.buttons; 
20407         var doc = this.editorcore.doc;
20408         btns.get('bold').setActive(doc.queryCommandState('bold'));
20409         btns.get('italic').setActive(doc.queryCommandState('italic'));
20410         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20411         
20412         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20413         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20414         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20415         
20416         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20417         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20418          /*
20419         
20420         var ans = this.editorcore.getAllAncestors();
20421         if (this.formatCombo) {
20422             
20423             
20424             var store = this.formatCombo.store;
20425             this.formatCombo.setValue("");
20426             for (var i =0; i < ans.length;i++) {
20427                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20428                     // select it..
20429                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20430                     break;
20431                 }
20432             }
20433         }
20434         
20435         
20436         
20437         // hides menus... - so this cant be on a menu...
20438         Roo.bootstrap.MenuMgr.hideAll();
20439         */
20440         Roo.bootstrap.MenuMgr.hideAll();
20441         //this.editorsyncValue();
20442     },
20443     onFirstFocus: function() {
20444         this.buttons.each(function(item){
20445            item.enable();
20446         });
20447     },
20448     toggleSourceEdit : function(sourceEditMode){
20449         
20450           
20451         if(sourceEditMode){
20452             Roo.log("disabling buttons");
20453            this.buttons.each( function(item){
20454                 if(item.cmd != 'pencil'){
20455                     item.disable();
20456                 }
20457             });
20458           
20459         }else{
20460             Roo.log("enabling buttons");
20461             if(this.editorcore.initialized){
20462                 this.buttons.each( function(item){
20463                     item.enable();
20464                 });
20465             }
20466             
20467         }
20468         Roo.log("calling toggole on editor");
20469         // tell the editor that it's been pressed..
20470         this.editor.toggleSourceEdit(sourceEditMode);
20471        
20472     }
20473 });
20474
20475
20476
20477
20478
20479 /**
20480  * @class Roo.bootstrap.Table.AbstractSelectionModel
20481  * @extends Roo.util.Observable
20482  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20483  * implemented by descendant classes.  This class should not be directly instantiated.
20484  * @constructor
20485  */
20486 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20487     this.locked = false;
20488     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20489 };
20490
20491
20492 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20493     /** @ignore Called by the grid automatically. Do not call directly. */
20494     init : function(grid){
20495         this.grid = grid;
20496         this.initEvents();
20497     },
20498
20499     /**
20500      * Locks the selections.
20501      */
20502     lock : function(){
20503         this.locked = true;
20504     },
20505
20506     /**
20507      * Unlocks the selections.
20508      */
20509     unlock : function(){
20510         this.locked = false;
20511     },
20512
20513     /**
20514      * Returns true if the selections are locked.
20515      * @return {Boolean}
20516      */
20517     isLocked : function(){
20518         return this.locked;
20519     }
20520 });
20521 /**
20522  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20523  * @class Roo.bootstrap.Table.RowSelectionModel
20524  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20525  * It supports multiple selections and keyboard selection/navigation. 
20526  * @constructor
20527  * @param {Object} config
20528  */
20529
20530 Roo.bootstrap.Table.RowSelectionModel = function(config){
20531     Roo.apply(this, config);
20532     this.selections = new Roo.util.MixedCollection(false, function(o){
20533         return o.id;
20534     });
20535
20536     this.last = false;
20537     this.lastActive = false;
20538
20539     this.addEvents({
20540         /**
20541              * @event selectionchange
20542              * Fires when the selection changes
20543              * @param {SelectionModel} this
20544              */
20545             "selectionchange" : true,
20546         /**
20547              * @event afterselectionchange
20548              * Fires after the selection changes (eg. by key press or clicking)
20549              * @param {SelectionModel} this
20550              */
20551             "afterselectionchange" : true,
20552         /**
20553              * @event beforerowselect
20554              * Fires when a row is selected being selected, return false to cancel.
20555              * @param {SelectionModel} this
20556              * @param {Number} rowIndex The selected index
20557              * @param {Boolean} keepExisting False if other selections will be cleared
20558              */
20559             "beforerowselect" : true,
20560         /**
20561              * @event rowselect
20562              * Fires when a row is selected.
20563              * @param {SelectionModel} this
20564              * @param {Number} rowIndex The selected index
20565              * @param {Roo.data.Record} r The record
20566              */
20567             "rowselect" : true,
20568         /**
20569              * @event rowdeselect
20570              * Fires when a row is deselected.
20571              * @param {SelectionModel} this
20572              * @param {Number} rowIndex The selected index
20573              */
20574         "rowdeselect" : true
20575     });
20576     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20577     this.locked = false;
20578 };
20579
20580 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20581     /**
20582      * @cfg {Boolean} singleSelect
20583      * True to allow selection of only one row at a time (defaults to false)
20584      */
20585     singleSelect : false,
20586
20587     // private
20588     initEvents : function(){
20589
20590         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20591             this.grid.on("mousedown", this.handleMouseDown, this);
20592         }else{ // allow click to work like normal
20593             this.grid.on("rowclick", this.handleDragableRowClick, this);
20594         }
20595
20596         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20597             "up" : function(e){
20598                 if(!e.shiftKey){
20599                     this.selectPrevious(e.shiftKey);
20600                 }else if(this.last !== false && this.lastActive !== false){
20601                     var last = this.last;
20602                     this.selectRange(this.last,  this.lastActive-1);
20603                     this.grid.getView().focusRow(this.lastActive);
20604                     if(last !== false){
20605                         this.last = last;
20606                     }
20607                 }else{
20608                     this.selectFirstRow();
20609                 }
20610                 this.fireEvent("afterselectionchange", this);
20611             },
20612             "down" : function(e){
20613                 if(!e.shiftKey){
20614                     this.selectNext(e.shiftKey);
20615                 }else if(this.last !== false && this.lastActive !== false){
20616                     var last = this.last;
20617                     this.selectRange(this.last,  this.lastActive+1);
20618                     this.grid.getView().focusRow(this.lastActive);
20619                     if(last !== false){
20620                         this.last = last;
20621                     }
20622                 }else{
20623                     this.selectFirstRow();
20624                 }
20625                 this.fireEvent("afterselectionchange", this);
20626             },
20627             scope: this
20628         });
20629
20630         var view = this.grid.view;
20631         view.on("refresh", this.onRefresh, this);
20632         view.on("rowupdated", this.onRowUpdated, this);
20633         view.on("rowremoved", this.onRemove, this);
20634     },
20635
20636     // private
20637     onRefresh : function(){
20638         var ds = this.grid.dataSource, i, v = this.grid.view;
20639         var s = this.selections;
20640         s.each(function(r){
20641             if((i = ds.indexOfId(r.id)) != -1){
20642                 v.onRowSelect(i);
20643             }else{
20644                 s.remove(r);
20645             }
20646         });
20647     },
20648
20649     // private
20650     onRemove : function(v, index, r){
20651         this.selections.remove(r);
20652     },
20653
20654     // private
20655     onRowUpdated : function(v, index, r){
20656         if(this.isSelected(r)){
20657             v.onRowSelect(index);
20658         }
20659     },
20660
20661     /**
20662      * Select records.
20663      * @param {Array} records The records to select
20664      * @param {Boolean} keepExisting (optional) True to keep existing selections
20665      */
20666     selectRecords : function(records, keepExisting){
20667         if(!keepExisting){
20668             this.clearSelections();
20669         }
20670         var ds = this.grid.dataSource;
20671         for(var i = 0, len = records.length; i < len; i++){
20672             this.selectRow(ds.indexOf(records[i]), true);
20673         }
20674     },
20675
20676     /**
20677      * Gets the number of selected rows.
20678      * @return {Number}
20679      */
20680     getCount : function(){
20681         return this.selections.length;
20682     },
20683
20684     /**
20685      * Selects the first row in the grid.
20686      */
20687     selectFirstRow : function(){
20688         this.selectRow(0);
20689     },
20690
20691     /**
20692      * Select the last row.
20693      * @param {Boolean} keepExisting (optional) True to keep existing selections
20694      */
20695     selectLastRow : function(keepExisting){
20696         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20697     },
20698
20699     /**
20700      * Selects the row immediately following the last selected row.
20701      * @param {Boolean} keepExisting (optional) True to keep existing selections
20702      */
20703     selectNext : function(keepExisting){
20704         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20705             this.selectRow(this.last+1, keepExisting);
20706             this.grid.getView().focusRow(this.last);
20707         }
20708     },
20709
20710     /**
20711      * Selects the row that precedes the last selected row.
20712      * @param {Boolean} keepExisting (optional) True to keep existing selections
20713      */
20714     selectPrevious : function(keepExisting){
20715         if(this.last){
20716             this.selectRow(this.last-1, keepExisting);
20717             this.grid.getView().focusRow(this.last);
20718         }
20719     },
20720
20721     /**
20722      * Returns the selected records
20723      * @return {Array} Array of selected records
20724      */
20725     getSelections : function(){
20726         return [].concat(this.selections.items);
20727     },
20728
20729     /**
20730      * Returns the first selected record.
20731      * @return {Record}
20732      */
20733     getSelected : function(){
20734         return this.selections.itemAt(0);
20735     },
20736
20737
20738     /**
20739      * Clears all selections.
20740      */
20741     clearSelections : function(fast){
20742         if(this.locked) return;
20743         if(fast !== true){
20744             var ds = this.grid.dataSource;
20745             var s = this.selections;
20746             s.each(function(r){
20747                 this.deselectRow(ds.indexOfId(r.id));
20748             }, this);
20749             s.clear();
20750         }else{
20751             this.selections.clear();
20752         }
20753         this.last = false;
20754     },
20755
20756
20757     /**
20758      * Selects all rows.
20759      */
20760     selectAll : function(){
20761         if(this.locked) return;
20762         this.selections.clear();
20763         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20764             this.selectRow(i, true);
20765         }
20766     },
20767
20768     /**
20769      * Returns True if there is a selection.
20770      * @return {Boolean}
20771      */
20772     hasSelection : function(){
20773         return this.selections.length > 0;
20774     },
20775
20776     /**
20777      * Returns True if the specified row is selected.
20778      * @param {Number/Record} record The record or index of the record to check
20779      * @return {Boolean}
20780      */
20781     isSelected : function(index){
20782         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20783         return (r && this.selections.key(r.id) ? true : false);
20784     },
20785
20786     /**
20787      * Returns True if the specified record id is selected.
20788      * @param {String} id The id of record to check
20789      * @return {Boolean}
20790      */
20791     isIdSelected : function(id){
20792         return (this.selections.key(id) ? true : false);
20793     },
20794
20795     // private
20796     handleMouseDown : function(e, t){
20797         var view = this.grid.getView(), rowIndex;
20798         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20799             return;
20800         };
20801         if(e.shiftKey && this.last !== false){
20802             var last = this.last;
20803             this.selectRange(last, rowIndex, e.ctrlKey);
20804             this.last = last; // reset the last
20805             view.focusRow(rowIndex);
20806         }else{
20807             var isSelected = this.isSelected(rowIndex);
20808             if(e.button !== 0 && isSelected){
20809                 view.focusRow(rowIndex);
20810             }else if(e.ctrlKey && isSelected){
20811                 this.deselectRow(rowIndex);
20812             }else if(!isSelected){
20813                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20814                 view.focusRow(rowIndex);
20815             }
20816         }
20817         this.fireEvent("afterselectionchange", this);
20818     },
20819     // private
20820     handleDragableRowClick :  function(grid, rowIndex, e) 
20821     {
20822         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20823             this.selectRow(rowIndex, false);
20824             grid.view.focusRow(rowIndex);
20825              this.fireEvent("afterselectionchange", this);
20826         }
20827     },
20828     
20829     /**
20830      * Selects multiple rows.
20831      * @param {Array} rows Array of the indexes of the row to select
20832      * @param {Boolean} keepExisting (optional) True to keep existing selections
20833      */
20834     selectRows : function(rows, keepExisting){
20835         if(!keepExisting){
20836             this.clearSelections();
20837         }
20838         for(var i = 0, len = rows.length; i < len; i++){
20839             this.selectRow(rows[i], true);
20840         }
20841     },
20842
20843     /**
20844      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20845      * @param {Number} startRow The index of the first row in the range
20846      * @param {Number} endRow The index of the last row in the range
20847      * @param {Boolean} keepExisting (optional) True to retain existing selections
20848      */
20849     selectRange : function(startRow, endRow, keepExisting){
20850         if(this.locked) return;
20851         if(!keepExisting){
20852             this.clearSelections();
20853         }
20854         if(startRow <= endRow){
20855             for(var i = startRow; i <= endRow; i++){
20856                 this.selectRow(i, true);
20857             }
20858         }else{
20859             for(var i = startRow; i >= endRow; i--){
20860                 this.selectRow(i, true);
20861             }
20862         }
20863     },
20864
20865     /**
20866      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20867      * @param {Number} startRow The index of the first row in the range
20868      * @param {Number} endRow The index of the last row in the range
20869      */
20870     deselectRange : function(startRow, endRow, preventViewNotify){
20871         if(this.locked) return;
20872         for(var i = startRow; i <= endRow; i++){
20873             this.deselectRow(i, preventViewNotify);
20874         }
20875     },
20876
20877     /**
20878      * Selects a row.
20879      * @param {Number} row The index of the row to select
20880      * @param {Boolean} keepExisting (optional) True to keep existing selections
20881      */
20882     selectRow : function(index, keepExisting, preventViewNotify){
20883         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20884         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20885             if(!keepExisting || this.singleSelect){
20886                 this.clearSelections();
20887             }
20888             var r = this.grid.dataSource.getAt(index);
20889             this.selections.add(r);
20890             this.last = this.lastActive = index;
20891             if(!preventViewNotify){
20892                 this.grid.getView().onRowSelect(index);
20893             }
20894             this.fireEvent("rowselect", this, index, r);
20895             this.fireEvent("selectionchange", this);
20896         }
20897     },
20898
20899     /**
20900      * Deselects a row.
20901      * @param {Number} row The index of the row to deselect
20902      */
20903     deselectRow : function(index, preventViewNotify){
20904         if(this.locked) return;
20905         if(this.last == index){
20906             this.last = false;
20907         }
20908         if(this.lastActive == index){
20909             this.lastActive = false;
20910         }
20911         var r = this.grid.dataSource.getAt(index);
20912         this.selections.remove(r);
20913         if(!preventViewNotify){
20914             this.grid.getView().onRowDeselect(index);
20915         }
20916         this.fireEvent("rowdeselect", this, index);
20917         this.fireEvent("selectionchange", this);
20918     },
20919
20920     // private
20921     restoreLast : function(){
20922         if(this._last){
20923             this.last = this._last;
20924         }
20925     },
20926
20927     // private
20928     acceptsNav : function(row, col, cm){
20929         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20930     },
20931
20932     // private
20933     onEditorKey : function(field, e){
20934         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20935         if(k == e.TAB){
20936             e.stopEvent();
20937             ed.completeEdit();
20938             if(e.shiftKey){
20939                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20940             }else{
20941                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20942             }
20943         }else if(k == e.ENTER && !e.ctrlKey){
20944             e.stopEvent();
20945             ed.completeEdit();
20946             if(e.shiftKey){
20947                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20948             }else{
20949                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20950             }
20951         }else if(k == e.ESC){
20952             ed.cancelEdit();
20953         }
20954         if(newCell){
20955             g.startEditing(newCell[0], newCell[1]);
20956         }
20957     }
20958 });/*
20959  * Based on:
20960  * Ext JS Library 1.1.1
20961  * Copyright(c) 2006-2007, Ext JS, LLC.
20962  *
20963  * Originally Released Under LGPL - original licence link has changed is not relivant.
20964  *
20965  * Fork - LGPL
20966  * <script type="text/javascript">
20967  */
20968  
20969 /**
20970  * @class Roo.bootstrap.PagingToolbar
20971  * @extends Roo.Row
20972  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20973  * @constructor
20974  * Create a new PagingToolbar
20975  * @param {Object} config The config object
20976  */
20977 Roo.bootstrap.PagingToolbar = function(config)
20978 {
20979     // old args format still supported... - xtype is prefered..
20980         // created from xtype...
20981     var ds = config.dataSource;
20982     this.toolbarItems = [];
20983     if (config.items) {
20984         this.toolbarItems = config.items;
20985 //        config.items = [];
20986     }
20987     
20988     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20989     this.ds = ds;
20990     this.cursor = 0;
20991     if (ds) { 
20992         this.bind(ds);
20993     }
20994     
20995     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20996     
20997 };
20998
20999 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21000     /**
21001      * @cfg {Roo.data.Store} dataSource
21002      * The underlying data store providing the paged data
21003      */
21004     /**
21005      * @cfg {String/HTMLElement/Element} container
21006      * container The id or element that will contain the toolbar
21007      */
21008     /**
21009      * @cfg {Boolean} displayInfo
21010      * True to display the displayMsg (defaults to false)
21011      */
21012     /**
21013      * @cfg {Number} pageSize
21014      * The number of records to display per page (defaults to 20)
21015      */
21016     pageSize: 20,
21017     /**
21018      * @cfg {String} displayMsg
21019      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21020      */
21021     displayMsg : 'Displaying {0} - {1} of {2}',
21022     /**
21023      * @cfg {String} emptyMsg
21024      * The message to display when no records are found (defaults to "No data to display")
21025      */
21026     emptyMsg : 'No data to display',
21027     /**
21028      * Customizable piece of the default paging text (defaults to "Page")
21029      * @type String
21030      */
21031     beforePageText : "Page",
21032     /**
21033      * Customizable piece of the default paging text (defaults to "of %0")
21034      * @type String
21035      */
21036     afterPageText : "of {0}",
21037     /**
21038      * Customizable piece of the default paging text (defaults to "First Page")
21039      * @type String
21040      */
21041     firstText : "First Page",
21042     /**
21043      * Customizable piece of the default paging text (defaults to "Previous Page")
21044      * @type String
21045      */
21046     prevText : "Previous Page",
21047     /**
21048      * Customizable piece of the default paging text (defaults to "Next Page")
21049      * @type String
21050      */
21051     nextText : "Next Page",
21052     /**
21053      * Customizable piece of the default paging text (defaults to "Last Page")
21054      * @type String
21055      */
21056     lastText : "Last Page",
21057     /**
21058      * Customizable piece of the default paging text (defaults to "Refresh")
21059      * @type String
21060      */
21061     refreshText : "Refresh",
21062
21063     buttons : false,
21064     // private
21065     onRender : function(ct, position) 
21066     {
21067         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21068         this.navgroup.parentId = this.id;
21069         this.navgroup.onRender(this.el, null);
21070         // add the buttons to the navgroup
21071         
21072         if(this.displayInfo){
21073             Roo.log(this.el.select('ul.navbar-nav',true).first());
21074             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21075             this.displayEl = this.el.select('.x-paging-info', true).first();
21076 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21077 //            this.displayEl = navel.el.select('span',true).first();
21078         }
21079         
21080         var _this = this;
21081         
21082         if(this.buttons){
21083             Roo.each(_this.buttons, function(e){
21084                Roo.factory(e).onRender(_this.el, null);
21085             });
21086         }
21087             
21088         Roo.each(_this.toolbarItems, function(e) {
21089             _this.navgroup.addItem(e);
21090         });
21091         
21092         
21093         this.first = this.navgroup.addItem({
21094             tooltip: this.firstText,
21095             cls: "prev",
21096             icon : 'fa fa-backward',
21097             disabled: true,
21098             preventDefault: true,
21099             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21100         });
21101         
21102         this.prev =  this.navgroup.addItem({
21103             tooltip: this.prevText,
21104             cls: "prev",
21105             icon : 'fa fa-step-backward',
21106             disabled: true,
21107             preventDefault: true,
21108             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21109         });
21110     //this.addSeparator();
21111         
21112         
21113         var field = this.navgroup.addItem( {
21114             tagtype : 'span',
21115             cls : 'x-paging-position',
21116             
21117             html : this.beforePageText  +
21118                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21119                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21120          } ); //?? escaped?
21121         
21122         this.field = field.el.select('input', true).first();
21123         this.field.on("keydown", this.onPagingKeydown, this);
21124         this.field.on("focus", function(){this.dom.select();});
21125     
21126     
21127         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21128         //this.field.setHeight(18);
21129         //this.addSeparator();
21130         this.next = this.navgroup.addItem({
21131             tooltip: this.nextText,
21132             cls: "next",
21133             html : ' <i class="fa fa-step-forward">',
21134             disabled: true,
21135             preventDefault: true,
21136             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21137         });
21138         this.last = this.navgroup.addItem({
21139             tooltip: this.lastText,
21140             icon : 'fa fa-forward',
21141             cls: "next",
21142             disabled: true,
21143             preventDefault: true,
21144             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21145         });
21146     //this.addSeparator();
21147         this.loading = this.navgroup.addItem({
21148             tooltip: this.refreshText,
21149             icon: 'fa fa-refresh',
21150             preventDefault: true,
21151             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21152         });
21153
21154     },
21155
21156     // private
21157     updateInfo : function(){
21158         if(this.displayEl){
21159             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21160             var msg = count == 0 ?
21161                 this.emptyMsg :
21162                 String.format(
21163                     this.displayMsg,
21164                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21165                 );
21166             this.displayEl.update(msg);
21167         }
21168     },
21169
21170     // private
21171     onLoad : function(ds, r, o){
21172        this.cursor = o.params ? o.params.start : 0;
21173        var d = this.getPageData(),
21174             ap = d.activePage,
21175             ps = d.pages;
21176         
21177        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21178        this.field.dom.value = ap;
21179        this.first.setDisabled(ap == 1);
21180        this.prev.setDisabled(ap == 1);
21181        this.next.setDisabled(ap == ps);
21182        this.last.setDisabled(ap == ps);
21183        this.loading.enable();
21184        this.updateInfo();
21185     },
21186
21187     // private
21188     getPageData : function(){
21189         var total = this.ds.getTotalCount();
21190         return {
21191             total : total,
21192             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21193             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21194         };
21195     },
21196
21197     // private
21198     onLoadError : function(){
21199         this.loading.enable();
21200     },
21201
21202     // private
21203     onPagingKeydown : function(e){
21204         var k = e.getKey();
21205         var d = this.getPageData();
21206         if(k == e.RETURN){
21207             var v = this.field.dom.value, pageNum;
21208             if(!v || isNaN(pageNum = parseInt(v, 10))){
21209                 this.field.dom.value = d.activePage;
21210                 return;
21211             }
21212             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21213             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21214             e.stopEvent();
21215         }
21216         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))
21217         {
21218           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21219           this.field.dom.value = pageNum;
21220           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21221           e.stopEvent();
21222         }
21223         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21224         {
21225           var v = this.field.dom.value, pageNum; 
21226           var increment = (e.shiftKey) ? 10 : 1;
21227           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21228             increment *= -1;
21229           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21230             this.field.dom.value = d.activePage;
21231             return;
21232           }
21233           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21234           {
21235             this.field.dom.value = parseInt(v, 10) + increment;
21236             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21237             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21238           }
21239           e.stopEvent();
21240         }
21241     },
21242
21243     // private
21244     beforeLoad : function(){
21245         if(this.loading){
21246             this.loading.disable();
21247         }
21248     },
21249
21250     // private
21251     onClick : function(which){
21252         
21253         var ds = this.ds;
21254         if (!ds) {
21255             return;
21256         }
21257         
21258         switch(which){
21259             case "first":
21260                 ds.load({params:{start: 0, limit: this.pageSize}});
21261             break;
21262             case "prev":
21263                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21264             break;
21265             case "next":
21266                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21267             break;
21268             case "last":
21269                 var total = ds.getTotalCount();
21270                 var extra = total % this.pageSize;
21271                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21272                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21273             break;
21274             case "refresh":
21275                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21276             break;
21277         }
21278     },
21279
21280     /**
21281      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21282      * @param {Roo.data.Store} store The data store to unbind
21283      */
21284     unbind : function(ds){
21285         ds.un("beforeload", this.beforeLoad, this);
21286         ds.un("load", this.onLoad, this);
21287         ds.un("loadexception", this.onLoadError, this);
21288         ds.un("remove", this.updateInfo, this);
21289         ds.un("add", this.updateInfo, this);
21290         this.ds = undefined;
21291     },
21292
21293     /**
21294      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21295      * @param {Roo.data.Store} store The data store to bind
21296      */
21297     bind : function(ds){
21298         ds.on("beforeload", this.beforeLoad, this);
21299         ds.on("load", this.onLoad, this);
21300         ds.on("loadexception", this.onLoadError, this);
21301         ds.on("remove", this.updateInfo, this);
21302         ds.on("add", this.updateInfo, this);
21303         this.ds = ds;
21304     }
21305 });/*
21306  * - LGPL
21307  *
21308  * element
21309  * 
21310  */
21311
21312 /**
21313  * @class Roo.bootstrap.MessageBar
21314  * @extends Roo.bootstrap.Component
21315  * Bootstrap MessageBar class
21316  * @cfg {String} html contents of the MessageBar
21317  * @cfg {String} weight (info | success | warning | danger) default info
21318  * @cfg {String} beforeClass insert the bar before the given class
21319  * @cfg {Boolean} closable (true | false) default false
21320  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21321  * 
21322  * @constructor
21323  * Create a new Element
21324  * @param {Object} config The config object
21325  */
21326
21327 Roo.bootstrap.MessageBar = function(config){
21328     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21329 };
21330
21331 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21332     
21333     html: '',
21334     weight: 'info',
21335     closable: false,
21336     fixed: false,
21337     beforeClass: 'bootstrap-sticky-wrap',
21338     
21339     getAutoCreate : function(){
21340         
21341         var cfg = {
21342             tag: 'div',
21343             cls: 'alert alert-dismissable alert-' + this.weight,
21344             cn: [
21345                 {
21346                     tag: 'span',
21347                     cls: 'message',
21348                     html: this.html || ''
21349                 }
21350             ]
21351         }
21352         
21353         if(this.fixed){
21354             cfg.cls += ' alert-messages-fixed';
21355         }
21356         
21357         if(this.closable){
21358             cfg.cn.push({
21359                 tag: 'button',
21360                 cls: 'close',
21361                 html: 'x'
21362             });
21363         }
21364         
21365         return cfg;
21366     },
21367     
21368     onRender : function(ct, position)
21369     {
21370         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21371         
21372         if(!this.el){
21373             var cfg = Roo.apply({},  this.getAutoCreate());
21374             cfg.id = Roo.id();
21375             
21376             if (this.cls) {
21377                 cfg.cls += ' ' + this.cls;
21378             }
21379             if (this.style) {
21380                 cfg.style = this.style;
21381             }
21382             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21383             
21384             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21385         }
21386         
21387         this.el.select('>button.close').on('click', this.hide, this);
21388         
21389     },
21390     
21391     show : function()
21392     {
21393         if (!this.rendered) {
21394             this.render();
21395         }
21396         
21397         this.el.show();
21398         
21399         this.fireEvent('show', this);
21400         
21401     },
21402     
21403     hide : function()
21404     {
21405         if (!this.rendered) {
21406             this.render();
21407         }
21408         
21409         this.el.hide();
21410         
21411         this.fireEvent('hide', this);
21412     },
21413     
21414     update : function()
21415     {
21416 //        var e = this.el.dom.firstChild;
21417 //        
21418 //        if(this.closable){
21419 //            e = e.nextSibling;
21420 //        }
21421 //        
21422 //        e.data = this.html || '';
21423
21424         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21425     }
21426    
21427 });
21428
21429  
21430
21431      /*
21432  * - LGPL
21433  *
21434  * Graph
21435  * 
21436  */
21437
21438
21439 /**
21440  * @class Roo.bootstrap.Graph
21441  * @extends Roo.bootstrap.Component
21442  * Bootstrap Graph class
21443 > Prameters
21444  -sm {number} sm 4
21445  -md {number} md 5
21446  @cfg {String} graphtype  bar | vbar | pie
21447  @cfg {number} g_x coodinator | centre x (pie)
21448  @cfg {number} g_y coodinator | centre y (pie)
21449  @cfg {number} g_r radius (pie)
21450  @cfg {number} g_height height of the chart (respected by all elements in the set)
21451  @cfg {number} g_width width of the chart (respected by all elements in the set)
21452  @cfg {Object} title The title of the chart
21453     
21454  -{Array}  values
21455  -opts (object) options for the chart 
21456      o {
21457      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21458      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21459      o vgutter (number)
21460      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.
21461      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21462      o to
21463      o stretch (boolean)
21464      o }
21465  -opts (object) options for the pie
21466      o{
21467      o cut
21468      o startAngle (number)
21469      o endAngle (number)
21470      } 
21471  *
21472  * @constructor
21473  * Create a new Input
21474  * @param {Object} config The config object
21475  */
21476
21477 Roo.bootstrap.Graph = function(config){
21478     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21479     
21480     this.addEvents({
21481         // img events
21482         /**
21483          * @event click
21484          * The img click event for the img.
21485          * @param {Roo.EventObject} e
21486          */
21487         "click" : true
21488     });
21489 };
21490
21491 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21492     
21493     sm: 4,
21494     md: 5,
21495     graphtype: 'bar',
21496     g_height: 250,
21497     g_width: 400,
21498     g_x: 50,
21499     g_y: 50,
21500     g_r: 30,
21501     opts:{
21502         //g_colors: this.colors,
21503         g_type: 'soft',
21504         g_gutter: '20%'
21505
21506     },
21507     title : false,
21508
21509     getAutoCreate : function(){
21510         
21511         var cfg = {
21512             tag: 'div',
21513             html : null
21514         }
21515         
21516         
21517         return  cfg;
21518     },
21519
21520     onRender : function(ct,position){
21521         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21522         this.raphael = Raphael(this.el.dom);
21523         
21524                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21525                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21526                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21527                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21528                 /*
21529                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21530                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21531                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21532                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21533                 
21534                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21535                 r.barchart(330, 10, 300, 220, data1);
21536                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21537                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21538                 */
21539                 
21540                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21541                 // r.barchart(30, 30, 560, 250,  xdata, {
21542                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21543                 //     axis : "0 0 1 1",
21544                 //     axisxlabels :  xdata
21545                 //     //yvalues : cols,
21546                    
21547                 // });
21548 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21549 //        
21550 //        this.load(null,xdata,{
21551 //                axis : "0 0 1 1",
21552 //                axisxlabels :  xdata
21553 //                });
21554
21555     },
21556
21557     load : function(graphtype,xdata,opts){
21558         this.raphael.clear();
21559         if(!graphtype) {
21560             graphtype = this.graphtype;
21561         }
21562         if(!opts){
21563             opts = this.opts;
21564         }
21565         var r = this.raphael,
21566             fin = function () {
21567                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21568             },
21569             fout = function () {
21570                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21571             },
21572             pfin = function() {
21573                 this.sector.stop();
21574                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21575
21576                 if (this.label) {
21577                     this.label[0].stop();
21578                     this.label[0].attr({ r: 7.5 });
21579                     this.label[1].attr({ "font-weight": 800 });
21580                 }
21581             },
21582             pfout = function() {
21583                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21584
21585                 if (this.label) {
21586                     this.label[0].animate({ r: 5 }, 500, "bounce");
21587                     this.label[1].attr({ "font-weight": 400 });
21588                 }
21589             };
21590
21591         switch(graphtype){
21592             case 'bar':
21593                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21594                 break;
21595             case 'hbar':
21596                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21597                 break;
21598             case 'pie':
21599 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21600 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21601 //            
21602                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21603                 
21604                 break;
21605
21606         }
21607         
21608         if(this.title){
21609             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21610         }
21611         
21612     },
21613     
21614     setTitle: function(o)
21615     {
21616         this.title = o;
21617     },
21618     
21619     initEvents: function() {
21620         
21621         if(!this.href){
21622             this.el.on('click', this.onClick, this);
21623         }
21624     },
21625     
21626     onClick : function(e)
21627     {
21628         Roo.log('img onclick');
21629         this.fireEvent('click', this, e);
21630     }
21631    
21632 });
21633
21634  
21635 /*
21636  * - LGPL
21637  *
21638  * numberBox
21639  * 
21640  */
21641 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21642
21643 /**
21644  * @class Roo.bootstrap.dash.NumberBox
21645  * @extends Roo.bootstrap.Component
21646  * Bootstrap NumberBox class
21647  * @cfg {String} headline Box headline
21648  * @cfg {String} content Box content
21649  * @cfg {String} icon Box icon
21650  * @cfg {String} footer Footer text
21651  * @cfg {String} fhref Footer href
21652  * 
21653  * @constructor
21654  * Create a new NumberBox
21655  * @param {Object} config The config object
21656  */
21657
21658
21659 Roo.bootstrap.dash.NumberBox = function(config){
21660     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21661     
21662 };
21663
21664 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21665     
21666     headline : '',
21667     content : '',
21668     icon : '',
21669     footer : '',
21670     fhref : '',
21671     ficon : '',
21672     
21673     getAutoCreate : function(){
21674         
21675         var cfg = {
21676             tag : 'div',
21677             cls : 'small-box ',
21678             cn : [
21679                 {
21680                     tag : 'div',
21681                     cls : 'inner',
21682                     cn :[
21683                         {
21684                             tag : 'h3',
21685                             cls : 'roo-headline',
21686                             html : this.headline
21687                         },
21688                         {
21689                             tag : 'p',
21690                             cls : 'roo-content',
21691                             html : this.content
21692                         }
21693                     ]
21694                 }
21695             ]
21696         }
21697         
21698         if(this.icon){
21699             cfg.cn.push({
21700                 tag : 'div',
21701                 cls : 'icon',
21702                 cn :[
21703                     {
21704                         tag : 'i',
21705                         cls : 'ion ' + this.icon
21706                     }
21707                 ]
21708             });
21709         }
21710         
21711         if(this.footer){
21712             var footer = {
21713                 tag : 'a',
21714                 cls : 'small-box-footer',
21715                 href : this.fhref || '#',
21716                 html : this.footer
21717             };
21718             
21719             cfg.cn.push(footer);
21720             
21721         }
21722         
21723         return  cfg;
21724     },
21725
21726     onRender : function(ct,position){
21727         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21728
21729
21730        
21731                 
21732     },
21733
21734     setHeadline: function (value)
21735     {
21736         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21737     },
21738     
21739     setFooter: function (value, href)
21740     {
21741         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21742         
21743         if(href){
21744             this.el.select('a.small-box-footer',true).first().attr('href', href);
21745         }
21746         
21747     },
21748
21749     setContent: function (value)
21750     {
21751         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21752     },
21753
21754     initEvents: function() 
21755     {   
21756         
21757     }
21758     
21759 });
21760
21761  
21762 /*
21763  * - LGPL
21764  *
21765  * TabBox
21766  * 
21767  */
21768 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21769
21770 /**
21771  * @class Roo.bootstrap.dash.TabBox
21772  * @extends Roo.bootstrap.Component
21773  * Bootstrap TabBox class
21774  * @cfg {String} title Title of the TabBox
21775  * @cfg {String} icon Icon of the TabBox
21776  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21777  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21778  * 
21779  * @constructor
21780  * Create a new TabBox
21781  * @param {Object} config The config object
21782  */
21783
21784
21785 Roo.bootstrap.dash.TabBox = function(config){
21786     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21787     this.addEvents({
21788         // raw events
21789         /**
21790          * @event addpane
21791          * When a pane is added
21792          * @param {Roo.bootstrap.dash.TabPane} pane
21793          */
21794         "addpane" : true,
21795         /**
21796          * @event activatepane
21797          * When a pane is activated
21798          * @param {Roo.bootstrap.dash.TabPane} pane
21799          */
21800         "activatepane" : true
21801         
21802          
21803     });
21804     
21805     this.panes = [];
21806 };
21807
21808 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21809
21810     title : '',
21811     icon : false,
21812     showtabs : true,
21813     tabScrollable : false,
21814     
21815     getChildContainer : function()
21816     {
21817         return this.el.select('.tab-content', true).first();
21818     },
21819     
21820     getAutoCreate : function(){
21821         
21822         var header = {
21823             tag: 'li',
21824             cls: 'pull-left header',
21825             html: this.title,
21826             cn : []
21827         };
21828         
21829         if(this.icon){
21830             header.cn.push({
21831                 tag: 'i',
21832                 cls: 'fa ' + this.icon
21833             });
21834         }
21835         
21836         var h = {
21837             tag: 'ul',
21838             cls: 'nav nav-tabs pull-right',
21839             cn: [
21840                 header
21841             ]
21842         };
21843         
21844         if(this.tabScrollable){
21845             h = {
21846                 tag: 'div',
21847                 cls: 'tab-header',
21848                 cn: [
21849                     {
21850                         tag: 'ul',
21851                         cls: 'nav nav-tabs pull-right',
21852                         cn: [
21853                             header
21854                         ]
21855                     }
21856                 ]
21857             }
21858         }
21859         
21860         var cfg = {
21861             tag: 'div',
21862             cls: 'nav-tabs-custom',
21863             cn: [
21864                 h,
21865                 {
21866                     tag: 'div',
21867                     cls: 'tab-content no-padding',
21868                     cn: []
21869                 }
21870             ]
21871         }
21872
21873         return  cfg;
21874     },
21875     initEvents : function()
21876     {
21877         //Roo.log('add add pane handler');
21878         this.on('addpane', this.onAddPane, this);
21879     },
21880      /**
21881      * Updates the box title
21882      * @param {String} html to set the title to.
21883      */
21884     setTitle : function(value)
21885     {
21886         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21887     },
21888     onAddPane : function(pane)
21889     {
21890         this.panes.push(pane);
21891         //Roo.log('addpane');
21892         //Roo.log(pane);
21893         // tabs are rendere left to right..
21894         if(!this.showtabs){
21895             return;
21896         }
21897         
21898         var ctr = this.el.select('.nav-tabs', true).first();
21899          
21900          
21901         var existing = ctr.select('.nav-tab',true);
21902         var qty = existing.getCount();;
21903         
21904         
21905         var tab = ctr.createChild({
21906             tag : 'li',
21907             cls : 'nav-tab' + (qty ? '' : ' active'),
21908             cn : [
21909                 {
21910                     tag : 'a',
21911                     href:'#',
21912                     html : pane.title
21913                 }
21914             ]
21915         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21916         pane.tab = tab;
21917         
21918         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21919         if (!qty) {
21920             pane.el.addClass('active');
21921         }
21922         
21923                 
21924     },
21925     onTabClick : function(ev,un,ob,pane)
21926     {
21927         //Roo.log('tab - prev default');
21928         ev.preventDefault();
21929         
21930         
21931         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21932         pane.tab.addClass('active');
21933         //Roo.log(pane.title);
21934         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21935         // technically we should have a deactivate event.. but maybe add later.
21936         // and it should not de-activate the selected tab...
21937         this.fireEvent('activatepane', pane);
21938         pane.el.addClass('active');
21939         pane.fireEvent('activate');
21940         
21941         
21942     },
21943     
21944     getActivePane : function()
21945     {
21946         var r = false;
21947         Roo.each(this.panes, function(p) {
21948             if(p.el.hasClass('active')){
21949                 r = p;
21950                 return false;
21951             }
21952             
21953             return;
21954         });
21955         
21956         return r;
21957     }
21958     
21959     
21960 });
21961
21962  
21963 /*
21964  * - LGPL
21965  *
21966  * Tab pane
21967  * 
21968  */
21969 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21970 /**
21971  * @class Roo.bootstrap.TabPane
21972  * @extends Roo.bootstrap.Component
21973  * Bootstrap TabPane class
21974  * @cfg {Boolean} active (false | true) Default false
21975  * @cfg {String} title title of panel
21976
21977  * 
21978  * @constructor
21979  * Create a new TabPane
21980  * @param {Object} config The config object
21981  */
21982
21983 Roo.bootstrap.dash.TabPane = function(config){
21984     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21985     
21986     this.addEvents({
21987         // raw events
21988         /**
21989          * @event activate
21990          * When a pane is activated
21991          * @param {Roo.bootstrap.dash.TabPane} pane
21992          */
21993         "activate" : true
21994          
21995     });
21996 };
21997
21998 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21999     
22000     active : false,
22001     title : '',
22002     
22003     // the tabBox that this is attached to.
22004     tab : false,
22005      
22006     getAutoCreate : function() 
22007     {
22008         var cfg = {
22009             tag: 'div',
22010             cls: 'tab-pane'
22011         }
22012         
22013         if(this.active){
22014             cfg.cls += ' active';
22015         }
22016         
22017         return cfg;
22018     },
22019     initEvents  : function()
22020     {
22021         //Roo.log('trigger add pane handler');
22022         this.parent().fireEvent('addpane', this)
22023     },
22024     
22025      /**
22026      * Updates the tab title 
22027      * @param {String} html to set the title to.
22028      */
22029     setTitle: function(str)
22030     {
22031         if (!this.tab) {
22032             return;
22033         }
22034         this.title = str;
22035         this.tab.select('a', true).first().dom.innerHTML = str;
22036         
22037     }
22038     
22039     
22040     
22041 });
22042
22043  
22044
22045
22046  /*
22047  * - LGPL
22048  *
22049  * menu
22050  * 
22051  */
22052 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22053
22054 /**
22055  * @class Roo.bootstrap.menu.Menu
22056  * @extends Roo.bootstrap.Component
22057  * Bootstrap Menu class - container for Menu
22058  * @cfg {String} html Text of the menu
22059  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22060  * @cfg {String} icon Font awesome icon
22061  * @cfg {String} pos Menu align to (top | bottom) default bottom
22062  * 
22063  * 
22064  * @constructor
22065  * Create a new Menu
22066  * @param {Object} config The config object
22067  */
22068
22069
22070 Roo.bootstrap.menu.Menu = function(config){
22071     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22072     
22073     this.addEvents({
22074         /**
22075          * @event beforeshow
22076          * Fires before this menu is displayed
22077          * @param {Roo.bootstrap.menu.Menu} this
22078          */
22079         beforeshow : true,
22080         /**
22081          * @event beforehide
22082          * Fires before this menu is hidden
22083          * @param {Roo.bootstrap.menu.Menu} this
22084          */
22085         beforehide : true,
22086         /**
22087          * @event show
22088          * Fires after this menu is displayed
22089          * @param {Roo.bootstrap.menu.Menu} this
22090          */
22091         show : true,
22092         /**
22093          * @event hide
22094          * Fires after this menu is hidden
22095          * @param {Roo.bootstrap.menu.Menu} this
22096          */
22097         hide : true,
22098         /**
22099          * @event click
22100          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22101          * @param {Roo.bootstrap.menu.Menu} this
22102          * @param {Roo.EventObject} e
22103          */
22104         click : true
22105     });
22106     
22107 };
22108
22109 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22110     
22111     submenu : false,
22112     html : '',
22113     weight : 'default',
22114     icon : false,
22115     pos : 'bottom',
22116     
22117     
22118     getChildContainer : function() {
22119         if(this.isSubMenu){
22120             return this.el;
22121         }
22122         
22123         return this.el.select('ul.dropdown-menu', true).first();  
22124     },
22125     
22126     getAutoCreate : function()
22127     {
22128         var text = [
22129             {
22130                 tag : 'span',
22131                 cls : 'roo-menu-text',
22132                 html : this.html
22133             }
22134         ];
22135         
22136         if(this.icon){
22137             text.unshift({
22138                 tag : 'i',
22139                 cls : 'fa ' + this.icon
22140             })
22141         }
22142         
22143         
22144         var cfg = {
22145             tag : 'div',
22146             cls : 'btn-group',
22147             cn : [
22148                 {
22149                     tag : 'button',
22150                     cls : 'dropdown-button btn btn-' + this.weight,
22151                     cn : text
22152                 },
22153                 {
22154                     tag : 'button',
22155                     cls : 'dropdown-toggle btn btn-' + this.weight,
22156                     cn : [
22157                         {
22158                             tag : 'span',
22159                             cls : 'caret'
22160                         }
22161                     ]
22162                 },
22163                 {
22164                     tag : 'ul',
22165                     cls : 'dropdown-menu'
22166                 }
22167             ]
22168             
22169         };
22170         
22171         if(this.pos == 'top'){
22172             cfg.cls += ' dropup';
22173         }
22174         
22175         if(this.isSubMenu){
22176             cfg = {
22177                 tag : 'ul',
22178                 cls : 'dropdown-menu'
22179             }
22180         }
22181         
22182         return cfg;
22183     },
22184     
22185     onRender : function(ct, position)
22186     {
22187         this.isSubMenu = ct.hasClass('dropdown-submenu');
22188         
22189         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22190     },
22191     
22192     initEvents : function() 
22193     {
22194         if(this.isSubMenu){
22195             return;
22196         }
22197         
22198         this.hidden = true;
22199         
22200         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22201         this.triggerEl.on('click', this.onTriggerPress, this);
22202         
22203         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22204         this.buttonEl.on('click', this.onClick, this);
22205         
22206     },
22207     
22208     list : function()
22209     {
22210         if(this.isSubMenu){
22211             return this.el;
22212         }
22213         
22214         return this.el.select('ul.dropdown-menu', true).first();
22215     },
22216     
22217     onClick : function(e)
22218     {
22219         this.fireEvent("click", this, e);
22220     },
22221     
22222     onTriggerPress  : function(e)
22223     {   
22224         if (this.isVisible()) {
22225             this.hide();
22226         } else {
22227             this.show();
22228         }
22229     },
22230     
22231     isVisible : function(){
22232         return !this.hidden;
22233     },
22234     
22235     show : function()
22236     {
22237         this.fireEvent("beforeshow", this);
22238         
22239         this.hidden = false;
22240         this.el.addClass('open');
22241         
22242         Roo.get(document).on("mouseup", this.onMouseUp, this);
22243         
22244         this.fireEvent("show", this);
22245         
22246         
22247     },
22248     
22249     hide : function()
22250     {
22251         this.fireEvent("beforehide", this);
22252         
22253         this.hidden = true;
22254         this.el.removeClass('open');
22255         
22256         Roo.get(document).un("mouseup", this.onMouseUp);
22257         
22258         this.fireEvent("hide", this);
22259     },
22260     
22261     onMouseUp : function()
22262     {
22263         this.hide();
22264     }
22265     
22266 });
22267
22268  
22269  /*
22270  * - LGPL
22271  *
22272  * menu item
22273  * 
22274  */
22275 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22276
22277 /**
22278  * @class Roo.bootstrap.menu.Item
22279  * @extends Roo.bootstrap.Component
22280  * Bootstrap MenuItem class
22281  * @cfg {Boolean} submenu (true | false) default false
22282  * @cfg {String} html text of the item
22283  * @cfg {String} href the link
22284  * @cfg {Boolean} disable (true | false) default false
22285  * @cfg {Boolean} preventDefault (true | false) default true
22286  * @cfg {String} icon Font awesome icon
22287  * @cfg {String} pos Submenu align to (left | right) default right 
22288  * 
22289  * 
22290  * @constructor
22291  * Create a new Item
22292  * @param {Object} config The config object
22293  */
22294
22295
22296 Roo.bootstrap.menu.Item = function(config){
22297     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22298     this.addEvents({
22299         /**
22300          * @event mouseover
22301          * Fires when the mouse is hovering over this menu
22302          * @param {Roo.bootstrap.menu.Item} this
22303          * @param {Roo.EventObject} e
22304          */
22305         mouseover : true,
22306         /**
22307          * @event mouseout
22308          * Fires when the mouse exits this menu
22309          * @param {Roo.bootstrap.menu.Item} this
22310          * @param {Roo.EventObject} e
22311          */
22312         mouseout : true,
22313         // raw events
22314         /**
22315          * @event click
22316          * The raw click event for the entire grid.
22317          * @param {Roo.EventObject} e
22318          */
22319         click : true
22320     });
22321 };
22322
22323 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22324     
22325     submenu : false,
22326     href : '',
22327     html : '',
22328     preventDefault: true,
22329     disable : false,
22330     icon : false,
22331     pos : 'right',
22332     
22333     getAutoCreate : function()
22334     {
22335         var text = [
22336             {
22337                 tag : 'span',
22338                 cls : 'roo-menu-item-text',
22339                 html : this.html
22340             }
22341         ];
22342         
22343         if(this.icon){
22344             text.unshift({
22345                 tag : 'i',
22346                 cls : 'fa ' + this.icon
22347             })
22348         }
22349         
22350         var cfg = {
22351             tag : 'li',
22352             cn : [
22353                 {
22354                     tag : 'a',
22355                     href : this.href || '#',
22356                     cn : text
22357                 }
22358             ]
22359         };
22360         
22361         if(this.disable){
22362             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22363         }
22364         
22365         if(this.submenu){
22366             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22367             
22368             if(this.pos == 'left'){
22369                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22370             }
22371         }
22372         
22373         return cfg;
22374     },
22375     
22376     initEvents : function() 
22377     {
22378         this.el.on('mouseover', this.onMouseOver, this);
22379         this.el.on('mouseout', this.onMouseOut, this);
22380         
22381         this.el.select('a', true).first().on('click', this.onClick, this);
22382         
22383     },
22384     
22385     onClick : function(e)
22386     {
22387         if(this.preventDefault){
22388             e.preventDefault();
22389         }
22390         
22391         this.fireEvent("click", this, e);
22392     },
22393     
22394     onMouseOver : function(e)
22395     {
22396         if(this.submenu && this.pos == 'left'){
22397             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22398         }
22399         
22400         this.fireEvent("mouseover", this, e);
22401     },
22402     
22403     onMouseOut : function(e)
22404     {
22405         this.fireEvent("mouseout", this, e);
22406     }
22407 });
22408
22409  
22410
22411  /*
22412  * - LGPL
22413  *
22414  * menu separator
22415  * 
22416  */
22417 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22418
22419 /**
22420  * @class Roo.bootstrap.menu.Separator
22421  * @extends Roo.bootstrap.Component
22422  * Bootstrap Separator class
22423  * 
22424  * @constructor
22425  * Create a new Separator
22426  * @param {Object} config The config object
22427  */
22428
22429
22430 Roo.bootstrap.menu.Separator = function(config){
22431     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22432 };
22433
22434 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22435     
22436     getAutoCreate : function(){
22437         var cfg = {
22438             tag : 'li',
22439             cls: 'divider'
22440         };
22441         
22442         return cfg;
22443     }
22444    
22445 });
22446
22447  
22448
22449  /*
22450  * - LGPL
22451  *
22452  * Tooltip
22453  * 
22454  */
22455
22456 /**
22457  * @class Roo.bootstrap.Tooltip
22458  * Bootstrap Tooltip class
22459  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22460  * to determine which dom element triggers the tooltip.
22461  * 
22462  * It needs to add support for additional attributes like tooltip-position
22463  * 
22464  * @constructor
22465  * Create a new Toolti
22466  * @param {Object} config The config object
22467  */
22468
22469 Roo.bootstrap.Tooltip = function(config){
22470     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22471 };
22472
22473 Roo.apply(Roo.bootstrap.Tooltip, {
22474     /**
22475      * @function init initialize tooltip monitoring.
22476      * @static
22477      */
22478     currentEl : false,
22479     currentTip : false,
22480     currentRegion : false,
22481     
22482     //  init : delay?
22483     
22484     init : function()
22485     {
22486         Roo.get(document).on('mouseover', this.enter ,this);
22487         Roo.get(document).on('mouseout', this.leave, this);
22488          
22489         
22490         this.currentTip = new Roo.bootstrap.Tooltip();
22491     },
22492     
22493     enter : function(ev)
22494     {
22495         var dom = ev.getTarget();
22496         
22497         //Roo.log(['enter',dom]);
22498         var el = Roo.fly(dom);
22499         if (this.currentEl) {
22500             //Roo.log(dom);
22501             //Roo.log(this.currentEl);
22502             //Roo.log(this.currentEl.contains(dom));
22503             if (this.currentEl == el) {
22504                 return;
22505             }
22506             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22507                 return;
22508             }
22509
22510         }
22511         
22512         
22513         
22514         if (this.currentTip.el) {
22515             this.currentTip.el.hide(); // force hiding...
22516         }    
22517         //Roo.log(ev);
22518         var bindEl = el;
22519         
22520         // you can not look for children, as if el is the body.. then everythign is the child..
22521         if (!el.attr('tooltip')) { //
22522             if (!el.select("[tooltip]").elements.length) {
22523                 return;
22524             }
22525             // is the mouse over this child...?
22526             bindEl = el.select("[tooltip]").first();
22527             var xy = ev.getXY();
22528             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22529                 //Roo.log("not in region.");
22530                 return;
22531             }
22532             //Roo.log("child element over..");
22533             
22534         }
22535         this.currentEl = bindEl;
22536         this.currentTip.bind(bindEl);
22537         this.currentRegion = Roo.lib.Region.getRegion(dom);
22538         this.currentTip.enter();
22539         
22540     },
22541     leave : function(ev)
22542     {
22543         var dom = ev.getTarget();
22544         //Roo.log(['leave',dom]);
22545         if (!this.currentEl) {
22546             return;
22547         }
22548         
22549         
22550         if (dom != this.currentEl.dom) {
22551             return;
22552         }
22553         var xy = ev.getXY();
22554         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22555             return;
22556         }
22557         // only activate leave if mouse cursor is outside... bounding box..
22558         
22559         
22560         
22561         
22562         if (this.currentTip) {
22563             this.currentTip.leave();
22564         }
22565         //Roo.log('clear currentEl');
22566         this.currentEl = false;
22567         
22568         
22569     },
22570     alignment : {
22571         'left' : ['r-l', [-2,0], 'right'],
22572         'right' : ['l-r', [2,0], 'left'],
22573         'bottom' : ['t-b', [0,2], 'top'],
22574         'top' : [ 'b-t', [0,-2], 'bottom']
22575     }
22576     
22577 });
22578
22579
22580 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22581     
22582     
22583     bindEl : false,
22584     
22585     delay : null, // can be { show : 300 , hide: 500}
22586     
22587     timeout : null,
22588     
22589     hoverState : null, //???
22590     
22591     placement : 'bottom', 
22592     
22593     getAutoCreate : function(){
22594     
22595         var cfg = {
22596            cls : 'tooltip',
22597            role : 'tooltip',
22598            cn : [
22599                 {
22600                     cls : 'tooltip-arrow'
22601                 },
22602                 {
22603                     cls : 'tooltip-inner'
22604                 }
22605            ]
22606         };
22607         
22608         return cfg;
22609     },
22610     bind : function(el)
22611     {
22612         this.bindEl = el;
22613     },
22614       
22615     
22616     enter : function () {
22617        
22618         if (this.timeout != null) {
22619             clearTimeout(this.timeout);
22620         }
22621         
22622         this.hoverState = 'in';
22623          //Roo.log("enter - show");
22624         if (!this.delay || !this.delay.show) {
22625             this.show();
22626             return;
22627         }
22628         var _t = this;
22629         this.timeout = setTimeout(function () {
22630             if (_t.hoverState == 'in') {
22631                 _t.show();
22632             }
22633         }, this.delay.show);
22634     },
22635     leave : function()
22636     {
22637         clearTimeout(this.timeout);
22638     
22639         this.hoverState = 'out';
22640          if (!this.delay || !this.delay.hide) {
22641             this.hide();
22642             return;
22643         }
22644        
22645         var _t = this;
22646         this.timeout = setTimeout(function () {
22647             //Roo.log("leave - timeout");
22648             
22649             if (_t.hoverState == 'out') {
22650                 _t.hide();
22651                 Roo.bootstrap.Tooltip.currentEl = false;
22652             }
22653         }, delay);
22654     },
22655     
22656     show : function ()
22657     {
22658         if (!this.el) {
22659             this.render(document.body);
22660         }
22661         // set content.
22662         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22663         
22664         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22665         
22666         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22667         
22668         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22669         
22670         var placement = typeof this.placement == 'function' ?
22671             this.placement.call(this, this.el, on_el) :
22672             this.placement;
22673             
22674         var autoToken = /\s?auto?\s?/i;
22675         var autoPlace = autoToken.test(placement);
22676         if (autoPlace) {
22677             placement = placement.replace(autoToken, '') || 'top';
22678         }
22679         
22680         //this.el.detach()
22681         //this.el.setXY([0,0]);
22682         this.el.show();
22683         //this.el.dom.style.display='block';
22684         this.el.addClass(placement);
22685         
22686         //this.el.appendTo(on_el);
22687         
22688         var p = this.getPosition();
22689         var box = this.el.getBox();
22690         
22691         if (autoPlace) {
22692             // fixme..
22693         }
22694         var align = Roo.bootstrap.Tooltip.alignment[placement];
22695         this.el.alignTo(this.bindEl, align[0],align[1]);
22696         //var arrow = this.el.select('.arrow',true).first();
22697         //arrow.set(align[2], 
22698         
22699         this.el.addClass('in fade');
22700         this.hoverState = null;
22701         
22702         if (this.el.hasClass('fade')) {
22703             // fade it?
22704         }
22705         
22706     },
22707     hide : function()
22708     {
22709          
22710         if (!this.el) {
22711             return;
22712         }
22713         //this.el.setXY([0,0]);
22714         this.el.removeClass('in');
22715         //this.el.hide();
22716         
22717     }
22718     
22719 });
22720  
22721
22722  /*
22723  * - LGPL
22724  *
22725  * Location Picker
22726  * 
22727  */
22728
22729 /**
22730  * @class Roo.bootstrap.LocationPicker
22731  * @extends Roo.bootstrap.Component
22732  * Bootstrap LocationPicker class
22733  * @cfg {Number} latitude Position when init default 0
22734  * @cfg {Number} longitude Position when init default 0
22735  * @cfg {Number} zoom default 15
22736  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22737  * @cfg {Boolean} mapTypeControl default false
22738  * @cfg {Boolean} disableDoubleClickZoom default false
22739  * @cfg {Boolean} scrollwheel default true
22740  * @cfg {Boolean} streetViewControl default false
22741  * @cfg {Number} radius default 0
22742  * @cfg {String} locationName
22743  * @cfg {Boolean} draggable default true
22744  * @cfg {Boolean} enableAutocomplete default false
22745  * @cfg {Boolean} enableReverseGeocode default true
22746  * @cfg {String} markerTitle
22747  * 
22748  * @constructor
22749  * Create a new LocationPicker
22750  * @param {Object} config The config object
22751  */
22752
22753
22754 Roo.bootstrap.LocationPicker = function(config){
22755     
22756     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22757     
22758     this.addEvents({
22759         /**
22760          * @event initial
22761          * Fires when the picker initialized.
22762          * @param {Roo.bootstrap.LocationPicker} this
22763          * @param {Google Location} location
22764          */
22765         initial : true,
22766         /**
22767          * @event positionchanged
22768          * Fires when the picker position changed.
22769          * @param {Roo.bootstrap.LocationPicker} this
22770          * @param {Google Location} location
22771          */
22772         positionchanged : true,
22773         /**
22774          * @event resize
22775          * Fires when the map resize.
22776          * @param {Roo.bootstrap.LocationPicker} this
22777          */
22778         resize : true,
22779         /**
22780          * @event show
22781          * Fires when the map show.
22782          * @param {Roo.bootstrap.LocationPicker} this
22783          */
22784         show : true,
22785         /**
22786          * @event hide
22787          * Fires when the map hide.
22788          * @param {Roo.bootstrap.LocationPicker} this
22789          */
22790         hide : true,
22791         /**
22792          * @event mapClick
22793          * Fires when click the map.
22794          * @param {Roo.bootstrap.LocationPicker} this
22795          * @param {Map event} e
22796          */
22797         mapClick : true,
22798         /**
22799          * @event mapRightClick
22800          * Fires when right click the map.
22801          * @param {Roo.bootstrap.LocationPicker} this
22802          * @param {Map event} e
22803          */
22804         mapRightClick : true,
22805         /**
22806          * @event markerClick
22807          * Fires when click the marker.
22808          * @param {Roo.bootstrap.LocationPicker} this
22809          * @param {Map event} e
22810          */
22811         markerClick : true,
22812         /**
22813          * @event markerRightClick
22814          * Fires when right click the marker.
22815          * @param {Roo.bootstrap.LocationPicker} this
22816          * @param {Map event} e
22817          */
22818         markerRightClick : true,
22819         /**
22820          * @event OverlayViewDraw
22821          * Fires when OverlayView Draw
22822          * @param {Roo.bootstrap.LocationPicker} this
22823          */
22824         OverlayViewDraw : true,
22825         /**
22826          * @event OverlayViewOnAdd
22827          * Fires when OverlayView Draw
22828          * @param {Roo.bootstrap.LocationPicker} this
22829          */
22830         OverlayViewOnAdd : true,
22831         /**
22832          * @event OverlayViewOnRemove
22833          * Fires when OverlayView Draw
22834          * @param {Roo.bootstrap.LocationPicker} this
22835          */
22836         OverlayViewOnRemove : true,
22837         /**
22838          * @event OverlayViewShow
22839          * Fires when OverlayView Draw
22840          * @param {Roo.bootstrap.LocationPicker} this
22841          * @param {Pixel} cpx
22842          */
22843         OverlayViewShow : true,
22844         /**
22845          * @event OverlayViewHide
22846          * Fires when OverlayView Draw
22847          * @param {Roo.bootstrap.LocationPicker} this
22848          */
22849         OverlayViewHide : true
22850     });
22851         
22852 };
22853
22854 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22855     
22856     gMapContext: false,
22857     
22858     latitude: 0,
22859     longitude: 0,
22860     zoom: 15,
22861     mapTypeId: false,
22862     mapTypeControl: false,
22863     disableDoubleClickZoom: false,
22864     scrollwheel: true,
22865     streetViewControl: false,
22866     radius: 0,
22867     locationName: '',
22868     draggable: true,
22869     enableAutocomplete: false,
22870     enableReverseGeocode: true,
22871     markerTitle: '',
22872     
22873     getAutoCreate: function()
22874     {
22875
22876         var cfg = {
22877             tag: 'div',
22878             cls: 'roo-location-picker'
22879         };
22880         
22881         return cfg
22882     },
22883     
22884     initEvents: function(ct, position)
22885     {       
22886         if(!this.el.getWidth() || this.isApplied()){
22887             return;
22888         }
22889         
22890         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22891         
22892         this.initial();
22893     },
22894     
22895     initial: function()
22896     {
22897         if(!this.mapTypeId){
22898             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22899         }
22900         
22901         this.gMapContext = this.GMapContext();
22902         
22903         this.initOverlayView();
22904         
22905         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22906         
22907         var _this = this;
22908                 
22909         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22910             _this.setPosition(_this.gMapContext.marker.position);
22911         });
22912         
22913         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22914             _this.fireEvent('mapClick', this, event);
22915             
22916         });
22917
22918         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22919             _this.fireEvent('mapRightClick', this, event);
22920             
22921         });
22922         
22923         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22924             _this.fireEvent('markerClick', this, event);
22925             
22926         });
22927
22928         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22929             _this.fireEvent('markerRightClick', this, event);
22930             
22931         });
22932         
22933         this.setPosition(this.gMapContext.location);
22934         
22935         this.fireEvent('initial', this, this.gMapContext.location);
22936     },
22937     
22938     initOverlayView: function()
22939     {
22940         var _this = this;
22941         
22942         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22943             
22944             draw: function()
22945             {
22946                 _this.fireEvent('OverlayViewDraw', _this);
22947             },
22948             
22949             onAdd: function()
22950             {
22951                 _this.fireEvent('OverlayViewOnAdd', _this);
22952             },
22953             
22954             onRemove: function()
22955             {
22956                 _this.fireEvent('OverlayViewOnRemove', _this);
22957             },
22958             
22959             show: function(cpx)
22960             {
22961                 _this.fireEvent('OverlayViewShow', _this, cpx);
22962             },
22963             
22964             hide: function()
22965             {
22966                 _this.fireEvent('OverlayViewHide', _this);
22967             }
22968             
22969         });
22970     },
22971     
22972     fromLatLngToContainerPixel: function(event)
22973     {
22974         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22975     },
22976     
22977     isApplied: function() 
22978     {
22979         return this.getGmapContext() == false ? false : true;
22980     },
22981     
22982     getGmapContext: function() 
22983     {
22984         return this.gMapContext
22985     },
22986     
22987     GMapContext: function() 
22988     {
22989         var position = new google.maps.LatLng(this.latitude, this.longitude);
22990         
22991         var _map = new google.maps.Map(this.el.dom, {
22992             center: position,
22993             zoom: this.zoom,
22994             mapTypeId: this.mapTypeId,
22995             mapTypeControl: this.mapTypeControl,
22996             disableDoubleClickZoom: this.disableDoubleClickZoom,
22997             scrollwheel: this.scrollwheel,
22998             streetViewControl: this.streetViewControl,
22999             locationName: this.locationName,
23000             draggable: this.draggable,
23001             enableAutocomplete: this.enableAutocomplete,
23002             enableReverseGeocode: this.enableReverseGeocode
23003         });
23004         
23005         var _marker = new google.maps.Marker({
23006             position: position,
23007             map: _map,
23008             title: this.markerTitle,
23009             draggable: this.draggable
23010         });
23011         
23012         return {
23013             map: _map,
23014             marker: _marker,
23015             circle: null,
23016             location: position,
23017             radius: this.radius,
23018             locationName: this.locationName,
23019             addressComponents: {
23020                 formatted_address: null,
23021                 addressLine1: null,
23022                 addressLine2: null,
23023                 streetName: null,
23024                 streetNumber: null,
23025                 city: null,
23026                 district: null,
23027                 state: null,
23028                 stateOrProvince: null
23029             },
23030             settings: this,
23031             domContainer: this.el.dom,
23032             geodecoder: new google.maps.Geocoder()
23033         };
23034     },
23035     
23036     drawCircle: function(center, radius, options) 
23037     {
23038         if (this.gMapContext.circle != null) {
23039             this.gMapContext.circle.setMap(null);
23040         }
23041         if (radius > 0) {
23042             radius *= 1;
23043             options = Roo.apply({}, options, {
23044                 strokeColor: "#0000FF",
23045                 strokeOpacity: .35,
23046                 strokeWeight: 2,
23047                 fillColor: "#0000FF",
23048                 fillOpacity: .2
23049             });
23050             
23051             options.map = this.gMapContext.map;
23052             options.radius = radius;
23053             options.center = center;
23054             this.gMapContext.circle = new google.maps.Circle(options);
23055             return this.gMapContext.circle;
23056         }
23057         
23058         return null;
23059     },
23060     
23061     setPosition: function(location) 
23062     {
23063         this.gMapContext.location = location;
23064         this.gMapContext.marker.setPosition(location);
23065         this.gMapContext.map.panTo(location);
23066         this.drawCircle(location, this.gMapContext.radius, {});
23067         
23068         var _this = this;
23069         
23070         if (this.gMapContext.settings.enableReverseGeocode) {
23071             this.gMapContext.geodecoder.geocode({
23072                 latLng: this.gMapContext.location
23073             }, function(results, status) {
23074                 
23075                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23076                     _this.gMapContext.locationName = results[0].formatted_address;
23077                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23078                     
23079                     _this.fireEvent('positionchanged', this, location);
23080                 }
23081             });
23082             
23083             return;
23084         }
23085         
23086         this.fireEvent('positionchanged', this, location);
23087     },
23088     
23089     resize: function()
23090     {
23091         google.maps.event.trigger(this.gMapContext.map, "resize");
23092         
23093         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23094         
23095         this.fireEvent('resize', this);
23096     },
23097     
23098     setPositionByLatLng: function(latitude, longitude)
23099     {
23100         this.setPosition(new google.maps.LatLng(latitude, longitude));
23101     },
23102     
23103     getCurrentPosition: function() 
23104     {
23105         return {
23106             latitude: this.gMapContext.location.lat(),
23107             longitude: this.gMapContext.location.lng()
23108         };
23109     },
23110     
23111     getAddressName: function() 
23112     {
23113         return this.gMapContext.locationName;
23114     },
23115     
23116     getAddressComponents: function() 
23117     {
23118         return this.gMapContext.addressComponents;
23119     },
23120     
23121     address_component_from_google_geocode: function(address_components) 
23122     {
23123         var result = {};
23124         
23125         for (var i = 0; i < address_components.length; i++) {
23126             var component = address_components[i];
23127             if (component.types.indexOf("postal_code") >= 0) {
23128                 result.postalCode = component.short_name;
23129             } else if (component.types.indexOf("street_number") >= 0) {
23130                 result.streetNumber = component.short_name;
23131             } else if (component.types.indexOf("route") >= 0) {
23132                 result.streetName = component.short_name;
23133             } else if (component.types.indexOf("neighborhood") >= 0) {
23134                 result.city = component.short_name;
23135             } else if (component.types.indexOf("locality") >= 0) {
23136                 result.city = component.short_name;
23137             } else if (component.types.indexOf("sublocality") >= 0) {
23138                 result.district = component.short_name;
23139             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23140                 result.stateOrProvince = component.short_name;
23141             } else if (component.types.indexOf("country") >= 0) {
23142                 result.country = component.short_name;
23143             }
23144         }
23145         
23146         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23147         result.addressLine2 = "";
23148         return result;
23149     },
23150     
23151     setZoomLevel: function(zoom)
23152     {
23153         this.gMapContext.map.setZoom(zoom);
23154     },
23155     
23156     show: function()
23157     {
23158         if(!this.el){
23159             return;
23160         }
23161         
23162         this.el.show();
23163         
23164         this.resize();
23165         
23166         this.fireEvent('show', this);
23167     },
23168     
23169     hide: function()
23170     {
23171         if(!this.el){
23172             return;
23173         }
23174         
23175         this.el.hide();
23176         
23177         this.fireEvent('hide', this);
23178     }
23179     
23180 });
23181
23182 Roo.apply(Roo.bootstrap.LocationPicker, {
23183     
23184     OverlayView : function(map, options)
23185     {
23186         options = options || {};
23187         
23188         this.setMap(map);
23189     }
23190     
23191     
23192 });/*
23193  * - LGPL
23194  *
23195  * Alert
23196  * 
23197  */
23198
23199 /**
23200  * @class Roo.bootstrap.Alert
23201  * @extends Roo.bootstrap.Component
23202  * Bootstrap Alert class
23203  * @cfg {String} title The title of alert
23204  * @cfg {String} html The content of alert
23205  * @cfg {String} weight (  success | info | warning | danger )
23206  * @cfg {String} faicon font-awesomeicon
23207  * 
23208  * @constructor
23209  * Create a new alert
23210  * @param {Object} config The config object
23211  */
23212
23213
23214 Roo.bootstrap.Alert = function(config){
23215     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23216     
23217 };
23218
23219 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23220     
23221     title: '',
23222     html: '',
23223     weight: false,
23224     faicon: false,
23225     
23226     getAutoCreate : function()
23227     {
23228         
23229         var cfg = {
23230             tag : 'div',
23231             cls : 'alert',
23232             cn : [
23233                 {
23234                     tag : 'i',
23235                     cls : 'roo-alert-icon'
23236                     
23237                 },
23238                 {
23239                     tag : 'b',
23240                     cls : 'roo-alert-title',
23241                     html : this.title
23242                 },
23243                 {
23244                     tag : 'span',
23245                     cls : 'roo-alert-text',
23246                     html : this.html
23247                 }
23248             ]
23249         };
23250         
23251         if(this.faicon){
23252             cfg.cn[0].cls += ' fa ' + this.faicon;
23253         }
23254         
23255         if(this.weight){
23256             cfg.cls += ' alert-' + this.weight;
23257         }
23258         
23259         return cfg;
23260     },
23261     
23262     initEvents: function() 
23263     {
23264         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23265     },
23266     
23267     setTitle : function(str)
23268     {
23269         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23270     },
23271     
23272     setText : function(str)
23273     {
23274         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23275     },
23276     
23277     setWeight : function(weight)
23278     {
23279         if(this.weight){
23280             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23281         }
23282         
23283         this.weight = weight;
23284         
23285         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23286     },
23287     
23288     setIcon : function(icon)
23289     {
23290         if(this.faicon){
23291             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23292         }
23293         
23294         this.faicon = icon
23295         
23296         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23297     },
23298     
23299     hide: function() 
23300     {
23301         this.el.hide();   
23302     },
23303     
23304     show: function() 
23305     {  
23306         this.el.show();   
23307     }
23308     
23309 });
23310
23311