roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         this.el.removeClass('hidden');
350     },
351     /**
352      * Hide a component - adds 'hidden' class
353      */
354     hide: function()
355     {
356         if (!this.el.hasClass('hidden')) {
357             this.el.addClass('hidden');
358         }
359         
360     }
361 });
362
363  /*
364  * - LGPL
365  *
366  * Body
367  * 
368  */
369
370 /**
371  * @class Roo.bootstrap.Body
372  * @extends Roo.bootstrap.Component
373  * Bootstrap Body class
374  * 
375  * @constructor
376  * Create a new body
377  * @param {Object} config The config object
378  */
379
380 Roo.bootstrap.Body = function(config){
381     Roo.bootstrap.Body.superclass.constructor.call(this, config);
382     this.el = Roo.get(document.body);
383     if (this.cls && this.cls.length) {
384         Roo.get(document.body).addClass(this.cls);
385     }
386 };
387
388 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
389       
390         autoCreate : {
391         cls: 'container'
392     },
393     onRender : function(ct, position)
394     {
395        /* Roo.log("Roo.bootstrap.Body - onRender");
396         if (this.cls && this.cls.length) {
397             Roo.get(document.body).addClass(this.cls);
398         }
399         // style??? xttr???
400         */
401     }
402     
403     
404  
405    
406 });
407
408  /*
409  * - LGPL
410  *
411  * button group
412  * 
413  */
414
415
416 /**
417  * @class Roo.bootstrap.ButtonGroup
418  * @extends Roo.bootstrap.Component
419  * Bootstrap ButtonGroup class
420  * @cfg {String} size lg | sm | xs (default empty normal)
421  * @cfg {String} align vertical | justified  (default none)
422  * @cfg {String} direction up | down (default down)
423  * @cfg {Boolean} toolbar false | true
424  * @cfg {Boolean} btn true | false
425  * 
426  * 
427  * @constructor
428  * Create a new Input
429  * @param {Object} config The config object
430  */
431
432 Roo.bootstrap.ButtonGroup = function(config){
433     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
434 };
435
436 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
437     
438     size: '',
439     align: '',
440     direction: '',
441     toolbar: false,
442     btn: true,
443
444     getAutoCreate : function(){
445         var cfg = {
446             cls: 'btn-group',
447             html : null
448         }
449         
450         cfg.html = this.html || cfg.html;
451         
452         if (this.toolbar) {
453             cfg = {
454                 cls: 'btn-toolbar',
455                 html: null
456             }
457             
458             return cfg;
459         }
460         
461         if (['vertical','justified'].indexOf(this.align)!==-1) {
462             cfg.cls = 'btn-group-' + this.align;
463             
464             if (this.align == 'justified') {
465                 console.log(this.items);
466             }
467         }
468         
469         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
470             cfg.cls += ' btn-group-' + this.size;
471         }
472         
473         if (this.direction == 'up') {
474             cfg.cls += ' dropup' ;
475         }
476         
477         return cfg;
478     }
479    
480 });
481
482  /*
483  * - LGPL
484  *
485  * button
486  * 
487  */
488
489 /**
490  * @class Roo.bootstrap.Button
491  * @extends Roo.bootstrap.Component
492  * Bootstrap Button class
493  * @cfg {String} html The button content
494  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
495  * @cfg {String} size ( lg | sm | xs)
496  * @cfg {String} tag ( a | input | submit)
497  * @cfg {String} href empty or href
498  * @cfg {Boolean} disabled default false;
499  * @cfg {Boolean} isClose default false;
500  * @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)
501  * @cfg {String} badge text for badge
502  * @cfg {String} theme default 
503  * @cfg {Boolean} inverse 
504  * @cfg {Boolean} toggle 
505  * @cfg {String} ontext text for on toggle state
506  * @cfg {String} offtext text for off toggle state
507  * @cfg {Boolean} defaulton 
508  * @cfg {Boolean} preventDefault  default true
509  * @cfg {Boolean} removeClass remove the standard class..
510  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
511  * 
512  * @constructor
513  * Create a new button
514  * @param {Object} config The config object
515  */
516
517
518 Roo.bootstrap.Button = function(config){
519     Roo.bootstrap.Button.superclass.constructor.call(this, config);
520     this.addEvents({
521         // raw events
522         /**
523          * @event click
524          * When a butotn is pressed
525          * @param {Roo.bootstrap.Button} this
526          * @param {Roo.EventObject} e
527          */
528         "click" : true,
529          /**
530          * @event toggle
531          * After the button has been toggles
532          * @param {Roo.EventObject} e
533          * @param {boolean} pressed (also available as button.pressed)
534          */
535         "toggle" : true
536     });
537 };
538
539 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
540     html: false,
541     active: false,
542     weight: '',
543     size: '',
544     tag: 'button',
545     href: '',
546     disabled: false,
547     isClose: false,
548     glyphicon: '',
549     badge: '',
550     theme: 'default',
551     inverse: false,
552     
553     toggle: false,
554     ontext: 'ON',
555     offtext: 'OFF',
556     defaulton: true,
557     preventDefault: true,
558     removeClass: false,
559     name: false,
560     target: false,
561     
562     
563     pressed : null,
564      
565     
566     getAutoCreate : function(){
567         
568         var cfg = {
569             tag : 'button',
570             cls : 'roo-button',
571             html: ''
572         };
573         
574         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
575             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
576             this.tag = 'button';
577         } else {
578             cfg.tag = this.tag;
579         }
580         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
581         
582         if (this.toggle == true) {
583             cfg={
584                 tag: 'div',
585                 cls: 'slider-frame roo-button',
586                 cn: [
587                     {
588                         tag: 'span',
589                         'data-on-text':'ON',
590                         'data-off-text':'OFF',
591                         cls: 'slider-button',
592                         html: this.offtext
593                     }
594                 ]
595             };
596             
597             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
598                 cfg.cls += ' '+this.weight;
599             }
600             
601             return cfg;
602         }
603         
604         if (this.isClose) {
605             cfg.cls += ' close';
606             
607             cfg["aria-hidden"] = true;
608             
609             cfg.html = "&times;";
610             
611             return cfg;
612         }
613         
614          
615         if (this.theme==='default') {
616             cfg.cls = 'btn roo-button';
617             
618             //if (this.parentType != 'Navbar') {
619             this.weight = this.weight.length ?  this.weight : 'default';
620             //}
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 
623                 cfg.cls += ' btn-' + this.weight;
624             }
625         } else if (this.theme==='glow') {
626             
627             cfg.tag = 'a';
628             cfg.cls = 'btn-glow roo-button';
629             
630             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
631                 
632                 cfg.cls += ' ' + this.weight;
633             }
634         }
635    
636         
637         if (this.inverse) {
638             this.cls += ' inverse';
639         }
640         
641         
642         if (this.active) {
643             cfg.cls += ' active';
644         }
645         
646         if (this.disabled) {
647             cfg.disabled = 'disabled';
648         }
649         
650         if (this.items) {
651             Roo.log('changing to ul' );
652             cfg.tag = 'ul';
653             this.glyphicon = 'caret';
654         }
655         
656         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
657          
658         //gsRoo.log(this.parentType);
659         if (this.parentType === 'Navbar' && !this.parent().bar) {
660             Roo.log('changing to li?');
661             
662             cfg.tag = 'li';
663             
664             cfg.cls = '';
665             cfg.cn =  [{
666                 tag : 'a',
667                 cls : 'roo-button',
668                 html : this.html,
669                 href : this.href || '#'
670             }];
671             if (this.menu) {
672                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
673                 cfg.cls += ' dropdown';
674             }   
675             
676             delete cfg.html;
677             
678         }
679         
680        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
681         
682         if (this.glyphicon) {
683             cfg.html = ' ' + cfg.html;
684             
685             cfg.cn = [
686                 {
687                     tag: 'span',
688                     cls: 'glyphicon glyphicon-' + this.glyphicon
689                 }
690             ];
691         }
692         
693         if (this.badge) {
694             cfg.html += ' ';
695             
696             cfg.tag = 'a';
697             
698 //            cfg.cls='btn roo-button';
699             
700             cfg.href=this.href;
701             
702             var value = cfg.html;
703             
704             if(this.glyphicon){
705                 value = {
706                             tag: 'span',
707                             cls: 'glyphicon glyphicon-' + this.glyphicon,
708                             html: this.html
709                         };
710                 
711             }
712             
713             cfg.cn = [
714                 value,
715                 {
716                     tag: 'span',
717                     cls: 'badge',
718                     html: this.badge
719                 }
720             ];
721             
722             cfg.html='';
723         }
724         
725         if (this.menu) {
726             cfg.cls += ' dropdown';
727             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
728         }
729         
730         if (cfg.tag !== 'a' && this.href !== '') {
731             throw "Tag must be a to set href.";
732         } else if (this.href.length > 0) {
733             cfg.href = this.href;
734         }
735         
736         if(this.removeClass){
737             cfg.cls = '';
738         }
739         
740         if(this.target){
741             cfg.target = this.target;
742         }
743         
744         return cfg;
745     },
746     initEvents: function() {
747        // Roo.log('init events?');
748 //        Roo.log(this.el.dom);
749         // add the menu...
750         
751         if (typeof (this.menu) != 'undefined') {
752             this.menu.parentType = this.xtype;
753             this.menu.triggerEl = this.el;
754             this.addxtype(Roo.apply({}, this.menu));
755         }
756
757
758        if (this.el.hasClass('roo-button')) {
759             this.el.on('click', this.onClick, this);
760        } else {
761             this.el.select('.roo-button').on('click', this.onClick, this);
762        }
763        
764        if(this.removeClass){
765            this.el.on('click', this.onClick, this);
766        }
767        
768        this.el.enableDisplayMode();
769         
770     },
771     onClick : function(e)
772     {
773         if (this.disabled) {
774             return;
775         }
776         
777         
778         Roo.log('button on click ');
779         if(this.preventDefault){
780             e.preventDefault();
781         }
782         if (this.pressed === true || this.pressed === false) {
783             this.pressed = !this.pressed;
784             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
785             this.fireEvent('toggle', this, e, this.pressed);
786         }
787         
788         
789         this.fireEvent('click', this, e);
790     },
791     
792     /**
793      * Enables this button
794      */
795     enable : function()
796     {
797         this.disabled = false;
798         this.el.removeClass('disabled');
799     },
800     
801     /**
802      * Disable this button
803      */
804     disable : function()
805     {
806         this.disabled = true;
807         this.el.addClass('disabled');
808     },
809      /**
810      * sets the active state on/off, 
811      * @param {Boolean} state (optional) Force a particular state
812      */
813     setActive : function(v) {
814         
815         this.el[v ? 'addClass' : 'removeClass']('active');
816     },
817      /**
818      * toggles the current active state 
819      */
820     toggleActive : function()
821     {
822        var active = this.el.hasClass('active');
823        this.setActive(!active);
824        
825         
826     },
827     setText : function(str)
828     {
829         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
830     },
831     getText : function()
832     {
833         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
834     },
835     hide: function() {
836        
837      
838         this.el.hide();   
839     },
840     show: function() {
841        
842         this.el.show();   
843     }
844     
845     
846 });
847
848  /*
849  * - LGPL
850  *
851  * column
852  * 
853  */
854
855 /**
856  * @class Roo.bootstrap.Column
857  * @extends Roo.bootstrap.Component
858  * Bootstrap Column class
859  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
860  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
861  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
862  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
863  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
864  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
865  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
866  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
867  *
868  * 
869  * @cfg {Boolean} hidden (true|false) hide the element
870  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
871  * @cfg {String} fa (ban|check|...) font awesome icon
872  * @cfg {Number} fasize (1|2|....) font awsome size
873
874  * @cfg {String} icon (info-sign|check|...) glyphicon name
875
876  * @cfg {String} html content of column.
877  * 
878  * @constructor
879  * Create a new Column
880  * @param {Object} config The config object
881  */
882
883 Roo.bootstrap.Column = function(config){
884     Roo.bootstrap.Column.superclass.constructor.call(this, config);
885 };
886
887 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
888     
889     xs: false,
890     sm: false,
891     md: false,
892     lg: false,
893     xsoff: false,
894     smoff: false,
895     mdoff: false,
896     lgoff: false,
897     html: '',
898     offset: 0,
899     alert: false,
900     fa: false,
901     icon : false,
902     hidden : false,
903     fasize : 1,
904     
905     getAutoCreate : function(){
906         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
907         
908         cfg = {
909             tag: 'div',
910             cls: 'column'
911         };
912         
913         var settings=this;
914         ['xs','sm','md','lg'].map(function(size){
915             //Roo.log( size + ':' + settings[size]);
916             
917             if (settings[size+'off'] !== false) {
918                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
919             }
920             
921             if (settings[size] === false) {
922                 return;
923             }
924             Roo.log(settings[size]);
925             if (!settings[size]) { // 0 = hidden
926                 cfg.cls += ' hidden-' + size;
927                 return;
928             }
929             cfg.cls += ' col-' + size + '-' + settings[size];
930             
931         });
932         
933         if (this.hidden) {
934             cfg.cls += ' hidden';
935         }
936         
937         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
938             cfg.cls +=' alert alert-' + this.alert;
939         }
940         
941         
942         if (this.html.length) {
943             cfg.html = this.html;
944         }
945         if (this.fa) {
946             var fasize = '';
947             if (this.fasize > 1) {
948                 fasize = ' fa-' + this.fasize + 'x';
949             }
950             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
951             
952             
953         }
954         if (this.icon) {
955             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  
964
965  /*
966  * - LGPL
967  *
968  * page container.
969  * 
970  */
971
972
973 /**
974  * @class Roo.bootstrap.Container
975  * @extends Roo.bootstrap.Component
976  * Bootstrap Container class
977  * @cfg {Boolean} jumbotron is it a jumbotron element
978  * @cfg {String} html content of element
979  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
980  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
981  * @cfg {String} header content of header (for panel)
982  * @cfg {String} footer content of footer (for panel)
983  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
984  * @cfg {String} tag (header|aside|section) type of HTML tag.
985  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
986  * @cfg {String} fa (ban|check|...) font awesome icon
987  * @cfg {String} icon (info-sign|check|...) glyphicon name
988  * @cfg {Boolean} hidden (true|false) hide the element
989  * @cfg {Boolean} expandable (true|false) default false
990  * @cfg {String} rheader contet on the right of header
991
992  *     
993  * @constructor
994  * Create a new Container
995  * @param {Object} config The config object
996  */
997
998 Roo.bootstrap.Container = function(config){
999     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1000     
1001     this.addEvents({
1002         // raw events
1003          /**
1004          * @event expand
1005          * After the panel has been expand
1006          * 
1007          * @param {Roo.bootstrap.Container} this
1008          */
1009         "expand" : true,
1010         /**
1011          * @event collapse
1012          * After the panel has been collapsed
1013          * 
1014          * @param {Roo.bootstrap.Container} this
1015          */
1016         "collapse" : true
1017     });
1018 };
1019
1020 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1021     
1022     jumbotron : false,
1023     well: '',
1024     panel : '',
1025     header: '',
1026     footer : '',
1027     sticky: '',
1028     tag : false,
1029     alert : false,
1030     fa: false,
1031     icon : false,
1032     expandable : false,
1033     rheader : '',
1034   
1035      
1036     getChildContainer : function() {
1037         
1038         if(!this.el){
1039             return false;
1040         }
1041         
1042         if (this.panel.length) {
1043             return this.el.select('.panel-body',true).first();
1044         }
1045         
1046         return this.el;
1047     },
1048     
1049     
1050     getAutoCreate : function(){
1051         
1052         var cfg = {
1053             tag : this.tag || 'div',
1054             html : '',
1055             cls : ''
1056         };
1057         if (this.jumbotron) {
1058             cfg.cls = 'jumbotron';
1059         }
1060         
1061         
1062         
1063         // - this is applied by the parent..
1064         //if (this.cls) {
1065         //    cfg.cls = this.cls + '';
1066         //}
1067         
1068         if (this.sticky.length) {
1069             
1070             var bd = Roo.get(document.body);
1071             if (!bd.hasClass('bootstrap-sticky')) {
1072                 bd.addClass('bootstrap-sticky');
1073                 Roo.select('html',true).setStyle('height', '100%');
1074             }
1075              
1076             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1077         }
1078         
1079         
1080         if (this.well.length) {
1081             switch (this.well) {
1082                 case 'lg':
1083                 case 'sm':
1084                     cfg.cls +=' well well-' +this.well;
1085                     break;
1086                 default:
1087                     cfg.cls +=' well';
1088                     break;
1089             }
1090         }
1091         
1092         if (this.hidden) {
1093             cfg.cls += ' hidden';
1094         }
1095         
1096         
1097         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1098             cfg.cls +=' alert alert-' + this.alert;
1099         }
1100         
1101         var body = cfg;
1102         
1103         if (this.panel.length) {
1104             cfg.cls += ' panel panel-' + this.panel;
1105             cfg.cn = [];
1106             if (this.header.length) {
1107                 
1108                 var h = [];
1109                 
1110                 if(this.expandable){
1111                     h.push({
1112                         tag: 'i',
1113                         cls: 'fa fa-minus'
1114                     });
1115                 }
1116                 
1117                 h.push(
1118                     {
1119                         tag: 'span',
1120                         cls : 'panel-title',
1121                         html : this.header
1122                     },
1123                     {
1124                         tag: 'span',
1125                         cls: 'panel-header-right',
1126                         html: this.rheader
1127                     }
1128                 );
1129                 
1130                 cfg.cn.push({
1131                     cls : 'panel-heading',
1132                     cn : h
1133                 });
1134                 
1135             }
1136             
1137             body = false;
1138             cfg.cn.push({
1139                 cls : 'panel-body',
1140                 html : this.html
1141             });
1142             
1143             
1144             if (this.footer.length) {
1145                 cfg.cn.push({
1146                     cls : 'panel-footer',
1147                     html : this.footer
1148                     
1149                 });
1150             }
1151             
1152         }
1153         
1154         if (body) {
1155             body.html = this.html || cfg.html;
1156             // prefix with the icons..
1157             if (this.fa) {
1158                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1159             }
1160             if (this.icon) {
1161                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1162             }
1163             
1164             
1165         }
1166         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1167             cfg.cls =  'container';
1168         }
1169         
1170         return cfg;
1171     },
1172     
1173     initEvents: function() 
1174     {
1175         var toggleEl = this.toggleEl();
1176         
1177         if(!toggleEl){
1178             return;
1179         }
1180         
1181         toggleEl.on('click', this.onToggleClick, this);
1182     },
1183     
1184     onToggleClick : function()
1185     {
1186         var toggleEl = this.toggleEl();
1187         
1188         if(!toggleEl){
1189             return;
1190         }
1191         
1192         if(toggleEl.hasClass('fa-minus')){
1193             this.collapse();
1194             return;
1195         }
1196         
1197         this.expand();
1198         
1199     },
1200     
1201     expand : function()
1202     {
1203         if(this.fireEvent('expand', this)) {
1204             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1205         
1206             var toggleEl = this.toggleEl();
1207
1208             if(!toggleEl){
1209                 return;
1210             }
1211
1212             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1213         }
1214         
1215     },
1216     
1217     collapse : function()
1218     {
1219         if(this.fireEvent('collapse', this)) {
1220             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1221         
1222             var toggleEl = this.toggleEl();
1223
1224             if(!toggleEl){
1225                 return;
1226             }
1227
1228             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1229         }
1230     },
1231     
1232     toggleEl : function()
1233     {
1234         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1235             return;
1236         }
1237         
1238         return this.el.select('.panel-heading .fa',true).first();
1239     },
1240     
1241     titleEl : function()
1242     {
1243         if(!this.el || !this.panel.length || !this.header.length){
1244             return;
1245         }
1246         
1247         return this.el.select('.panel-title',true).first();
1248     },
1249     
1250     setTitle : function(v)
1251     {
1252         var titleEl = this.titleEl();
1253         
1254         if(!titleEl){
1255             return;
1256         }
1257         
1258         titleEl.dom.innerHTML = v;
1259     },
1260     
1261     getTitle : function()
1262     {
1263         
1264         var titleEl = this.titleEl();
1265         
1266         if(!titleEl){
1267             return '';
1268         }
1269         
1270         return titleEl.dom.innerHTML;
1271     },
1272     
1273     setRightTitle : function(v)
1274     {
1275         var t = this.el.select('.panel-header-right',true).first();
1276         
1277         if(!t){
1278             return;
1279         }
1280         
1281         t.dom.innerHTML = v;
1282     }
1283    
1284 });
1285
1286  /*
1287  * - LGPL
1288  *
1289  * image
1290  * 
1291  */
1292
1293
1294 /**
1295  * @class Roo.bootstrap.Img
1296  * @extends Roo.bootstrap.Component
1297  * Bootstrap Img class
1298  * @cfg {Boolean} imgResponsive false | true
1299  * @cfg {String} border rounded | circle | thumbnail
1300  * @cfg {String} src image source
1301  * @cfg {String} alt image alternative text
1302  * @cfg {String} href a tag href
1303  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1304  * 
1305  * @constructor
1306  * Create a new Input
1307  * @param {Object} config The config object
1308  */
1309
1310 Roo.bootstrap.Img = function(config){
1311     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1312     
1313     this.addEvents({
1314         // img events
1315         /**
1316          * @event click
1317          * The img click event for the img.
1318          * @param {Roo.EventObject} e
1319          */
1320         "click" : true
1321     });
1322 };
1323
1324 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1325     
1326     imgResponsive: true,
1327     border: '',
1328     src: '',
1329     href: false,
1330     target: false,
1331
1332     getAutoCreate : function(){
1333         
1334         var cfg = {
1335             tag: 'img',
1336             cls: (this.imgResponsive) ? 'img-responsive' : '',
1337             html : null
1338         }
1339         
1340         cfg.html = this.html || cfg.html;
1341         
1342         cfg.src = this.src || cfg.src;
1343         
1344         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1345             cfg.cls += ' img-' + this.border;
1346         }
1347         
1348         if(this.alt){
1349             cfg.alt = this.alt;
1350         }
1351         
1352         if(this.href){
1353             var a = {
1354                 tag: 'a',
1355                 href: this.href,
1356                 cn: [
1357                     cfg
1358                 ]
1359             }
1360             
1361             if(this.target){
1362                 a.target = this.target;
1363             }
1364             
1365         }
1366         
1367         
1368         return (this.href) ? a : cfg;
1369     },
1370     
1371     initEvents: function() {
1372         
1373         if(!this.href){
1374             this.el.on('click', this.onClick, this);
1375         }
1376     },
1377     
1378     onClick : function(e)
1379     {
1380         Roo.log('img onclick');
1381         this.fireEvent('click', this, e);
1382     }
1383    
1384 });
1385
1386  /*
1387  * - LGPL
1388  *
1389  * image
1390  * 
1391  */
1392
1393
1394 /**
1395  * @class Roo.bootstrap.Link
1396  * @extends Roo.bootstrap.Component
1397  * Bootstrap Link Class
1398  * @cfg {String} alt image alternative text
1399  * @cfg {String} href a tag href
1400  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1401  * @cfg {String} html the content of the link.
1402  * @cfg {String} anchor name for the anchor link
1403
1404  * @cfg {Boolean} preventDefault (true | false) default false
1405
1406  * 
1407  * @constructor
1408  * Create a new Input
1409  * @param {Object} config The config object
1410  */
1411
1412 Roo.bootstrap.Link = function(config){
1413     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1414     
1415     this.addEvents({
1416         // img events
1417         /**
1418          * @event click
1419          * The img click event for the img.
1420          * @param {Roo.EventObject} e
1421          */
1422         "click" : true
1423     });
1424 };
1425
1426 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1427     
1428     href: false,
1429     target: false,
1430     preventDefault: false,
1431     anchor : false,
1432     alt : false,
1433
1434     getAutoCreate : function()
1435     {
1436         
1437         var cfg = {
1438             tag: 'a'
1439         };
1440         // anchor's do not require html/href...
1441         if (this.anchor === false) {
1442             cfg.html = this.html || 'html-missing';
1443             cfg.href = this.href || '#';
1444         } else {
1445             cfg.name = this.anchor;
1446             if (this.html !== false) {
1447                 cfg.html = this.html;
1448             }
1449             if (this.href !== false) {
1450                 cfg.href = this.href;
1451             }
1452         }
1453         
1454         if(this.alt !== false){
1455             cfg.alt = this.alt;
1456         }
1457         
1458         
1459         if(this.target !== false) {
1460             cfg.target = this.target;
1461         }
1462         
1463         return cfg;
1464     },
1465     
1466     initEvents: function() {
1467         
1468         if(!this.href || this.preventDefault){
1469             this.el.on('click', this.onClick, this);
1470         }
1471     },
1472     
1473     onClick : function(e)
1474     {
1475         if(this.preventDefault){
1476             e.preventDefault();
1477         }
1478         //Roo.log('img onclick');
1479         this.fireEvent('click', this, e);
1480     }
1481    
1482 });
1483
1484  /*
1485  * - LGPL
1486  *
1487  * header
1488  * 
1489  */
1490
1491 /**
1492  * @class Roo.bootstrap.Header
1493  * @extends Roo.bootstrap.Component
1494  * Bootstrap Header class
1495  * @cfg {String} html content of header
1496  * @cfg {Number} level (1|2|3|4|5|6) default 1
1497  * 
1498  * @constructor
1499  * Create a new Header
1500  * @param {Object} config The config object
1501  */
1502
1503
1504 Roo.bootstrap.Header  = function(config){
1505     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1506 };
1507
1508 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1509     
1510     //href : false,
1511     html : false,
1512     level : 1,
1513     
1514     
1515     
1516     getAutoCreate : function(){
1517         
1518         
1519         
1520         var cfg = {
1521             tag: 'h' + (1 *this.level),
1522             html: this.html || ''
1523         } ;
1524         
1525         return cfg;
1526     }
1527    
1528 });
1529
1530  
1531
1532  /*
1533  * Based on:
1534  * Ext JS Library 1.1.1
1535  * Copyright(c) 2006-2007, Ext JS, LLC.
1536  *
1537  * Originally Released Under LGPL - original licence link has changed is not relivant.
1538  *
1539  * Fork - LGPL
1540  * <script type="text/javascript">
1541  */
1542  
1543 /**
1544  * @class Roo.bootstrap.MenuMgr
1545  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1546  * @singleton
1547  */
1548 Roo.bootstrap.MenuMgr = function(){
1549    var menus, active, groups = {}, attached = false, lastShow = new Date();
1550
1551    // private - called when first menu is created
1552    function init(){
1553        menus = {};
1554        active = new Roo.util.MixedCollection();
1555        Roo.get(document).addKeyListener(27, function(){
1556            if(active.length > 0){
1557                hideAll();
1558            }
1559        });
1560    }
1561
1562    // private
1563    function hideAll(){
1564        if(active && active.length > 0){
1565            var c = active.clone();
1566            c.each(function(m){
1567                m.hide();
1568            });
1569        }
1570    }
1571
1572    // private
1573    function onHide(m){
1574        active.remove(m);
1575        if(active.length < 1){
1576            Roo.get(document).un("mouseup", onMouseDown);
1577             
1578            attached = false;
1579        }
1580    }
1581
1582    // private
1583    function onShow(m){
1584        var last = active.last();
1585        lastShow = new Date();
1586        active.add(m);
1587        if(!attached){
1588           Roo.get(document).on("mouseup", onMouseDown);
1589            
1590            attached = true;
1591        }
1592        if(m.parentMenu){
1593           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1594           m.parentMenu.activeChild = m;
1595        }else if(last && last.isVisible()){
1596           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1597        }
1598    }
1599
1600    // private
1601    function onBeforeHide(m){
1602        if(m.activeChild){
1603            m.activeChild.hide();
1604        }
1605        if(m.autoHideTimer){
1606            clearTimeout(m.autoHideTimer);
1607            delete m.autoHideTimer;
1608        }
1609    }
1610
1611    // private
1612    function onBeforeShow(m){
1613        var pm = m.parentMenu;
1614        if(!pm && !m.allowOtherMenus){
1615            hideAll();
1616        }else if(pm && pm.activeChild && active != m){
1617            pm.activeChild.hide();
1618        }
1619    }
1620
1621    // private
1622    function onMouseDown(e){
1623         Roo.log("on MouseDown");
1624         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1625             Roo.log("hideAll");
1626             hideAll();
1627             e.stopEvent();
1628         }
1629         
1630         
1631    }
1632
1633    // private
1634    function onBeforeCheck(mi, state){
1635        if(state){
1636            var g = groups[mi.group];
1637            for(var i = 0, l = g.length; i < l; i++){
1638                if(g[i] != mi){
1639                    g[i].setChecked(false);
1640                }
1641            }
1642        }
1643    }
1644
1645    return {
1646
1647        /**
1648         * Hides all menus that are currently visible
1649         */
1650        hideAll : function(){
1651             hideAll();  
1652        },
1653
1654        // private
1655        register : function(menu){
1656            if(!menus){
1657                init();
1658            }
1659            menus[menu.id] = menu;
1660            menu.on("beforehide", onBeforeHide);
1661            menu.on("hide", onHide);
1662            menu.on("beforeshow", onBeforeShow);
1663            menu.on("show", onShow);
1664            var g = menu.group;
1665            if(g && menu.events["checkchange"]){
1666                if(!groups[g]){
1667                    groups[g] = [];
1668                }
1669                groups[g].push(menu);
1670                menu.on("checkchange", onCheck);
1671            }
1672        },
1673
1674         /**
1675          * Returns a {@link Roo.menu.Menu} object
1676          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1677          * be used to generate and return a new Menu instance.
1678          */
1679        get : function(menu){
1680            if(typeof menu == "string"){ // menu id
1681                return menus[menu];
1682            }else if(menu.events){  // menu instance
1683                return menu;
1684            }
1685            /*else if(typeof menu.length == 'number'){ // array of menu items?
1686                return new Roo.bootstrap.Menu({items:menu});
1687            }else{ // otherwise, must be a config
1688                return new Roo.bootstrap.Menu(menu);
1689            }
1690            */
1691            return false;
1692        },
1693
1694        // private
1695        unregister : function(menu){
1696            delete menus[menu.id];
1697            menu.un("beforehide", onBeforeHide);
1698            menu.un("hide", onHide);
1699            menu.un("beforeshow", onBeforeShow);
1700            menu.un("show", onShow);
1701            var g = menu.group;
1702            if(g && menu.events["checkchange"]){
1703                groups[g].remove(menu);
1704                menu.un("checkchange", onCheck);
1705            }
1706        },
1707
1708        // private
1709        registerCheckable : function(menuItem){
1710            var g = menuItem.group;
1711            if(g){
1712                if(!groups[g]){
1713                    groups[g] = [];
1714                }
1715                groups[g].push(menuItem);
1716                menuItem.on("beforecheckchange", onBeforeCheck);
1717            }
1718        },
1719
1720        // private
1721        unregisterCheckable : function(menuItem){
1722            var g = menuItem.group;
1723            if(g){
1724                groups[g].remove(menuItem);
1725                menuItem.un("beforecheckchange", onBeforeCheck);
1726            }
1727        }
1728    };
1729 }();/*
1730  * - LGPL
1731  *
1732  * menu
1733  * 
1734  */
1735
1736 /**
1737  * @class Roo.bootstrap.Menu
1738  * @extends Roo.bootstrap.Component
1739  * Bootstrap Menu class - container for MenuItems
1740  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1741  * 
1742  * @constructor
1743  * Create a new Menu
1744  * @param {Object} config The config object
1745  */
1746
1747
1748 Roo.bootstrap.Menu = function(config){
1749     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1750     if (this.registerMenu) {
1751         Roo.bootstrap.MenuMgr.register(this);
1752     }
1753     this.addEvents({
1754         /**
1755          * @event beforeshow
1756          * Fires before this menu is displayed
1757          * @param {Roo.menu.Menu} this
1758          */
1759         beforeshow : true,
1760         /**
1761          * @event beforehide
1762          * Fires before this menu is hidden
1763          * @param {Roo.menu.Menu} this
1764          */
1765         beforehide : true,
1766         /**
1767          * @event show
1768          * Fires after this menu is displayed
1769          * @param {Roo.menu.Menu} this
1770          */
1771         show : true,
1772         /**
1773          * @event hide
1774          * Fires after this menu is hidden
1775          * @param {Roo.menu.Menu} this
1776          */
1777         hide : true,
1778         /**
1779          * @event click
1780          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1781          * @param {Roo.menu.Menu} this
1782          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1783          * @param {Roo.EventObject} e
1784          */
1785         click : true,
1786         /**
1787          * @event mouseover
1788          * Fires when the mouse is hovering over this menu
1789          * @param {Roo.menu.Menu} this
1790          * @param {Roo.EventObject} e
1791          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1792          */
1793         mouseover : true,
1794         /**
1795          * @event mouseout
1796          * Fires when the mouse exits this menu
1797          * @param {Roo.menu.Menu} this
1798          * @param {Roo.EventObject} e
1799          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1800          */
1801         mouseout : true,
1802         /**
1803          * @event itemclick
1804          * Fires when a menu item contained in this menu is clicked
1805          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1806          * @param {Roo.EventObject} e
1807          */
1808         itemclick: true
1809     });
1810     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1811 };
1812
1813 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1814     
1815    /// html : false,
1816     //align : '',
1817     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1818     type: false,
1819     /**
1820      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1821      */
1822     registerMenu : true,
1823     
1824     menuItems :false, // stores the menu items..
1825     
1826     hidden:true,
1827     
1828     parentMenu : false,
1829     
1830     getChildContainer : function() {
1831         return this.el;  
1832     },
1833     
1834     getAutoCreate : function(){
1835          
1836         //if (['right'].indexOf(this.align)!==-1) {
1837         //    cfg.cn[1].cls += ' pull-right'
1838         //}
1839         
1840         
1841         var cfg = {
1842             tag : 'ul',
1843             cls : 'dropdown-menu' ,
1844             style : 'z-index:1000'
1845             
1846         }
1847         
1848         if (this.type === 'submenu') {
1849             cfg.cls = 'submenu active';
1850         }
1851         if (this.type === 'treeview') {
1852             cfg.cls = 'treeview-menu';
1853         }
1854         
1855         return cfg;
1856     },
1857     initEvents : function() {
1858         
1859        // Roo.log("ADD event");
1860        // Roo.log(this.triggerEl.dom);
1861         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1862         
1863         this.triggerEl.addClass('dropdown-toggle');
1864         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1865
1866         this.el.on("mouseover", this.onMouseOver, this);
1867         this.el.on("mouseout", this.onMouseOut, this);
1868         
1869         
1870     },
1871     findTargetItem : function(e){
1872         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1873         if(!t){
1874             return false;
1875         }
1876         //Roo.log(t);         Roo.log(t.id);
1877         if(t && t.id){
1878             //Roo.log(this.menuitems);
1879             return this.menuitems.get(t.id);
1880             
1881             //return this.items.get(t.menuItemId);
1882         }
1883         
1884         return false;
1885     },
1886     onClick : function(e){
1887         Roo.log("menu.onClick");
1888         var t = this.findTargetItem(e);
1889         if(!t || t.isContainer){
1890             return;
1891         }
1892         Roo.log(e);
1893         /*
1894         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1895             if(t == this.activeItem && t.shouldDeactivate(e)){
1896                 this.activeItem.deactivate();
1897                 delete this.activeItem;
1898                 return;
1899             }
1900             if(t.canActivate){
1901                 this.setActiveItem(t, true);
1902             }
1903             return;
1904             
1905             
1906         }
1907         */
1908        
1909         Roo.log('pass click event');
1910         
1911         t.onClick(e);
1912         
1913         this.fireEvent("click", this, t, e);
1914         
1915         this.hide();
1916     },
1917      onMouseOver : function(e){
1918         var t  = this.findTargetItem(e);
1919         //Roo.log(t);
1920         //if(t){
1921         //    if(t.canActivate && !t.disabled){
1922         //        this.setActiveItem(t, true);
1923         //    }
1924         //}
1925         
1926         this.fireEvent("mouseover", this, e, t);
1927     },
1928     isVisible : function(){
1929         return !this.hidden;
1930     },
1931      onMouseOut : function(e){
1932         var t  = this.findTargetItem(e);
1933         
1934         //if(t ){
1935         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1936         //        this.activeItem.deactivate();
1937         //        delete this.activeItem;
1938         //    }
1939         //}
1940         this.fireEvent("mouseout", this, e, t);
1941     },
1942     
1943     
1944     /**
1945      * Displays this menu relative to another element
1946      * @param {String/HTMLElement/Roo.Element} element The element to align to
1947      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1948      * the element (defaults to this.defaultAlign)
1949      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1950      */
1951     show : function(el, pos, parentMenu){
1952         this.parentMenu = parentMenu;
1953         if(!this.el){
1954             this.render();
1955         }
1956         this.fireEvent("beforeshow", this);
1957         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1958     },
1959      /**
1960      * Displays this menu at a specific xy position
1961      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1962      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1963      */
1964     showAt : function(xy, parentMenu, /* private: */_e){
1965         this.parentMenu = parentMenu;
1966         if(!this.el){
1967             this.render();
1968         }
1969         if(_e !== false){
1970             this.fireEvent("beforeshow", this);
1971             //xy = this.el.adjustForConstraints(xy);
1972         }
1973         
1974         //this.el.show();
1975         this.hideMenuItems();
1976         this.hidden = false;
1977         this.triggerEl.addClass('open');
1978         
1979         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1980             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1981         }
1982         
1983         this.el.setXY(xy);
1984         this.focus();
1985         this.fireEvent("show", this);
1986     },
1987     
1988     focus : function(){
1989         return;
1990         if(!this.hidden){
1991             this.doFocus.defer(50, this);
1992         }
1993     },
1994
1995     doFocus : function(){
1996         if(!this.hidden){
1997             this.focusEl.focus();
1998         }
1999     },
2000
2001     /**
2002      * Hides this menu and optionally all parent menus
2003      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2004      */
2005     hide : function(deep){
2006         
2007         this.hideMenuItems();
2008         if(this.el && this.isVisible()){
2009             this.fireEvent("beforehide", this);
2010             if(this.activeItem){
2011                 this.activeItem.deactivate();
2012                 this.activeItem = null;
2013             }
2014             this.triggerEl.removeClass('open');;
2015             this.hidden = true;
2016             this.fireEvent("hide", this);
2017         }
2018         if(deep === true && this.parentMenu){
2019             this.parentMenu.hide(true);
2020         }
2021     },
2022     
2023     onTriggerPress  : function(e)
2024     {
2025         
2026         Roo.log('trigger press');
2027         //Roo.log(e.getTarget());
2028        // Roo.log(this.triggerEl.dom);
2029         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2030             return;
2031         }
2032         
2033         if (this.isVisible()) {
2034             Roo.log('hide');
2035             this.hide();
2036         } else {
2037             Roo.log('show');
2038             this.show(this.triggerEl, false, false);
2039         }
2040         
2041         
2042     },
2043     
2044          
2045        
2046     
2047     hideMenuItems : function()
2048     {
2049         //$(backdrop).remove()
2050         Roo.select('.open',true).each(function(aa) {
2051             
2052             aa.removeClass('open');
2053           //var parent = getParent($(this))
2054           //var relatedTarget = { relatedTarget: this }
2055           
2056            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2057           //if (e.isDefaultPrevented()) return
2058            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2059         })
2060     },
2061     addxtypeChild : function (tree, cntr) {
2062         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2063           
2064         this.menuitems.add(comp);
2065         return comp;
2066
2067     },
2068     getEl : function()
2069     {
2070         Roo.log(this.el);
2071         return this.el;
2072     }
2073 });
2074
2075  
2076  /*
2077  * - LGPL
2078  *
2079  * menu item
2080  * 
2081  */
2082
2083
2084 /**
2085  * @class Roo.bootstrap.MenuItem
2086  * @extends Roo.bootstrap.Component
2087  * Bootstrap MenuItem class
2088  * @cfg {String} html the menu label
2089  * @cfg {String} href the link
2090  * @cfg {Boolean} preventDefault (true | false) default true
2091  * @cfg {Boolean} isContainer (true | false) default false
2092  * 
2093  * 
2094  * @constructor
2095  * Create a new MenuItem
2096  * @param {Object} config The config object
2097  */
2098
2099
2100 Roo.bootstrap.MenuItem = function(config){
2101     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2102     this.addEvents({
2103         // raw events
2104         /**
2105          * @event click
2106          * The raw click event for the entire grid.
2107          * @param {Roo.bootstrap.MenuItem} this
2108          * @param {Roo.EventObject} e
2109          */
2110         "click" : true
2111     });
2112 };
2113
2114 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2115     
2116     href : false,
2117     html : false,
2118     preventDefault: true,
2119     isContainer : false,
2120     
2121     getAutoCreate : function(){
2122         
2123         if(this.isContainer){
2124             return {
2125                 tag: 'li',
2126                 cls: 'dropdown-menu-item'
2127             };
2128         }
2129         
2130         var cfg= {
2131             tag: 'li',
2132             cls: 'dropdown-menu-item',
2133             cn: [
2134                     {
2135                         tag : 'a',
2136                         href : '#',
2137                         html : 'Link'
2138                     }
2139                 ]
2140         };
2141         if (this.parent().type == 'treeview') {
2142             cfg.cls = 'treeview-menu';
2143         }
2144         
2145         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2146         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2147         return cfg;
2148     },
2149     
2150     initEvents: function() {
2151         
2152         //this.el.select('a').on('click', this.onClick, this);
2153         
2154     },
2155     onClick : function(e)
2156     {
2157         Roo.log('item on click ');
2158         //if(this.preventDefault){
2159         //    e.preventDefault();
2160         //}
2161         //this.parent().hideMenuItems();
2162         
2163         this.fireEvent('click', this, e);
2164     },
2165     getEl : function()
2166     {
2167         return this.el;
2168     }
2169 });
2170
2171  
2172
2173  /*
2174  * - LGPL
2175  *
2176  * menu separator
2177  * 
2178  */
2179
2180
2181 /**
2182  * @class Roo.bootstrap.MenuSeparator
2183  * @extends Roo.bootstrap.Component
2184  * Bootstrap MenuSeparator class
2185  * 
2186  * @constructor
2187  * Create a new MenuItem
2188  * @param {Object} config The config object
2189  */
2190
2191
2192 Roo.bootstrap.MenuSeparator = function(config){
2193     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2194 };
2195
2196 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2197     
2198     getAutoCreate : function(){
2199         var cfg = {
2200             cls: 'divider',
2201             tag : 'li'
2202         };
2203         
2204         return cfg;
2205     }
2206    
2207 });
2208
2209  
2210
2211  
2212 /*
2213 * Licence: LGPL
2214 */
2215
2216 /**
2217  * @class Roo.bootstrap.Modal
2218  * @extends Roo.bootstrap.Component
2219  * Bootstrap Modal class
2220  * @cfg {String} title Title of dialog
2221  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2222  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2223  * @cfg {Boolean} specificTitle default false
2224  * @cfg {Array} buttons Array of buttons or standard button set..
2225  * @cfg {String} buttonPosition (left|right|center) default right
2226  * @cfg {Boolean} animate default true
2227  * @cfg {Boolean} allow_close default true
2228  * 
2229  * @constructor
2230  * Create a new Modal Dialog
2231  * @param {Object} config The config object
2232  */
2233
2234 Roo.bootstrap.Modal = function(config){
2235     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2236     this.addEvents({
2237         // raw events
2238         /**
2239          * @event btnclick
2240          * The raw btnclick event for the button
2241          * @param {Roo.EventObject} e
2242          */
2243         "btnclick" : true
2244     });
2245     this.buttons = this.buttons || [];
2246      
2247     if (this.tmpl) {
2248         this.tmpl = Roo.factory(this.tmpl);
2249     }
2250     
2251 };
2252
2253 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2254     
2255     title : 'test dialog',
2256    
2257     buttons : false,
2258     
2259     // set on load...
2260      
2261     html: false,
2262     
2263     tmp: false,
2264     
2265     specificTitle: false,
2266     
2267     buttonPosition: 'right',
2268     
2269     allow_close : true,
2270     
2271     animate : true,
2272     
2273     
2274      // private
2275     bodyEl:  false,
2276     footerEl:  false,
2277     titleEl:  false,
2278     closeEl:  false,
2279     
2280     
2281     onRender : function(ct, position)
2282     {
2283         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2284      
2285         if(!this.el){
2286             var cfg = Roo.apply({},  this.getAutoCreate());
2287             cfg.id = Roo.id();
2288             //if(!cfg.name){
2289             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2290             //}
2291             //if (!cfg.name.length) {
2292             //    delete cfg.name;
2293            // }
2294             if (this.cls) {
2295                 cfg.cls += ' ' + this.cls;
2296             }
2297             if (this.style) {
2298                 cfg.style = this.style;
2299             }
2300             this.el = Roo.get(document.body).createChild(cfg, position);
2301         }
2302         //var type = this.el.dom.type;
2303         
2304         
2305         
2306         
2307         if(this.tabIndex !== undefined){
2308             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2309         }
2310         
2311         
2312         this.bodyEl = this.el.select('.modal-body',true).first();
2313         this.closeEl = this.el.select('.modal-header .close', true).first();
2314         this.footerEl = this.el.select('.modal-footer',true).first();
2315         this.titleEl = this.el.select('.modal-title',true).first();
2316         
2317         
2318          
2319         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2320         this.maskEl.enableDisplayMode("block");
2321         this.maskEl.hide();
2322         //this.el.addClass("x-dlg-modal");
2323     
2324         if (this.buttons.length) {
2325             Roo.each(this.buttons, function(bb) {
2326                 b = Roo.apply({}, bb);
2327                 b.xns = b.xns || Roo.bootstrap;
2328                 b.xtype = b.xtype || 'Button';
2329                 if (typeof(b.listeners) == 'undefined') {
2330                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2331                 }
2332                 
2333                 var btn = Roo.factory(b);
2334                 
2335                 btn.onRender(this.el.select('.modal-footer div').first());
2336                 
2337             },this);
2338         }
2339         // render the children.
2340         var nitems = [];
2341         
2342         if(typeof(this.items) != 'undefined'){
2343             var items = this.items;
2344             delete this.items;
2345
2346             for(var i =0;i < items.length;i++) {
2347                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2348             }
2349         }
2350         
2351         this.items = nitems;
2352         
2353         // where are these used - they used to be body/close/footer
2354         
2355        
2356         this.initEvents();
2357         //this.el.addClass([this.fieldClass, this.cls]);
2358         
2359     },
2360     getAutoCreate : function(){
2361         
2362         
2363         var bdy = {
2364                 cls : 'modal-body',
2365                 html : this.html || ''
2366         };
2367         
2368         var title = {
2369             tag: 'h4',
2370             cls : 'modal-title',
2371             html : this.title
2372         };
2373         
2374         if(this.specificTitle){
2375             title = this.title;
2376             
2377         };
2378         
2379         var header = [];
2380         if (this.allow_close) {
2381             header.push({
2382                 tag: 'button',
2383                 cls : 'close',
2384                 html : '&times'
2385             });
2386         }
2387         header.push(title);
2388         
2389         var modal = {
2390             cls: "modal",
2391             style : 'display: none',
2392             cn : [
2393                 {
2394                     cls: "modal-dialog",
2395                     cn : [
2396                         {
2397                             cls : "modal-content",
2398                             cn : [
2399                                 {
2400                                     cls : 'modal-header',
2401                                     cn : header
2402                                 },
2403                                 bdy,
2404                                 {
2405                                     cls : 'modal-footer',
2406                                     cn : [
2407                                         {
2408                                             tag: 'div',
2409                                             cls: 'btn-' + this.buttonPosition
2410                                         }
2411                                     ]
2412                                     
2413                                 }
2414                                 
2415                                 
2416                             ]
2417                             
2418                         }
2419                     ]
2420                         
2421                 }
2422             ]
2423         };
2424         
2425         if(this.animate){
2426             modal.cls += ' fade';
2427         }
2428         
2429         return modal;
2430           
2431     },
2432     getChildContainer : function() {
2433          
2434          return this.bodyEl;
2435         
2436     },
2437     getButtonContainer : function() {
2438          return this.el.select('.modal-footer div',true).first();
2439         
2440     },
2441     initEvents : function()
2442     {
2443         if (this.allow_close) {
2444             this.closeEl.on('click', this.hide, this);
2445         }
2446
2447     },
2448     show : function() {
2449         
2450         if (!this.rendered) {
2451             this.render();
2452         }
2453         
2454         this.el.setStyle('display', 'block');
2455         
2456         if(this.animate){
2457             var _this = this;
2458             (function(){ _this.el.addClass('in'); }).defer(50);
2459         }else{
2460             this.el.addClass('in');
2461         }
2462         
2463         // not sure how we can show data in here.. 
2464         //if (this.tmpl) {
2465         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2466         //}
2467         
2468         Roo.get(document.body).addClass("x-body-masked");
2469         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2470         this.maskEl.show();
2471         this.el.setStyle('zIndex', '10001');
2472        
2473         this.fireEvent('show', this);
2474         
2475         
2476     },
2477     hide : function()
2478     {
2479         this.maskEl.hide();
2480         Roo.get(document.body).removeClass("x-body-masked");
2481         this.el.removeClass('in');
2482         
2483         if(this.animate){
2484             var _this = this;
2485             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2486         }else{
2487             this.el.setStyle('display', 'none');
2488         }
2489         
2490         this.fireEvent('hide', this);
2491     },
2492     
2493     addButton : function(str, cb)
2494     {
2495          
2496         
2497         var b = Roo.apply({}, { html : str } );
2498         b.xns = b.xns || Roo.bootstrap;
2499         b.xtype = b.xtype || 'Button';
2500         if (typeof(b.listeners) == 'undefined') {
2501             b.listeners = { click : cb.createDelegate(this)  };
2502         }
2503         
2504         var btn = Roo.factory(b);
2505            
2506         btn.onRender(this.el.select('.modal-footer div').first());
2507         
2508         return btn;   
2509        
2510     },
2511     
2512     setDefaultButton : function(btn)
2513     {
2514         //this.el.select('.modal-footer').()
2515     },
2516     resizeTo: function(w,h)
2517     {
2518         // skip..
2519     },
2520     setContentSize  : function(w, h)
2521     {
2522         
2523     },
2524     onButtonClick: function(btn,e)
2525     {
2526         //Roo.log([a,b,c]);
2527         this.fireEvent('btnclick', btn.name, e);
2528     },
2529      /**
2530      * Set the title of the Dialog
2531      * @param {String} str new Title
2532      */
2533     setTitle: function(str) {
2534         this.titleEl.dom.innerHTML = str;    
2535     },
2536     /**
2537      * Set the body of the Dialog
2538      * @param {String} str new Title
2539      */
2540     setBody: function(str) {
2541         this.bodyEl.dom.innerHTML = str;    
2542     },
2543     /**
2544      * Set the body of the Dialog using the template
2545      * @param {Obj} data - apply this data to the template and replace the body contents.
2546      */
2547     applyBody: function(obj)
2548     {
2549         if (!this.tmpl) {
2550             Roo.log("Error - using apply Body without a template");
2551             //code
2552         }
2553         this.tmpl.overwrite(this.bodyEl, obj);
2554     }
2555     
2556 });
2557
2558
2559 Roo.apply(Roo.bootstrap.Modal,  {
2560     /**
2561          * Button config that displays a single OK button
2562          * @type Object
2563          */
2564         OK :  [{
2565             name : 'ok',
2566             weight : 'primary',
2567             html : 'OK'
2568         }], 
2569         /**
2570          * Button config that displays Yes and No buttons
2571          * @type Object
2572          */
2573         YESNO : [
2574             {
2575                 name  : 'no',
2576                 html : 'No'
2577             },
2578             {
2579                 name  :'yes',
2580                 weight : 'primary',
2581                 html : 'Yes'
2582             }
2583         ],
2584         
2585         /**
2586          * Button config that displays OK and Cancel buttons
2587          * @type Object
2588          */
2589         OKCANCEL : [
2590             {
2591                name : 'cancel',
2592                 html : 'Cancel'
2593             },
2594             {
2595                 name : 'ok',
2596                 weight : 'primary',
2597                 html : 'OK'
2598             }
2599         ],
2600         /**
2601          * Button config that displays Yes, No and Cancel buttons
2602          * @type Object
2603          */
2604         YESNOCANCEL : [
2605             {
2606                 name : 'yes',
2607                 weight : 'primary',
2608                 html : 'Yes'
2609             },
2610             {
2611                 name : 'no',
2612                 html : 'No'
2613             },
2614             {
2615                 name : 'cancel',
2616                 html : 'Cancel'
2617             }
2618         ]
2619 });
2620  
2621  /*
2622  * - LGPL
2623  *
2624  * messagebox - can be used as a replace
2625  * 
2626  */
2627 /**
2628  * @class Roo.MessageBox
2629  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2630  * Example usage:
2631  *<pre><code>
2632 // Basic alert:
2633 Roo.Msg.alert('Status', 'Changes saved successfully.');
2634
2635 // Prompt for user data:
2636 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2637     if (btn == 'ok'){
2638         // process text value...
2639     }
2640 });
2641
2642 // Show a dialog using config options:
2643 Roo.Msg.show({
2644    title:'Save Changes?',
2645    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2646    buttons: Roo.Msg.YESNOCANCEL,
2647    fn: processResult,
2648    animEl: 'elId'
2649 });
2650 </code></pre>
2651  * @singleton
2652  */
2653 Roo.bootstrap.MessageBox = function(){
2654     var dlg, opt, mask, waitTimer;
2655     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2656     var buttons, activeTextEl, bwidth;
2657
2658     
2659     // private
2660     var handleButton = function(button){
2661         dlg.hide();
2662         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2663     };
2664
2665     // private
2666     var handleHide = function(){
2667         if(opt && opt.cls){
2668             dlg.el.removeClass(opt.cls);
2669         }
2670         //if(waitTimer){
2671         //    Roo.TaskMgr.stop(waitTimer);
2672         //    waitTimer = null;
2673         //}
2674     };
2675
2676     // private
2677     var updateButtons = function(b){
2678         var width = 0;
2679         if(!b){
2680             buttons["ok"].hide();
2681             buttons["cancel"].hide();
2682             buttons["yes"].hide();
2683             buttons["no"].hide();
2684             //dlg.footer.dom.style.display = 'none';
2685             return width;
2686         }
2687         dlg.footerEl.dom.style.display = '';
2688         for(var k in buttons){
2689             if(typeof buttons[k] != "function"){
2690                 if(b[k]){
2691                     buttons[k].show();
2692                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2693                     width += buttons[k].el.getWidth()+15;
2694                 }else{
2695                     buttons[k].hide();
2696                 }
2697             }
2698         }
2699         return width;
2700     };
2701
2702     // private
2703     var handleEsc = function(d, k, e){
2704         if(opt && opt.closable !== false){
2705             dlg.hide();
2706         }
2707         if(e){
2708             e.stopEvent();
2709         }
2710     };
2711
2712     return {
2713         /**
2714          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2715          * @return {Roo.BasicDialog} The BasicDialog element
2716          */
2717         getDialog : function(){
2718            if(!dlg){
2719                 dlg = new Roo.bootstrap.Modal( {
2720                     //draggable: true,
2721                     //resizable:false,
2722                     //constraintoviewport:false,
2723                     //fixedcenter:true,
2724                     //collapsible : false,
2725                     //shim:true,
2726                     //modal: true,
2727                   //  width:400,
2728                   //  height:100,
2729                     //buttonAlign:"center",
2730                     closeClick : function(){
2731                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2732                             handleButton("no");
2733                         }else{
2734                             handleButton("cancel");
2735                         }
2736                     }
2737                 });
2738                 dlg.render();
2739                 dlg.on("hide", handleHide);
2740                 mask = dlg.mask;
2741                 //dlg.addKeyListener(27, handleEsc);
2742                 buttons = {};
2743                 this.buttons = buttons;
2744                 var bt = this.buttonText;
2745                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2746                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2747                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2748                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2749                 Roo.log(buttons)
2750                 bodyEl = dlg.bodyEl.createChild({
2751
2752                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2753                         '<textarea class="roo-mb-textarea"></textarea>' +
2754                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2755                 });
2756                 msgEl = bodyEl.dom.firstChild;
2757                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2758                 textboxEl.enableDisplayMode();
2759                 textboxEl.addKeyListener([10,13], function(){
2760                     if(dlg.isVisible() && opt && opt.buttons){
2761                         if(opt.buttons.ok){
2762                             handleButton("ok");
2763                         }else if(opt.buttons.yes){
2764                             handleButton("yes");
2765                         }
2766                     }
2767                 });
2768                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2769                 textareaEl.enableDisplayMode();
2770                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2771                 progressEl.enableDisplayMode();
2772                 var pf = progressEl.dom.firstChild;
2773                 if (pf) {
2774                     pp = Roo.get(pf.firstChild);
2775                     pp.setHeight(pf.offsetHeight);
2776                 }
2777                 
2778             }
2779             return dlg;
2780         },
2781
2782         /**
2783          * Updates the message box body text
2784          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2785          * the XHTML-compliant non-breaking space character '&amp;#160;')
2786          * @return {Roo.MessageBox} This message box
2787          */
2788         updateText : function(text){
2789             if(!dlg.isVisible() && !opt.width){
2790                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2791             }
2792             msgEl.innerHTML = text || '&#160;';
2793       
2794             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2795             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2796             var w = Math.max(
2797                     Math.min(opt.width || cw , this.maxWidth), 
2798                     Math.max(opt.minWidth || this.minWidth, bwidth)
2799             );
2800             if(opt.prompt){
2801                 activeTextEl.setWidth(w);
2802             }
2803             if(dlg.isVisible()){
2804                 dlg.fixedcenter = false;
2805             }
2806             // to big, make it scroll. = But as usual stupid IE does not support
2807             // !important..
2808             
2809             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2810                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2811                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2812             } else {
2813                 bodyEl.dom.style.height = '';
2814                 bodyEl.dom.style.overflowY = '';
2815             }
2816             if (cw > w) {
2817                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2818             } else {
2819                 bodyEl.dom.style.overflowX = '';
2820             }
2821             
2822             dlg.setContentSize(w, bodyEl.getHeight());
2823             if(dlg.isVisible()){
2824                 dlg.fixedcenter = true;
2825             }
2826             return this;
2827         },
2828
2829         /**
2830          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2831          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2832          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2833          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2834          * @return {Roo.MessageBox} This message box
2835          */
2836         updateProgress : function(value, text){
2837             if(text){
2838                 this.updateText(text);
2839             }
2840             if (pp) { // weird bug on my firefox - for some reason this is not defined
2841                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2842             }
2843             return this;
2844         },        
2845
2846         /**
2847          * Returns true if the message box is currently displayed
2848          * @return {Boolean} True if the message box is visible, else false
2849          */
2850         isVisible : function(){
2851             return dlg && dlg.isVisible();  
2852         },
2853
2854         /**
2855          * Hides the message box if it is displayed
2856          */
2857         hide : function(){
2858             if(this.isVisible()){
2859                 dlg.hide();
2860             }  
2861         },
2862
2863         /**
2864          * Displays a new message box, or reinitializes an existing message box, based on the config options
2865          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2866          * The following config object properties are supported:
2867          * <pre>
2868 Property    Type             Description
2869 ----------  ---------------  ------------------------------------------------------------------------------------
2870 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2871                                    closes (defaults to undefined)
2872 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2873                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2874 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2875                                    progress and wait dialogs will ignore this property and always hide the
2876                                    close button as they can only be closed programmatically.
2877 cls               String           A custom CSS class to apply to the message box element
2878 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2879                                    displayed (defaults to 75)
2880 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2881                                    function will be btn (the name of the button that was clicked, if applicable,
2882                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2883                                    Progress and wait dialogs will ignore this option since they do not respond to
2884                                    user actions and can only be closed programmatically, so any required function
2885                                    should be called by the same code after it closes the dialog.
2886 icon              String           A CSS class that provides a background image to be used as an icon for
2887                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2888 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2889 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2890 modal             Boolean          False to allow user interaction with the page while the message box is
2891                                    displayed (defaults to true)
2892 msg               String           A string that will replace the existing message box body text (defaults
2893                                    to the XHTML-compliant non-breaking space character '&#160;')
2894 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2895 progress          Boolean          True to display a progress bar (defaults to false)
2896 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2897 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2898 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2899 title             String           The title text
2900 value             String           The string value to set into the active textbox element if displayed
2901 wait              Boolean          True to display a progress bar (defaults to false)
2902 width             Number           The width of the dialog in pixels
2903 </pre>
2904          *
2905          * Example usage:
2906          * <pre><code>
2907 Roo.Msg.show({
2908    title: 'Address',
2909    msg: 'Please enter your address:',
2910    width: 300,
2911    buttons: Roo.MessageBox.OKCANCEL,
2912    multiline: true,
2913    fn: saveAddress,
2914    animEl: 'addAddressBtn'
2915 });
2916 </code></pre>
2917          * @param {Object} config Configuration options
2918          * @return {Roo.MessageBox} This message box
2919          */
2920         show : function(options)
2921         {
2922             
2923             // this causes nightmares if you show one dialog after another
2924             // especially on callbacks..
2925              
2926             if(this.isVisible()){
2927                 
2928                 this.hide();
2929                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2930                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2931                 Roo.log("New Dialog Message:" +  options.msg )
2932                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2933                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2934                 
2935             }
2936             var d = this.getDialog();
2937             opt = options;
2938             d.setTitle(opt.title || "&#160;");
2939             d.closeEl.setDisplayed(opt.closable !== false);
2940             activeTextEl = textboxEl;
2941             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2942             if(opt.prompt){
2943                 if(opt.multiline){
2944                     textboxEl.hide();
2945                     textareaEl.show();
2946                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2947                         opt.multiline : this.defaultTextHeight);
2948                     activeTextEl = textareaEl;
2949                 }else{
2950                     textboxEl.show();
2951                     textareaEl.hide();
2952                 }
2953             }else{
2954                 textboxEl.hide();
2955                 textareaEl.hide();
2956             }
2957             progressEl.setDisplayed(opt.progress === true);
2958             this.updateProgress(0);
2959             activeTextEl.dom.value = opt.value || "";
2960             if(opt.prompt){
2961                 dlg.setDefaultButton(activeTextEl);
2962             }else{
2963                 var bs = opt.buttons;
2964                 var db = null;
2965                 if(bs && bs.ok){
2966                     db = buttons["ok"];
2967                 }else if(bs && bs.yes){
2968                     db = buttons["yes"];
2969                 }
2970                 dlg.setDefaultButton(db);
2971             }
2972             bwidth = updateButtons(opt.buttons);
2973             this.updateText(opt.msg);
2974             if(opt.cls){
2975                 d.el.addClass(opt.cls);
2976             }
2977             d.proxyDrag = opt.proxyDrag === true;
2978             d.modal = opt.modal !== false;
2979             d.mask = opt.modal !== false ? mask : false;
2980             if(!d.isVisible()){
2981                 // force it to the end of the z-index stack so it gets a cursor in FF
2982                 document.body.appendChild(dlg.el.dom);
2983                 d.animateTarget = null;
2984                 d.show(options.animEl);
2985             }
2986             return this;
2987         },
2988
2989         /**
2990          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2991          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2992          * and closing the message box when the process is complete.
2993          * @param {String} title The title bar text
2994          * @param {String} msg The message box body text
2995          * @return {Roo.MessageBox} This message box
2996          */
2997         progress : function(title, msg){
2998             this.show({
2999                 title : title,
3000                 msg : msg,
3001                 buttons: false,
3002                 progress:true,
3003                 closable:false,
3004                 minWidth: this.minProgressWidth,
3005                 modal : true
3006             });
3007             return this;
3008         },
3009
3010         /**
3011          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3012          * If a callback function is passed it will be called after the user clicks the button, and the
3013          * id of the button that was clicked will be passed as the only parameter to the callback
3014          * (could also be the top-right close button).
3015          * @param {String} title The title bar text
3016          * @param {String} msg The message box body text
3017          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3018          * @param {Object} scope (optional) The scope of the callback function
3019          * @return {Roo.MessageBox} This message box
3020          */
3021         alert : function(title, msg, fn, scope){
3022             this.show({
3023                 title : title,
3024                 msg : msg,
3025                 buttons: this.OK,
3026                 fn: fn,
3027                 scope : scope,
3028                 modal : true
3029             });
3030             return this;
3031         },
3032
3033         /**
3034          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3035          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3036          * You are responsible for closing the message box when the process is complete.
3037          * @param {String} msg The message box body text
3038          * @param {String} title (optional) The title bar text
3039          * @return {Roo.MessageBox} This message box
3040          */
3041         wait : function(msg, title){
3042             this.show({
3043                 title : title,
3044                 msg : msg,
3045                 buttons: false,
3046                 closable:false,
3047                 progress:true,
3048                 modal:true,
3049                 width:300,
3050                 wait:true
3051             });
3052             waitTimer = Roo.TaskMgr.start({
3053                 run: function(i){
3054                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3055                 },
3056                 interval: 1000
3057             });
3058             return this;
3059         },
3060
3061         /**
3062          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3063          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3064          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3065          * @param {String} title The title bar text
3066          * @param {String} msg The message box body text
3067          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3068          * @param {Object} scope (optional) The scope of the callback function
3069          * @return {Roo.MessageBox} This message box
3070          */
3071         confirm : function(title, msg, fn, scope){
3072             this.show({
3073                 title : title,
3074                 msg : msg,
3075                 buttons: this.YESNO,
3076                 fn: fn,
3077                 scope : scope,
3078                 modal : true
3079             });
3080             return this;
3081         },
3082
3083         /**
3084          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3085          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3086          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3087          * (could also be the top-right close button) and the text that was entered will be passed as the two
3088          * parameters to the callback.
3089          * @param {String} title The title bar text
3090          * @param {String} msg The message box body text
3091          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3092          * @param {Object} scope (optional) The scope of the callback function
3093          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3094          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3095          * @return {Roo.MessageBox} This message box
3096          */
3097         prompt : function(title, msg, fn, scope, multiline){
3098             this.show({
3099                 title : title,
3100                 msg : msg,
3101                 buttons: this.OKCANCEL,
3102                 fn: fn,
3103                 minWidth:250,
3104                 scope : scope,
3105                 prompt:true,
3106                 multiline: multiline,
3107                 modal : true
3108             });
3109             return this;
3110         },
3111
3112         /**
3113          * Button config that displays a single OK button
3114          * @type Object
3115          */
3116         OK : {ok:true},
3117         /**
3118          * Button config that displays Yes and No buttons
3119          * @type Object
3120          */
3121         YESNO : {yes:true, no:true},
3122         /**
3123          * Button config that displays OK and Cancel buttons
3124          * @type Object
3125          */
3126         OKCANCEL : {ok:true, cancel:true},
3127         /**
3128          * Button config that displays Yes, No and Cancel buttons
3129          * @type Object
3130          */
3131         YESNOCANCEL : {yes:true, no:true, cancel:true},
3132
3133         /**
3134          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3135          * @type Number
3136          */
3137         defaultTextHeight : 75,
3138         /**
3139          * The maximum width in pixels of the message box (defaults to 600)
3140          * @type Number
3141          */
3142         maxWidth : 600,
3143         /**
3144          * The minimum width in pixels of the message box (defaults to 100)
3145          * @type Number
3146          */
3147         minWidth : 100,
3148         /**
3149          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3150          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3151          * @type Number
3152          */
3153         minProgressWidth : 250,
3154         /**
3155          * An object containing the default button text strings that can be overriden for localized language support.
3156          * Supported properties are: ok, cancel, yes and no.
3157          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3158          * @type Object
3159          */
3160         buttonText : {
3161             ok : "OK",
3162             cancel : "Cancel",
3163             yes : "Yes",
3164             no : "No"
3165         }
3166     };
3167 }();
3168
3169 /**
3170  * Shorthand for {@link Roo.MessageBox}
3171  */
3172 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3173 Roo.Msg = Roo.Msg || Roo.MessageBox;
3174 /*
3175  * - LGPL
3176  *
3177  * navbar
3178  * 
3179  */
3180
3181 /**
3182  * @class Roo.bootstrap.Navbar
3183  * @extends Roo.bootstrap.Component
3184  * Bootstrap Navbar class
3185
3186  * @constructor
3187  * Create a new Navbar
3188  * @param {Object} config The config object
3189  */
3190
3191
3192 Roo.bootstrap.Navbar = function(config){
3193     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3194     
3195 };
3196
3197 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3198     
3199     
3200    
3201     // private
3202     navItems : false,
3203     loadMask : false,
3204     
3205     
3206     getAutoCreate : function(){
3207         
3208         
3209         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3210         
3211     },
3212     
3213     initEvents :function ()
3214     {
3215         //Roo.log(this.el.select('.navbar-toggle',true));
3216         this.el.select('.navbar-toggle',true).on('click', function() {
3217            // Roo.log('click');
3218             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3219         }, this);
3220         
3221         var mark = {
3222             tag: "div",
3223             cls:"x-dlg-mask"
3224         }
3225         
3226         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3227         
3228         var size = this.el.getSize();
3229         this.maskEl.setSize(size.width, size.height);
3230         this.maskEl.enableDisplayMode("block");
3231         this.maskEl.hide();
3232         
3233         if(this.loadMask){
3234             this.maskEl.show();
3235         }
3236     },
3237     
3238     
3239     getChildContainer : function()
3240     {
3241         if (this.el.select('.collapse').getCount()) {
3242             return this.el.select('.collapse',true).first();
3243         }
3244         
3245         return this.el;
3246     },
3247     
3248     mask : function()
3249     {
3250         this.maskEl.show();
3251     },
3252     
3253     unmask : function()
3254     {
3255         this.maskEl.hide();
3256     } 
3257     
3258     
3259     
3260     
3261 });
3262
3263
3264
3265  
3266
3267  /*
3268  * - LGPL
3269  *
3270  * navbar
3271  * 
3272  */
3273
3274 /**
3275  * @class Roo.bootstrap.NavSimplebar
3276  * @extends Roo.bootstrap.Navbar
3277  * Bootstrap Sidebar class
3278  *
3279  * @cfg {Boolean} inverse is inverted color
3280  * 
3281  * @cfg {String} type (nav | pills | tabs)
3282  * @cfg {Boolean} arrangement stacked | justified
3283  * @cfg {String} align (left | right) alignment
3284  * 
3285  * @cfg {Boolean} main (true|false) main nav bar? default false
3286  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3287  * 
3288  * @cfg {String} tag (header|footer|nav|div) default is nav 
3289
3290  * 
3291  * 
3292  * 
3293  * @constructor
3294  * Create a new Sidebar
3295  * @param {Object} config The config object
3296  */
3297
3298
3299 Roo.bootstrap.NavSimplebar = function(config){
3300     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3301 };
3302
3303 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3304     
3305     inverse: false,
3306     
3307     type: false,
3308     arrangement: '',
3309     align : false,
3310     
3311     
3312     
3313     main : false,
3314     
3315     
3316     tag : false,
3317     
3318     
3319     getAutoCreate : function(){
3320         
3321         
3322         var cfg = {
3323             tag : this.tag || 'div',
3324             cls : 'navbar'
3325         };
3326           
3327         
3328         cfg.cn = [
3329             {
3330                 cls: 'nav',
3331                 tag : 'ul'
3332             }
3333         ];
3334         
3335          
3336         this.type = this.type || 'nav';
3337         if (['tabs','pills'].indexOf(this.type)!==-1) {
3338             cfg.cn[0].cls += ' nav-' + this.type
3339         
3340         
3341         } else {
3342             if (this.type!=='nav') {
3343                 Roo.log('nav type must be nav/tabs/pills')
3344             }
3345             cfg.cn[0].cls += ' navbar-nav'
3346         }
3347         
3348         
3349         
3350         
3351         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3352             cfg.cn[0].cls += ' nav-' + this.arrangement;
3353         }
3354         
3355         
3356         if (this.align === 'right') {
3357             cfg.cn[0].cls += ' navbar-right';
3358         }
3359         
3360         if (this.inverse) {
3361             cfg.cls += ' navbar-inverse';
3362             
3363         }
3364         
3365         
3366         return cfg;
3367     
3368         
3369     }
3370     
3371     
3372     
3373 });
3374
3375
3376
3377  
3378
3379  
3380        /*
3381  * - LGPL
3382  *
3383  * navbar
3384  * 
3385  */
3386
3387 /**
3388  * @class Roo.bootstrap.NavHeaderbar
3389  * @extends Roo.bootstrap.NavSimplebar
3390  * Bootstrap Sidebar class
3391  *
3392  * @cfg {String} brand what is brand
3393  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3394  * @cfg {String} brand_href href of the brand
3395  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3396  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3397  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3398  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3399  * 
3400  * @constructor
3401  * Create a new Sidebar
3402  * @param {Object} config The config object
3403  */
3404
3405
3406 Roo.bootstrap.NavHeaderbar = function(config){
3407     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3408       
3409 };
3410
3411 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3412     
3413     position: '',
3414     brand: '',
3415     brand_href: false,
3416     srButton : true,
3417     autohide : false,
3418     desktopCenter : false,
3419    
3420     
3421     getAutoCreate : function(){
3422         
3423         var   cfg = {
3424             tag: this.nav || 'nav',
3425             cls: 'navbar',
3426             role: 'navigation',
3427             cn: []
3428         };
3429         
3430         var cn = cfg.cn;
3431         if (this.desktopCenter) {
3432             cn.push({cls : 'container', cn : []});
3433             cn = cn[0].cn;
3434         }
3435         
3436         if(this.srButton){
3437             cn.push({
3438                 tag: 'div',
3439                 cls: 'navbar-header',
3440                 cn: [
3441                     {
3442                         tag: 'button',
3443                         type: 'button',
3444                         cls: 'navbar-toggle',
3445                         'data-toggle': 'collapse',
3446                         cn: [
3447                             {
3448                                 tag: 'span',
3449                                 cls: 'sr-only',
3450                                 html: 'Toggle navigation'
3451                             },
3452                             {
3453                                 tag: 'span',
3454                                 cls: 'icon-bar'
3455                             },
3456                             {
3457                                 tag: 'span',
3458                                 cls: 'icon-bar'
3459                             },
3460                             {
3461                                 tag: 'span',
3462                                 cls: 'icon-bar'
3463                             }
3464                         ]
3465                     }
3466                 ]
3467             });
3468         }
3469         
3470         cn.push({
3471             tag: 'div',
3472             cls: 'collapse navbar-collapse',
3473             cn : []
3474         });
3475         
3476         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3477         
3478         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3479             cfg.cls += ' navbar-' + this.position;
3480             
3481             // tag can override this..
3482             
3483             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3484         }
3485         
3486         if (this.brand !== '') {
3487             cn[0].cn.push({
3488                 tag: 'a',
3489                 href: this.brand_href ? this.brand_href : '#',
3490                 cls: 'navbar-brand',
3491                 cn: [
3492                 this.brand
3493                 ]
3494             });
3495         }
3496         
3497         if(this.main){
3498             cfg.cls += ' main-nav';
3499         }
3500         
3501         
3502         return cfg;
3503
3504         
3505     },
3506     getHeaderChildContainer : function()
3507     {
3508         if (this.el.select('.navbar-header').getCount()) {
3509             return this.el.select('.navbar-header',true).first();
3510         }
3511         
3512         return this.getChildContainer();
3513     },
3514     
3515     
3516     initEvents : function()
3517     {
3518         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3519         
3520         if (this.autohide) {
3521             
3522             var prevScroll = 0;
3523             var ft = this.el;
3524             
3525             Roo.get(document).on('scroll',function(e) {
3526                 var ns = Roo.get(document).getScroll().top;
3527                 var os = prevScroll;
3528                 prevScroll = ns;
3529                 
3530                 if(ns > os){
3531                     ft.removeClass('slideDown');
3532                     ft.addClass('slideUp');
3533                     return;
3534                 }
3535                 ft.removeClass('slideUp');
3536                 ft.addClass('slideDown');
3537                  
3538               
3539           },this);
3540         }
3541     }    
3542     
3543 });
3544
3545
3546
3547  
3548
3549  /*
3550  * - LGPL
3551  *
3552  * navbar
3553  * 
3554  */
3555
3556 /**
3557  * @class Roo.bootstrap.NavSidebar
3558  * @extends Roo.bootstrap.Navbar
3559  * Bootstrap Sidebar class
3560  * 
3561  * @constructor
3562  * Create a new Sidebar
3563  * @param {Object} config The config object
3564  */
3565
3566
3567 Roo.bootstrap.NavSidebar = function(config){
3568     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3569 };
3570
3571 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3572     
3573     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3574     
3575     getAutoCreate : function(){
3576         
3577         
3578         return  {
3579             tag: 'div',
3580             cls: 'sidebar sidebar-nav'
3581         };
3582     
3583         
3584     }
3585     
3586     
3587     
3588 });
3589
3590
3591
3592  
3593
3594  /*
3595  * - LGPL
3596  *
3597  * nav group
3598  * 
3599  */
3600
3601 /**
3602  * @class Roo.bootstrap.NavGroup
3603  * @extends Roo.bootstrap.Component
3604  * Bootstrap NavGroup class
3605  * @cfg {String} align left | right
3606  * @cfg {Boolean} inverse false | true
3607  * @cfg {String} type (nav|pills|tab) default nav
3608  * @cfg {String} navId - reference Id for navbar.
3609
3610  * 
3611  * @constructor
3612  * Create a new nav group
3613  * @param {Object} config The config object
3614  */
3615
3616 Roo.bootstrap.NavGroup = function(config){
3617     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3618     this.navItems = [];
3619    
3620     Roo.bootstrap.NavGroup.register(this);
3621      this.addEvents({
3622         /**
3623              * @event changed
3624              * Fires when the active item changes
3625              * @param {Roo.bootstrap.NavGroup} this
3626              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3627              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3628          */
3629         'changed': true
3630      });
3631     
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3635     
3636     align: '',
3637     inverse: false,
3638     form: false,
3639     type: 'nav',
3640     navId : '',
3641     // private
3642     
3643     navItems : false, 
3644     
3645     getAutoCreate : function()
3646     {
3647         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3648         
3649         cfg = {
3650             tag : 'ul',
3651             cls: 'nav' 
3652         }
3653         
3654         if (['tabs','pills'].indexOf(this.type)!==-1) {
3655             cfg.cls += ' nav-' + this.type
3656         } else {
3657             if (this.type!=='nav') {
3658                 Roo.log('nav type must be nav/tabs/pills')
3659             }
3660             cfg.cls += ' navbar-nav'
3661         }
3662         
3663         if (this.parent().sidebar) {
3664             cfg = {
3665                 tag: 'ul',
3666                 cls: 'dashboard-menu sidebar-menu'
3667             }
3668             
3669             return cfg;
3670         }
3671         
3672         if (this.form === true) {
3673             cfg = {
3674                 tag: 'form',
3675                 cls: 'navbar-form'
3676             }
3677             
3678             if (this.align === 'right') {
3679                 cfg.cls += ' navbar-right';
3680             } else {
3681                 cfg.cls += ' navbar-left';
3682             }
3683         }
3684         
3685         if (this.align === 'right') {
3686             cfg.cls += ' navbar-right';
3687         }
3688         
3689         if (this.inverse) {
3690             cfg.cls += ' navbar-inverse';
3691             
3692         }
3693         
3694         
3695         return cfg;
3696     },
3697     /**
3698     * sets the active Navigation item
3699     * @param {Roo.bootstrap.NavItem} the new current navitem
3700     */
3701     setActiveItem : function(item)
3702     {
3703         var prev = false;
3704         Roo.each(this.navItems, function(v){
3705             if (v == item) {
3706                 return ;
3707             }
3708             if (v.isActive()) {
3709                 v.setActive(false, true);
3710                 prev = v;
3711                 
3712             }
3713             
3714         });
3715
3716         item.setActive(true, true);
3717         this.fireEvent('changed', this, item, prev);
3718         
3719         
3720     },
3721     /**
3722     * gets the active Navigation item
3723     * @return {Roo.bootstrap.NavItem} the current navitem
3724     */
3725     getActive : function()
3726     {
3727         
3728         var prev = false;
3729         Roo.each(this.navItems, function(v){
3730             
3731             if (v.isActive()) {
3732                 prev = v;
3733                 
3734             }
3735             
3736         });
3737         return prev;
3738     },
3739     
3740     indexOfNav : function()
3741     {
3742         
3743         var prev = false;
3744         Roo.each(this.navItems, function(v,i){
3745             
3746             if (v.isActive()) {
3747                 prev = i;
3748                 
3749             }
3750             
3751         });
3752         return prev;
3753     },
3754     /**
3755     * adds a Navigation item
3756     * @param {Roo.bootstrap.NavItem} the navitem to add
3757     */
3758     addItem : function(cfg)
3759     {
3760         var cn = new Roo.bootstrap.NavItem(cfg);
3761         this.register(cn);
3762         cn.parentId = this.id;
3763         cn.onRender(this.el, null);
3764         return cn;
3765     },
3766     /**
3767     * register a Navigation item
3768     * @param {Roo.bootstrap.NavItem} the navitem to add
3769     */
3770     register : function(item)
3771     {
3772         this.navItems.push( item);
3773         item.navId = this.navId;
3774     
3775     },
3776     
3777     /**
3778     * clear all the Navigation item
3779     */
3780    
3781     clearAll : function()
3782     {
3783         this.navItems = [];
3784         this.el.dom.innerHTML = '';
3785     },
3786     
3787     getNavItem: function(tabId)
3788     {
3789         var ret = false;
3790         Roo.each(this.navItems, function(e) {
3791             if (e.tabId == tabId) {
3792                ret =  e;
3793                return false;
3794             }
3795             return true;
3796             
3797         });
3798         return ret;
3799     },
3800     
3801     setActiveNext : function()
3802     {
3803         var i = this.indexOfNav(this.getActive());
3804         if (i > this.navItems.length) {
3805             return;
3806         }
3807         this.setActiveItem(this.navItems[i+1]);
3808     },
3809     setActivePrev : function()
3810     {
3811         var i = this.indexOfNav(this.getActive());
3812         if (i  < 1) {
3813             return;
3814         }
3815         this.setActiveItem(this.navItems[i-1]);
3816     },
3817     clearWasActive : function(except) {
3818         Roo.each(this.navItems, function(e) {
3819             if (e.tabId != except.tabId && e.was_active) {
3820                e.was_active = false;
3821                return false;
3822             }
3823             return true;
3824             
3825         });
3826     },
3827     getWasActive : function ()
3828     {
3829         var r = false;
3830         Roo.each(this.navItems, function(e) {
3831             if (e.was_active) {
3832                r = e;
3833                return false;
3834             }
3835             return true;
3836             
3837         });
3838         return r;
3839     }
3840     
3841     
3842 });
3843
3844  
3845 Roo.apply(Roo.bootstrap.NavGroup, {
3846     
3847     groups: {},
3848      /**
3849     * register a Navigation Group
3850     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3851     */
3852     register : function(navgrp)
3853     {
3854         this.groups[navgrp.navId] = navgrp;
3855         
3856     },
3857     /**
3858     * fetch a Navigation Group based on the navigation ID
3859     * @param {string} the navgroup to add
3860     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3861     */
3862     get: function(navId) {
3863         if (typeof(this.groups[navId]) == 'undefined') {
3864             return false;
3865             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3866         }
3867         return this.groups[navId] ;
3868     }
3869     
3870     
3871     
3872 });
3873
3874  /*
3875  * - LGPL
3876  *
3877  * row
3878  * 
3879  */
3880
3881 /**
3882  * @class Roo.bootstrap.NavItem
3883  * @extends Roo.bootstrap.Component
3884  * Bootstrap Navbar.NavItem class
3885  * @cfg {String} href  link to
3886  * @cfg {String} html content of button
3887  * @cfg {String} badge text inside badge
3888  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3889  * @cfg {String} glyphicon name of glyphicon
3890  * @cfg {String} icon name of font awesome icon
3891  * @cfg {Boolean} active Is item active
3892  * @cfg {Boolean} disabled Is item disabled
3893  
3894  * @cfg {Boolean} preventDefault (true | false) default false
3895  * @cfg {String} tabId the tab that this item activates.
3896  * @cfg {String} tagtype (a|span) render as a href or span?
3897  * @cfg {Boolean} animateRef (true|false) link to element default false  
3898   
3899  * @constructor
3900  * Create a new Navbar Item
3901  * @param {Object} config The config object
3902  */
3903 Roo.bootstrap.NavItem = function(config){
3904     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3905     this.addEvents({
3906         // raw events
3907         /**
3908          * @event click
3909          * The raw click event for the entire grid.
3910          * @param {Roo.EventObject} e
3911          */
3912         "click" : true,
3913          /**
3914             * @event changed
3915             * Fires when the active item active state changes
3916             * @param {Roo.bootstrap.NavItem} this
3917             * @param {boolean} state the new state
3918              
3919          */
3920         'changed': true,
3921         /**
3922             * @event scrollto
3923             * Fires when scroll to element
3924             * @param {Roo.bootstrap.NavItem} this
3925             * @param {Object} options
3926             * @param {Roo.EventObject} e
3927              
3928          */
3929         'scrollto': true
3930     });
3931    
3932 };
3933
3934 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3935     
3936     href: false,
3937     html: '',
3938     badge: '',
3939     icon: false,
3940     glyphicon: false,
3941     active: false,
3942     preventDefault : false,
3943     tabId : false,
3944     tagtype : 'a',
3945     disabled : false,
3946     animateRef : false,
3947     was_active : false,
3948     
3949     getAutoCreate : function(){
3950          
3951         var cfg = {
3952             tag: 'li',
3953             cls: 'nav-item'
3954             
3955         }
3956         if (this.active) {
3957             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3958         }
3959         if (this.disabled) {
3960             cfg.cls += ' disabled';
3961         }
3962         
3963         if (this.href || this.html || this.glyphicon || this.icon) {
3964             cfg.cn = [
3965                 {
3966                     tag: this.tagtype,
3967                     href : this.href || "#",
3968                     html: this.html || ''
3969                 }
3970             ];
3971             
3972             if (this.icon) {
3973                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3974             }
3975
3976             if(this.glyphicon) {
3977                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3978             }
3979             
3980             if (this.menu) {
3981                 
3982                 cfg.cn[0].html += " <span class='caret'></span>";
3983              
3984             }
3985             
3986             if (this.badge !== '') {
3987                  
3988                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3989             }
3990         }
3991         
3992         
3993         
3994         return cfg;
3995     },
3996     initEvents: function() 
3997     {
3998         if (typeof (this.menu) != 'undefined') {
3999             this.menu.parentType = this.xtype;
4000             this.menu.triggerEl = this.el;
4001             this.menu = this.addxtype(Roo.apply({}, this.menu));
4002         }
4003         
4004         this.el.select('a',true).on('click', this.onClick, this);
4005         
4006         if(this.tagtype == 'span'){
4007             this.el.select('span',true).on('click', this.onClick, this);
4008         }
4009        
4010         // at this point parent should be available..
4011         this.parent().register(this);
4012     },
4013     
4014     onClick : function(e)
4015     {
4016         if(
4017                 this.preventDefault || 
4018                 this.href == '#' 
4019         ){
4020             
4021             e.preventDefault();
4022         }
4023         
4024         if (this.disabled) {
4025             return;
4026         }
4027         
4028         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4029         if (tg && tg.transition) {
4030             Roo.log("waiting for the transitionend");
4031             return;
4032         }
4033         
4034         
4035         
4036         //Roo.log("fire event clicked");
4037         if(this.fireEvent('click', this, e) === false){
4038             return;
4039         };
4040         
4041         if(this.tagtype == 'span'){
4042             return;
4043         }
4044         
4045         //Roo.log(this.href);
4046         var ael = this.el.select('a',true).first();
4047         //Roo.log(ael);
4048         
4049         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4050             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4051             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4052                 return; // ignore... - it's a 'hash' to another page.
4053             }
4054             
4055             e.preventDefault();
4056             this.scrollToElement(e);
4057         }
4058         
4059         
4060         var p =  this.parent();
4061    
4062         if (['tabs','pills'].indexOf(p.type)!==-1) {
4063             if (typeof(p.setActiveItem) !== 'undefined') {
4064                 p.setActiveItem(this);
4065             }
4066         }
4067         
4068         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4069         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4070             // remove the collapsed menu expand...
4071             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4072         }
4073     },
4074     
4075     isActive: function () {
4076         return this.active
4077     },
4078     setActive : function(state, fire, is_was_active)
4079     {
4080         if (this.active && !state & this.navId) {
4081             this.was_active = true;
4082             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4083             if (nv) {
4084                 nv.clearWasActive(this);
4085             }
4086             
4087         }
4088         this.active = state;
4089         
4090         if (!state ) {
4091             this.el.removeClass('active');
4092         } else if (!this.el.hasClass('active')) {
4093             this.el.addClass('active');
4094         }
4095         if (fire) {
4096             this.fireEvent('changed', this, state);
4097         }
4098         
4099         // show a panel if it's registered and related..
4100         
4101         if (!this.navId || !this.tabId || !state || is_was_active) {
4102             return;
4103         }
4104         
4105         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4106         if (!tg) {
4107             return;
4108         }
4109         var pan = tg.getPanelByName(this.tabId);
4110         if (!pan) {
4111             return;
4112         }
4113         // if we can not flip to new panel - go back to old nav highlight..
4114         if (false == tg.showPanel(pan)) {
4115             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4116             if (nv) {
4117                 var onav = nv.getWasActive();
4118                 if (onav) {
4119                     onav.setActive(true, false, true);
4120                 }
4121             }
4122             
4123         }
4124         
4125         
4126         
4127     },
4128      // this should not be here...
4129     setDisabled : function(state)
4130     {
4131         this.disabled = state;
4132         if (!state ) {
4133             this.el.removeClass('disabled');
4134         } else if (!this.el.hasClass('disabled')) {
4135             this.el.addClass('disabled');
4136         }
4137         
4138     },
4139     
4140     /**
4141      * Fetch the element to display the tooltip on.
4142      * @return {Roo.Element} defaults to this.el
4143      */
4144     tooltipEl : function()
4145     {
4146         return this.el.select('' + this.tagtype + '', true).first();
4147     },
4148     
4149     scrollToElement : function(e)
4150     {
4151         var c = document.body;
4152         
4153         /*
4154          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4155          */
4156         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4157             c = document.documentElement;
4158         }
4159         
4160         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4161         
4162         if(!target){
4163             return;
4164         }
4165
4166         var o = target.calcOffsetsTo(c);
4167         
4168         var options = {
4169             target : target,
4170             value : o[1]
4171         }
4172         
4173         this.fireEvent('scrollto', this, options, e);
4174         
4175         Roo.get(c).scrollTo('top', options.value, true);
4176         
4177         return;
4178     }
4179 });
4180  
4181
4182  /*
4183  * - LGPL
4184  *
4185  * sidebar item
4186  *
4187  *  li
4188  *    <span> icon </span>
4189  *    <span> text </span>
4190  *    <span>badge </span>
4191  */
4192
4193 /**
4194  * @class Roo.bootstrap.NavSidebarItem
4195  * @extends Roo.bootstrap.NavItem
4196  * Bootstrap Navbar.NavSidebarItem class
4197  * @constructor
4198  * Create a new Navbar Button
4199  * @param {Object} config The config object
4200  */
4201 Roo.bootstrap.NavSidebarItem = function(config){
4202     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4203     this.addEvents({
4204         // raw events
4205         /**
4206          * @event click
4207          * The raw click event for the entire grid.
4208          * @param {Roo.EventObject} e
4209          */
4210         "click" : true,
4211          /**
4212             * @event changed
4213             * Fires when the active item active state changes
4214             * @param {Roo.bootstrap.NavSidebarItem} this
4215             * @param {boolean} state the new state
4216              
4217          */
4218         'changed': true
4219     });
4220    
4221 };
4222
4223 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4224     
4225     
4226     getAutoCreate : function(){
4227         
4228         
4229         var a = {
4230                 tag: 'a',
4231                 href : this.href || '#',
4232                 cls: '',
4233                 html : '',
4234                 cn : []
4235         };
4236         var cfg = {
4237             tag: 'li',
4238             cls: '',
4239             cn: [ a ]
4240         }
4241         var span = {
4242             tag: 'span',
4243             html : this.html || ''
4244         }
4245         
4246         
4247         if (this.active) {
4248             cfg.cls += ' active';
4249         }
4250         
4251         // left icon..
4252         if (this.glyphicon || this.icon) {
4253             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4254             a.cn.push({ tag : 'i', cls : c }) ;
4255         }
4256         // html..
4257         a.cn.push(span);
4258         // then badge..
4259         if (this.badge !== '') {
4260             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4261         }
4262         // fi
4263         if (this.menu) {
4264             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4265             a.cls += 'dropdown-toggle treeview' ;
4266             
4267         }
4268         
4269         
4270         
4271         return cfg;
4272          
4273            
4274     }
4275    
4276      
4277  
4278 });
4279  
4280
4281  /*
4282  * - LGPL
4283  *
4284  * row
4285  * 
4286  */
4287
4288 /**
4289  * @class Roo.bootstrap.Row
4290  * @extends Roo.bootstrap.Component
4291  * Bootstrap Row class (contains columns...)
4292  * 
4293  * @constructor
4294  * Create a new Row
4295  * @param {Object} config The config object
4296  */
4297
4298 Roo.bootstrap.Row = function(config){
4299     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4300 };
4301
4302 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4303     
4304     getAutoCreate : function(){
4305        return {
4306             cls: 'row clearfix'
4307        };
4308     }
4309     
4310     
4311 });
4312
4313  
4314
4315  /*
4316  * - LGPL
4317  *
4318  * element
4319  * 
4320  */
4321
4322 /**
4323  * @class Roo.bootstrap.Element
4324  * @extends Roo.bootstrap.Component
4325  * Bootstrap Element class
4326  * @cfg {String} html contents of the element
4327  * @cfg {String} tag tag of the element
4328  * @cfg {String} cls class of the element
4329  * @cfg {Boolean} preventDefault (true|false) default false
4330  * @cfg {Boolean} clickable (true|false) default false
4331  * 
4332  * @constructor
4333  * Create a new Element
4334  * @param {Object} config The config object
4335  */
4336
4337 Roo.bootstrap.Element = function(config){
4338     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4339     
4340     this.addEvents({
4341         // raw events
4342         /**
4343          * @event click
4344          * When a element is chick
4345          * @param {Roo.bootstrap.Element} this
4346          * @param {Roo.EventObject} e
4347          */
4348         "click" : true
4349     });
4350 };
4351
4352 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4353     
4354     tag: 'div',
4355     cls: '',
4356     html: '',
4357     preventDefault: false, 
4358     clickable: false,
4359     
4360     getAutoCreate : function(){
4361         
4362         var cfg = {
4363             tag: this.tag,
4364             cls: this.cls,
4365             html: this.html
4366         }
4367         
4368         return cfg;
4369     },
4370     
4371     initEvents: function() 
4372     {
4373         Roo.bootstrap.Element.superclass.initEvents.call(this);
4374         
4375         if(this.clickable){
4376             this.el.on('click', this.onClick, this);
4377         }
4378         
4379     },
4380     
4381     onClick : function(e)
4382     {
4383         if(this.preventDefault){
4384             e.preventDefault();
4385         }
4386         
4387         this.fireEvent('click', this, e);
4388     },
4389     
4390     getValue : function()
4391     {
4392         return this.el.dom.innerHTML;
4393     },
4394     
4395     setValue : function(value)
4396     {
4397         this.el.dom.innerHTML = value;
4398     }
4399    
4400 });
4401
4402  
4403
4404  /*
4405  * - LGPL
4406  *
4407  * pagination
4408  * 
4409  */
4410
4411 /**
4412  * @class Roo.bootstrap.Pagination
4413  * @extends Roo.bootstrap.Component
4414  * Bootstrap Pagination class
4415  * @cfg {String} size xs | sm | md | lg
4416  * @cfg {Boolean} inverse false | true
4417  * 
4418  * @constructor
4419  * Create a new Pagination
4420  * @param {Object} config The config object
4421  */
4422
4423 Roo.bootstrap.Pagination = function(config){
4424     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4425 };
4426
4427 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4428     
4429     cls: false,
4430     size: false,
4431     inverse: false,
4432     
4433     getAutoCreate : function(){
4434         var cfg = {
4435             tag: 'ul',
4436                 cls: 'pagination'
4437         };
4438         if (this.inverse) {
4439             cfg.cls += ' inverse';
4440         }
4441         if (this.html) {
4442             cfg.html=this.html;
4443         }
4444         if (this.cls) {
4445             cfg.cls += " " + this.cls;
4446         }
4447         return cfg;
4448     }
4449    
4450 });
4451
4452  
4453
4454  /*
4455  * - LGPL
4456  *
4457  * Pagination item
4458  * 
4459  */
4460
4461
4462 /**
4463  * @class Roo.bootstrap.PaginationItem
4464  * @extends Roo.bootstrap.Component
4465  * Bootstrap PaginationItem class
4466  * @cfg {String} html text
4467  * @cfg {String} href the link
4468  * @cfg {Boolean} preventDefault (true | false) default true
4469  * @cfg {Boolean} active (true | false) default false
4470  * @cfg {Boolean} disabled default false
4471  * 
4472  * 
4473  * @constructor
4474  * Create a new PaginationItem
4475  * @param {Object} config The config object
4476  */
4477
4478
4479 Roo.bootstrap.PaginationItem = function(config){
4480     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4481     this.addEvents({
4482         // raw events
4483         /**
4484          * @event click
4485          * The raw click event for the entire grid.
4486          * @param {Roo.EventObject} e
4487          */
4488         "click" : true
4489     });
4490 };
4491
4492 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4493     
4494     href : false,
4495     html : false,
4496     preventDefault: true,
4497     active : false,
4498     cls : false,
4499     disabled: false,
4500     
4501     getAutoCreate : function(){
4502         var cfg= {
4503             tag: 'li',
4504             cn: [
4505                 {
4506                     tag : 'a',
4507                     href : this.href ? this.href : '#',
4508                     html : this.html ? this.html : ''
4509                 }
4510             ]
4511         };
4512         
4513         if(this.cls){
4514             cfg.cls = this.cls;
4515         }
4516         
4517         if(this.disabled){
4518             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4519         }
4520         
4521         if(this.active){
4522             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4523         }
4524         
4525         return cfg;
4526     },
4527     
4528     initEvents: function() {
4529         
4530         this.el.on('click', this.onClick, this);
4531         
4532     },
4533     onClick : function(e)
4534     {
4535         Roo.log('PaginationItem on click ');
4536         if(this.preventDefault){
4537             e.preventDefault();
4538         }
4539         
4540         if(this.disabled){
4541             return;
4542         }
4543         
4544         this.fireEvent('click', this, e);
4545     }
4546    
4547 });
4548
4549  
4550
4551  /*
4552  * - LGPL
4553  *
4554  * slider
4555  * 
4556  */
4557
4558
4559 /**
4560  * @class Roo.bootstrap.Slider
4561  * @extends Roo.bootstrap.Component
4562  * Bootstrap Slider class
4563  *    
4564  * @constructor
4565  * Create a new Slider
4566  * @param {Object} config The config object
4567  */
4568
4569 Roo.bootstrap.Slider = function(config){
4570     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4571 };
4572
4573 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4574     
4575     getAutoCreate : function(){
4576         
4577         var cfg = {
4578             tag: 'div',
4579             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4580             cn: [
4581                 {
4582                     tag: 'a',
4583                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4584                 }
4585             ]
4586         }
4587         
4588         return cfg;
4589     }
4590    
4591 });
4592
4593  /*
4594  * Based on:
4595  * Ext JS Library 1.1.1
4596  * Copyright(c) 2006-2007, Ext JS, LLC.
4597  *
4598  * Originally Released Under LGPL - original licence link has changed is not relivant.
4599  *
4600  * Fork - LGPL
4601  * <script type="text/javascript">
4602  */
4603  
4604
4605 /**
4606  * @class Roo.grid.ColumnModel
4607  * @extends Roo.util.Observable
4608  * This is the default implementation of a ColumnModel used by the Grid. It defines
4609  * the columns in the grid.
4610  * <br>Usage:<br>
4611  <pre><code>
4612  var colModel = new Roo.grid.ColumnModel([
4613         {header: "Ticker", width: 60, sortable: true, locked: true},
4614         {header: "Company Name", width: 150, sortable: true},
4615         {header: "Market Cap.", width: 100, sortable: true},
4616         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4617         {header: "Employees", width: 100, sortable: true, resizable: false}
4618  ]);
4619  </code></pre>
4620  * <p>
4621  
4622  * The config options listed for this class are options which may appear in each
4623  * individual column definition.
4624  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4625  * @constructor
4626  * @param {Object} config An Array of column config objects. See this class's
4627  * config objects for details.
4628 */
4629 Roo.grid.ColumnModel = function(config){
4630         /**
4631      * The config passed into the constructor
4632      */
4633     this.config = config;
4634     this.lookup = {};
4635
4636     // if no id, create one
4637     // if the column does not have a dataIndex mapping,
4638     // map it to the order it is in the config
4639     for(var i = 0, len = config.length; i < len; i++){
4640         var c = config[i];
4641         if(typeof c.dataIndex == "undefined"){
4642             c.dataIndex = i;
4643         }
4644         if(typeof c.renderer == "string"){
4645             c.renderer = Roo.util.Format[c.renderer];
4646         }
4647         if(typeof c.id == "undefined"){
4648             c.id = Roo.id();
4649         }
4650         if(c.editor && c.editor.xtype){
4651             c.editor  = Roo.factory(c.editor, Roo.grid);
4652         }
4653         if(c.editor && c.editor.isFormField){
4654             c.editor = new Roo.grid.GridEditor(c.editor);
4655         }
4656         this.lookup[c.id] = c;
4657     }
4658
4659     /**
4660      * The width of columns which have no width specified (defaults to 100)
4661      * @type Number
4662      */
4663     this.defaultWidth = 100;
4664
4665     /**
4666      * Default sortable of columns which have no sortable specified (defaults to false)
4667      * @type Boolean
4668      */
4669     this.defaultSortable = false;
4670
4671     this.addEvents({
4672         /**
4673              * @event widthchange
4674              * Fires when the width of a column changes.
4675              * @param {ColumnModel} this
4676              * @param {Number} columnIndex The column index
4677              * @param {Number} newWidth The new width
4678              */
4679             "widthchange": true,
4680         /**
4681              * @event headerchange
4682              * Fires when the text of a header changes.
4683              * @param {ColumnModel} this
4684              * @param {Number} columnIndex The column index
4685              * @param {Number} newText The new header text
4686              */
4687             "headerchange": true,
4688         /**
4689              * @event hiddenchange
4690              * Fires when a column is hidden or "unhidden".
4691              * @param {ColumnModel} this
4692              * @param {Number} columnIndex The column index
4693              * @param {Boolean} hidden true if hidden, false otherwise
4694              */
4695             "hiddenchange": true,
4696             /**
4697          * @event columnmoved
4698          * Fires when a column is moved.
4699          * @param {ColumnModel} this
4700          * @param {Number} oldIndex
4701          * @param {Number} newIndex
4702          */
4703         "columnmoved" : true,
4704         /**
4705          * @event columlockchange
4706          * Fires when a column's locked state is changed
4707          * @param {ColumnModel} this
4708          * @param {Number} colIndex
4709          * @param {Boolean} locked true if locked
4710          */
4711         "columnlockchange" : true
4712     });
4713     Roo.grid.ColumnModel.superclass.constructor.call(this);
4714 };
4715 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4716     /**
4717      * @cfg {String} header The header text to display in the Grid view.
4718      */
4719     /**
4720      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4721      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4722      * specified, the column's index is used as an index into the Record's data Array.
4723      */
4724     /**
4725      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4726      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4727      */
4728     /**
4729      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4730      * Defaults to the value of the {@link #defaultSortable} property.
4731      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4732      */
4733     /**
4734      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4735      */
4736     /**
4737      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4738      */
4739     /**
4740      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4741      */
4742     /**
4743      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4744      */
4745     /**
4746      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4747      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4748      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4749      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4750      */
4751        /**
4752      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4753      */
4754     /**
4755      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4756      */
4757     /**
4758      * @cfg {String} cursor (Optional)
4759      */
4760     /**
4761      * @cfg {String} tooltip (Optional)
4762      */
4763     /**
4764      * Returns the id of the column at the specified index.
4765      * @param {Number} index The column index
4766      * @return {String} the id
4767      */
4768     getColumnId : function(index){
4769         return this.config[index].id;
4770     },
4771
4772     /**
4773      * Returns the column for a specified id.
4774      * @param {String} id The column id
4775      * @return {Object} the column
4776      */
4777     getColumnById : function(id){
4778         return this.lookup[id];
4779     },
4780
4781     
4782     /**
4783      * Returns the column for a specified dataIndex.
4784      * @param {String} dataIndex The column dataIndex
4785      * @return {Object|Boolean} the column or false if not found
4786      */
4787     getColumnByDataIndex: function(dataIndex){
4788         var index = this.findColumnIndex(dataIndex);
4789         return index > -1 ? this.config[index] : false;
4790     },
4791     
4792     /**
4793      * Returns the index for a specified column id.
4794      * @param {String} id The column id
4795      * @return {Number} the index, or -1 if not found
4796      */
4797     getIndexById : function(id){
4798         for(var i = 0, len = this.config.length; i < len; i++){
4799             if(this.config[i].id == id){
4800                 return i;
4801             }
4802         }
4803         return -1;
4804     },
4805     
4806     /**
4807      * Returns the index for a specified column dataIndex.
4808      * @param {String} dataIndex The column dataIndex
4809      * @return {Number} the index, or -1 if not found
4810      */
4811     
4812     findColumnIndex : function(dataIndex){
4813         for(var i = 0, len = this.config.length; i < len; i++){
4814             if(this.config[i].dataIndex == dataIndex){
4815                 return i;
4816             }
4817         }
4818         return -1;
4819     },
4820     
4821     
4822     moveColumn : function(oldIndex, newIndex){
4823         var c = this.config[oldIndex];
4824         this.config.splice(oldIndex, 1);
4825         this.config.splice(newIndex, 0, c);
4826         this.dataMap = null;
4827         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4828     },
4829
4830     isLocked : function(colIndex){
4831         return this.config[colIndex].locked === true;
4832     },
4833
4834     setLocked : function(colIndex, value, suppressEvent){
4835         if(this.isLocked(colIndex) == value){
4836             return;
4837         }
4838         this.config[colIndex].locked = value;
4839         if(!suppressEvent){
4840             this.fireEvent("columnlockchange", this, colIndex, value);
4841         }
4842     },
4843
4844     getTotalLockedWidth : function(){
4845         var totalWidth = 0;
4846         for(var i = 0; i < this.config.length; i++){
4847             if(this.isLocked(i) && !this.isHidden(i)){
4848                 this.totalWidth += this.getColumnWidth(i);
4849             }
4850         }
4851         return totalWidth;
4852     },
4853
4854     getLockedCount : function(){
4855         for(var i = 0, len = this.config.length; i < len; i++){
4856             if(!this.isLocked(i)){
4857                 return i;
4858             }
4859         }
4860     },
4861
4862     /**
4863      * Returns the number of columns.
4864      * @return {Number}
4865      */
4866     getColumnCount : function(visibleOnly){
4867         if(visibleOnly === true){
4868             var c = 0;
4869             for(var i = 0, len = this.config.length; i < len; i++){
4870                 if(!this.isHidden(i)){
4871                     c++;
4872                 }
4873             }
4874             return c;
4875         }
4876         return this.config.length;
4877     },
4878
4879     /**
4880      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4881      * @param {Function} fn
4882      * @param {Object} scope (optional)
4883      * @return {Array} result
4884      */
4885     getColumnsBy : function(fn, scope){
4886         var r = [];
4887         for(var i = 0, len = this.config.length; i < len; i++){
4888             var c = this.config[i];
4889             if(fn.call(scope||this, c, i) === true){
4890                 r[r.length] = c;
4891             }
4892         }
4893         return r;
4894     },
4895
4896     /**
4897      * Returns true if the specified column is sortable.
4898      * @param {Number} col The column index
4899      * @return {Boolean}
4900      */
4901     isSortable : function(col){
4902         if(typeof this.config[col].sortable == "undefined"){
4903             return this.defaultSortable;
4904         }
4905         return this.config[col].sortable;
4906     },
4907
4908     /**
4909      * Returns the rendering (formatting) function defined for the column.
4910      * @param {Number} col The column index.
4911      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4912      */
4913     getRenderer : function(col){
4914         if(!this.config[col].renderer){
4915             return Roo.grid.ColumnModel.defaultRenderer;
4916         }
4917         return this.config[col].renderer;
4918     },
4919
4920     /**
4921      * Sets the rendering (formatting) function for a column.
4922      * @param {Number} col The column index
4923      * @param {Function} fn The function to use to process the cell's raw data
4924      * to return HTML markup for the grid view. The render function is called with
4925      * the following parameters:<ul>
4926      * <li>Data value.</li>
4927      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4928      * <li>css A CSS style string to apply to the table cell.</li>
4929      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4930      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4931      * <li>Row index</li>
4932      * <li>Column index</li>
4933      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4934      */
4935     setRenderer : function(col, fn){
4936         this.config[col].renderer = fn;
4937     },
4938
4939     /**
4940      * Returns the width for the specified column.
4941      * @param {Number} col The column index
4942      * @return {Number}
4943      */
4944     getColumnWidth : function(col){
4945         return this.config[col].width * 1 || this.defaultWidth;
4946     },
4947
4948     /**
4949      * Sets the width for a column.
4950      * @param {Number} col The column index
4951      * @param {Number} width The new width
4952      */
4953     setColumnWidth : function(col, width, suppressEvent){
4954         this.config[col].width = width;
4955         this.totalWidth = null;
4956         if(!suppressEvent){
4957              this.fireEvent("widthchange", this, col, width);
4958         }
4959     },
4960
4961     /**
4962      * Returns the total width of all columns.
4963      * @param {Boolean} includeHidden True to include hidden column widths
4964      * @return {Number}
4965      */
4966     getTotalWidth : function(includeHidden){
4967         if(!this.totalWidth){
4968             this.totalWidth = 0;
4969             for(var i = 0, len = this.config.length; i < len; i++){
4970                 if(includeHidden || !this.isHidden(i)){
4971                     this.totalWidth += this.getColumnWidth(i);
4972                 }
4973             }
4974         }
4975         return this.totalWidth;
4976     },
4977
4978     /**
4979      * Returns the header for the specified column.
4980      * @param {Number} col The column index
4981      * @return {String}
4982      */
4983     getColumnHeader : function(col){
4984         return this.config[col].header;
4985     },
4986
4987     /**
4988      * Sets the header for a column.
4989      * @param {Number} col The column index
4990      * @param {String} header The new header
4991      */
4992     setColumnHeader : function(col, header){
4993         this.config[col].header = header;
4994         this.fireEvent("headerchange", this, col, header);
4995     },
4996
4997     /**
4998      * Returns the tooltip for the specified column.
4999      * @param {Number} col The column index
5000      * @return {String}
5001      */
5002     getColumnTooltip : function(col){
5003             return this.config[col].tooltip;
5004     },
5005     /**
5006      * Sets the tooltip for a column.
5007      * @param {Number} col The column index
5008      * @param {String} tooltip The new tooltip
5009      */
5010     setColumnTooltip : function(col, tooltip){
5011             this.config[col].tooltip = tooltip;
5012     },
5013
5014     /**
5015      * Returns the dataIndex for the specified column.
5016      * @param {Number} col The column index
5017      * @return {Number}
5018      */
5019     getDataIndex : function(col){
5020         return this.config[col].dataIndex;
5021     },
5022
5023     /**
5024      * Sets the dataIndex for a column.
5025      * @param {Number} col The column index
5026      * @param {Number} dataIndex The new dataIndex
5027      */
5028     setDataIndex : function(col, dataIndex){
5029         this.config[col].dataIndex = dataIndex;
5030     },
5031
5032     
5033     
5034     /**
5035      * Returns true if the cell is editable.
5036      * @param {Number} colIndex The column index
5037      * @param {Number} rowIndex The row index
5038      * @return {Boolean}
5039      */
5040     isCellEditable : function(colIndex, rowIndex){
5041         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5042     },
5043
5044     /**
5045      * Returns the editor defined for the cell/column.
5046      * return false or null to disable editing.
5047      * @param {Number} colIndex The column index
5048      * @param {Number} rowIndex The row index
5049      * @return {Object}
5050      */
5051     getCellEditor : function(colIndex, rowIndex){
5052         return this.config[colIndex].editor;
5053     },
5054
5055     /**
5056      * Sets if a column is editable.
5057      * @param {Number} col The column index
5058      * @param {Boolean} editable True if the column is editable
5059      */
5060     setEditable : function(col, editable){
5061         this.config[col].editable = editable;
5062     },
5063
5064
5065     /**
5066      * Returns true if the column is hidden.
5067      * @param {Number} colIndex The column index
5068      * @return {Boolean}
5069      */
5070     isHidden : function(colIndex){
5071         return this.config[colIndex].hidden;
5072     },
5073
5074
5075     /**
5076      * Returns true if the column width cannot be changed
5077      */
5078     isFixed : function(colIndex){
5079         return this.config[colIndex].fixed;
5080     },
5081
5082     /**
5083      * Returns true if the column can be resized
5084      * @return {Boolean}
5085      */
5086     isResizable : function(colIndex){
5087         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5088     },
5089     /**
5090      * Sets if a column is hidden.
5091      * @param {Number} colIndex The column index
5092      * @param {Boolean} hidden True if the column is hidden
5093      */
5094     setHidden : function(colIndex, hidden){
5095         this.config[colIndex].hidden = hidden;
5096         this.totalWidth = null;
5097         this.fireEvent("hiddenchange", this, colIndex, hidden);
5098     },
5099
5100     /**
5101      * Sets the editor for a column.
5102      * @param {Number} col The column index
5103      * @param {Object} editor The editor object
5104      */
5105     setEditor : function(col, editor){
5106         this.config[col].editor = editor;
5107     }
5108 });
5109
5110 Roo.grid.ColumnModel.defaultRenderer = function(value){
5111         if(typeof value == "string" && value.length < 1){
5112             return "&#160;";
5113         }
5114         return value;
5115 };
5116
5117 // Alias for backwards compatibility
5118 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5119 /*
5120  * Based on:
5121  * Ext JS Library 1.1.1
5122  * Copyright(c) 2006-2007, Ext JS, LLC.
5123  *
5124  * Originally Released Under LGPL - original licence link has changed is not relivant.
5125  *
5126  * Fork - LGPL
5127  * <script type="text/javascript">
5128  */
5129  
5130 /**
5131  * @class Roo.LoadMask
5132  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5133  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5134  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5135  * element's UpdateManager load indicator and will be destroyed after the initial load.
5136  * @constructor
5137  * Create a new LoadMask
5138  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5139  * @param {Object} config The config object
5140  */
5141 Roo.LoadMask = function(el, config){
5142     this.el = Roo.get(el);
5143     Roo.apply(this, config);
5144     if(this.store){
5145         this.store.on('beforeload', this.onBeforeLoad, this);
5146         this.store.on('load', this.onLoad, this);
5147         this.store.on('loadexception', this.onLoadException, this);
5148         this.removeMask = false;
5149     }else{
5150         var um = this.el.getUpdateManager();
5151         um.showLoadIndicator = false; // disable the default indicator
5152         um.on('beforeupdate', this.onBeforeLoad, this);
5153         um.on('update', this.onLoad, this);
5154         um.on('failure', this.onLoad, this);
5155         this.removeMask = true;
5156     }
5157 };
5158
5159 Roo.LoadMask.prototype = {
5160     /**
5161      * @cfg {Boolean} removeMask
5162      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5163      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5164      */
5165     /**
5166      * @cfg {String} msg
5167      * The text to display in a centered loading message box (defaults to 'Loading...')
5168      */
5169     msg : 'Loading...',
5170     /**
5171      * @cfg {String} msgCls
5172      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5173      */
5174     msgCls : 'x-mask-loading',
5175
5176     /**
5177      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5178      * @type Boolean
5179      */
5180     disabled: false,
5181
5182     /**
5183      * Disables the mask to prevent it from being displayed
5184      */
5185     disable : function(){
5186        this.disabled = true;
5187     },
5188
5189     /**
5190      * Enables the mask so that it can be displayed
5191      */
5192     enable : function(){
5193         this.disabled = false;
5194     },
5195     
5196     onLoadException : function()
5197     {
5198         Roo.log(arguments);
5199         
5200         if (typeof(arguments[3]) != 'undefined') {
5201             Roo.MessageBox.alert("Error loading",arguments[3]);
5202         } 
5203         /*
5204         try {
5205             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5206                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5207             }   
5208         } catch(e) {
5209             
5210         }
5211         */
5212     
5213         
5214         
5215         this.el.unmask(this.removeMask);
5216     },
5217     // private
5218     onLoad : function()
5219     {
5220         this.el.unmask(this.removeMask);
5221     },
5222
5223     // private
5224     onBeforeLoad : function(){
5225         if(!this.disabled){
5226             this.el.mask(this.msg, this.msgCls);
5227         }
5228     },
5229
5230     // private
5231     destroy : function(){
5232         if(this.store){
5233             this.store.un('beforeload', this.onBeforeLoad, this);
5234             this.store.un('load', this.onLoad, this);
5235             this.store.un('loadexception', this.onLoadException, this);
5236         }else{
5237             var um = this.el.getUpdateManager();
5238             um.un('beforeupdate', this.onBeforeLoad, this);
5239             um.un('update', this.onLoad, this);
5240             um.un('failure', this.onLoad, this);
5241         }
5242     }
5243 };/*
5244  * - LGPL
5245  *
5246  * table
5247  * 
5248  */
5249
5250 /**
5251  * @class Roo.bootstrap.Table
5252  * @extends Roo.bootstrap.Component
5253  * Bootstrap Table class
5254  * @cfg {String} cls table class
5255  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5256  * @cfg {String} bgcolor Specifies the background color for a table
5257  * @cfg {Number} border Specifies whether the table cells should have borders or not
5258  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5259  * @cfg {Number} cellspacing Specifies the space between cells
5260  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5261  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5262  * @cfg {String} sortable Specifies that the table should be sortable
5263  * @cfg {String} summary Specifies a summary of the content of a table
5264  * @cfg {Number} width Specifies the width of a table
5265  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5266  * 
5267  * @cfg {boolean} striped Should the rows be alternative striped
5268  * @cfg {boolean} bordered Add borders to the table
5269  * @cfg {boolean} hover Add hover highlighting
5270  * @cfg {boolean} condensed Format condensed
5271  * @cfg {boolean} responsive Format condensed
5272  * @cfg {Boolean} loadMask (true|false) default false
5273  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5274  * @cfg {Boolean} thead (true|false) generate thead, default true
5275  * @cfg {Boolean} RowSelection (true|false) default false
5276  * @cfg {Boolean} CellSelection (true|false) default false
5277  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5278  
5279  * 
5280  * @constructor
5281  * Create a new Table
5282  * @param {Object} config The config object
5283  */
5284
5285 Roo.bootstrap.Table = function(config){
5286     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5287     
5288     if (this.sm) {
5289         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5290         this.sm = this.selModel;
5291         this.sm.xmodule = this.xmodule || false;
5292     }
5293     if (this.cm && typeof(this.cm.config) == 'undefined') {
5294         this.colModel = new Roo.grid.ColumnModel(this.cm);
5295         this.cm = this.colModel;
5296         this.cm.xmodule = this.xmodule || false;
5297     }
5298     if (this.store) {
5299         this.store= Roo.factory(this.store, Roo.data);
5300         this.ds = this.store;
5301         this.ds.xmodule = this.xmodule || false;
5302          
5303     }
5304     if (this.footer && this.store) {
5305         this.footer.dataSource = this.ds;
5306         this.footer = Roo.factory(this.footer);
5307     }
5308     
5309     /** @private */
5310     this.addEvents({
5311         /**
5312          * @event cellclick
5313          * Fires when a cell is clicked
5314          * @param {Roo.bootstrap.Table} this
5315          * @param {Roo.Element} el
5316          * @param {Number} rowIndex
5317          * @param {Number} columnIndex
5318          * @param {Roo.EventObject} e
5319          */
5320         "cellclick" : true,
5321         /**
5322          * @event celldblclick
5323          * Fires when a cell is double clicked
5324          * @param {Roo.bootstrap.Table} this
5325          * @param {Roo.Element} el
5326          * @param {Number} rowIndex
5327          * @param {Number} columnIndex
5328          * @param {Roo.EventObject} e
5329          */
5330         "celldblclick" : true,
5331         /**
5332          * @event rowclick
5333          * Fires when a row is clicked
5334          * @param {Roo.bootstrap.Table} this
5335          * @param {Roo.Element} el
5336          * @param {Number} rowIndex
5337          * @param {Roo.EventObject} e
5338          */
5339         "rowclick" : true,
5340         /**
5341          * @event rowdblclick
5342          * Fires when a row is double clicked
5343          * @param {Roo.bootstrap.Table} this
5344          * @param {Roo.Element} el
5345          * @param {Number} rowIndex
5346          * @param {Roo.EventObject} e
5347          */
5348         "rowdblclick" : true,
5349         /**
5350          * @event mouseover
5351          * Fires when a mouseover occur
5352          * @param {Roo.bootstrap.Table} this
5353          * @param {Roo.Element} el
5354          * @param {Number} rowIndex
5355          * @param {Number} columnIndex
5356          * @param {Roo.EventObject} e
5357          */
5358         "mouseover" : true,
5359         /**
5360          * @event mouseout
5361          * Fires when a mouseout occur
5362          * @param {Roo.bootstrap.Table} this
5363          * @param {Roo.Element} el
5364          * @param {Number} rowIndex
5365          * @param {Number} columnIndex
5366          * @param {Roo.EventObject} e
5367          */
5368         "mouseout" : true,
5369         /**
5370          * @event rowclass
5371          * Fires when a row is rendered, so you can change add a style to it.
5372          * @param {Roo.bootstrap.Table} this
5373          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5374          */
5375         'rowclass' : true,
5376           /**
5377          * @event rowsrendered
5378          * Fires when all the  rows have been rendered
5379          * @param {Roo.bootstrap.Table} this
5380          */
5381         'rowsrendered' : true
5382         
5383     });
5384 };
5385
5386 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5387     
5388     cls: false,
5389     align: false,
5390     bgcolor: false,
5391     border: false,
5392     cellpadding: false,
5393     cellspacing: false,
5394     frame: false,
5395     rules: false,
5396     sortable: false,
5397     summary: false,
5398     width: false,
5399     striped : false,
5400     bordered: false,
5401     hover:  false,
5402     condensed : false,
5403     responsive : false,
5404     sm : false,
5405     cm : false,
5406     store : false,
5407     loadMask : false,
5408     tfoot : true,
5409     thead : true,
5410     RowSelection : false,
5411     CellSelection : false,
5412     layout : false,
5413     
5414     // Roo.Element - the tbody
5415     mainBody: false, 
5416     
5417     getAutoCreate : function(){
5418         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5419         
5420         cfg = {
5421             tag: 'table',
5422             cls : 'table',
5423             cn : []
5424         }
5425             
5426         if (this.striped) {
5427             cfg.cls += ' table-striped';
5428         }
5429         
5430         if (this.hover) {
5431             cfg.cls += ' table-hover';
5432         }
5433         if (this.bordered) {
5434             cfg.cls += ' table-bordered';
5435         }
5436         if (this.condensed) {
5437             cfg.cls += ' table-condensed';
5438         }
5439         if (this.responsive) {
5440             cfg.cls += ' table-responsive';
5441         }
5442         
5443         if (this.cls) {
5444             cfg.cls+=  ' ' +this.cls;
5445         }
5446         
5447         // this lot should be simplifed...
5448         
5449         if (this.align) {
5450             cfg.align=this.align;
5451         }
5452         if (this.bgcolor) {
5453             cfg.bgcolor=this.bgcolor;
5454         }
5455         if (this.border) {
5456             cfg.border=this.border;
5457         }
5458         if (this.cellpadding) {
5459             cfg.cellpadding=this.cellpadding;
5460         }
5461         if (this.cellspacing) {
5462             cfg.cellspacing=this.cellspacing;
5463         }
5464         if (this.frame) {
5465             cfg.frame=this.frame;
5466         }
5467         if (this.rules) {
5468             cfg.rules=this.rules;
5469         }
5470         if (this.sortable) {
5471             cfg.sortable=this.sortable;
5472         }
5473         if (this.summary) {
5474             cfg.summary=this.summary;
5475         }
5476         if (this.width) {
5477             cfg.width=this.width;
5478         }
5479         if (this.layout) {
5480             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5481         }
5482         
5483         if(this.store || this.cm){
5484             if(this.thead){
5485                 cfg.cn.push(this.renderHeader());
5486             }
5487             
5488             cfg.cn.push(this.renderBody());
5489             
5490             if(this.tfoot){
5491                 cfg.cn.push(this.renderFooter());
5492             }
5493             
5494             cfg.cls+=  ' TableGrid';
5495         }
5496         
5497         return { cn : [ cfg ] };
5498     },
5499     
5500     initEvents : function()
5501     {   
5502         if(!this.store || !this.cm){
5503             return;
5504         }
5505         
5506         //Roo.log('initEvents with ds!!!!');
5507         
5508         this.mainBody = this.el.select('tbody', true).first();
5509         
5510         
5511         var _this = this;
5512         
5513         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5514             e.on('click', _this.sort, _this);
5515         });
5516         
5517         this.el.on("click", this.onClick, this);
5518         this.el.on("dblclick", this.onDblClick, this);
5519         
5520         // why is this done????? = it breaks dialogs??
5521         //this.parent().el.setStyle('position', 'relative');
5522         
5523         
5524         if (this.footer) {
5525             this.footer.parentId = this.id;
5526             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5527         }
5528         
5529         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5530         
5531         this.store.on('load', this.onLoad, this);
5532         this.store.on('beforeload', this.onBeforeLoad, this);
5533         this.store.on('update', this.onUpdate, this);
5534         this.store.on('add', this.onAdd, this);
5535         
5536     },
5537     
5538     onMouseover : function(e, el)
5539     {
5540         var cell = Roo.get(el);
5541         
5542         if(!cell){
5543             return;
5544         }
5545         
5546         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5547             cell = cell.findParent('td', false, true);
5548         }
5549         
5550         var row = cell.findParent('tr', false, true);
5551         var cellIndex = cell.dom.cellIndex;
5552         var rowIndex = row.dom.rowIndex - 1; // start from 0
5553         
5554         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5555         
5556     },
5557     
5558     onMouseout : function(e, el)
5559     {
5560         var cell = Roo.get(el);
5561         
5562         if(!cell){
5563             return;
5564         }
5565         
5566         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5567             cell = cell.findParent('td', false, true);
5568         }
5569         
5570         var row = cell.findParent('tr', false, true);
5571         var cellIndex = cell.dom.cellIndex;
5572         var rowIndex = row.dom.rowIndex - 1; // start from 0
5573         
5574         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5575         
5576     },
5577     
5578     onClick : function(e, el)
5579     {
5580         var cell = Roo.get(el);
5581         
5582         if(!cell || (!this.CellSelection && !this.RowSelection)){
5583             return;
5584         }
5585         
5586         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5587             cell = cell.findParent('td', false, true);
5588         }
5589         
5590         if(!cell || typeof(cell) == 'undefined'){
5591             return;
5592         }
5593         
5594         var row = cell.findParent('tr', false, true);
5595         
5596         if(!row || typeof(row) == 'undefined'){
5597             return;
5598         }
5599         
5600         var cellIndex = cell.dom.cellIndex;
5601         var rowIndex = this.getRowIndex(row);
5602         
5603         if(this.CellSelection){
5604             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5605         }
5606         
5607         if(this.RowSelection){
5608             this.fireEvent('rowclick', this, row, rowIndex, e);
5609         }
5610         
5611         
5612     },
5613     
5614     onDblClick : function(e,el)
5615     {
5616         var cell = Roo.get(el);
5617         
5618         if(!cell || (!this.CellSelection && !this.RowSelection)){
5619             return;
5620         }
5621         
5622         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5623             cell = cell.findParent('td', false, true);
5624         }
5625         
5626         if(!cell || typeof(cell) == 'undefined'){
5627             return;
5628         }
5629         
5630         var row = cell.findParent('tr', false, true);
5631         
5632         if(!row || typeof(row) == 'undefined'){
5633             return;
5634         }
5635         
5636         var cellIndex = cell.dom.cellIndex;
5637         var rowIndex = this.getRowIndex(row);
5638         
5639         if(this.CellSelection){
5640             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5641         }
5642         
5643         if(this.RowSelection){
5644             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5645         }
5646     },
5647     
5648     sort : function(e,el)
5649     {
5650         var col = Roo.get(el);
5651         
5652         if(!col.hasClass('sortable')){
5653             return;
5654         }
5655         
5656         var sort = col.attr('sort');
5657         var dir = 'ASC';
5658         
5659         if(col.hasClass('glyphicon-arrow-up')){
5660             dir = 'DESC';
5661         }
5662         
5663         this.store.sortInfo = {field : sort, direction : dir};
5664         
5665         if (this.footer) {
5666             Roo.log("calling footer first");
5667             this.footer.onClick('first');
5668         } else {
5669         
5670             this.store.load({ params : { start : 0 } });
5671         }
5672     },
5673     
5674     renderHeader : function()
5675     {
5676         var header = {
5677             tag: 'thead',
5678             cn : []
5679         };
5680         
5681         var cm = this.cm;
5682         
5683         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5684             
5685             var config = cm.config[i];
5686                     
5687             var c = {
5688                 tag: 'th',
5689                 style : '',
5690                 html: cm.getColumnHeader(i)
5691             };
5692             
5693             if(typeof(config.tooltip) != 'undefined'){
5694                 c.tooltip = config.tooltip;
5695             }
5696             
5697             if(typeof(config.colspan) != 'undefined'){
5698                 c.colspan = config.colspan;
5699             }
5700             
5701             if(typeof(config.hidden) != 'undefined' && config.hidden){
5702                 c.style += ' display:none;';
5703             }
5704             
5705             if(typeof(config.dataIndex) != 'undefined'){
5706                 c.sort = config.dataIndex;
5707             }
5708             
5709             if(typeof(config.sortable) != 'undefined' && config.sortable){
5710                 c.cls = 'sortable';
5711             }
5712             
5713             if(typeof(config.align) != 'undefined' && config.align.length){
5714                 c.style += ' text-align:' + config.align + ';';
5715             }
5716             
5717             if(typeof(config.width) != 'undefined'){
5718                 c.style += ' width:' + config.width + 'px;';
5719             }
5720             
5721             if(typeof(config.cls) != 'undefined'){
5722                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5723             }
5724             
5725             header.cn.push(c)
5726         }
5727         
5728         return header;
5729     },
5730     
5731     renderBody : function()
5732     {
5733         var body = {
5734             tag: 'tbody',
5735             cn : [
5736                 {
5737                     tag: 'tr',
5738                     cn : [
5739                         {
5740                             tag : 'td',
5741                             colspan :  this.cm.getColumnCount()
5742                         }
5743                     ]
5744                 }
5745             ]
5746         };
5747         
5748         return body;
5749     },
5750     
5751     renderFooter : function()
5752     {
5753         var footer = {
5754             tag: 'tfoot',
5755             cn : [
5756                 {
5757                     tag: 'tr',
5758                     cn : [
5759                         {
5760                             tag : 'td',
5761                             colspan :  this.cm.getColumnCount()
5762                         }
5763                     ]
5764                 }
5765             ]
5766         };
5767         
5768         return footer;
5769     },
5770     
5771     
5772     
5773     onLoad : function()
5774     {
5775         Roo.log('ds onload');
5776         this.clear();
5777         
5778         var _this = this;
5779         var cm = this.cm;
5780         var ds = this.store;
5781         
5782         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5783             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5784             
5785             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5786                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5787             }
5788             
5789             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5790                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5791             }
5792         });
5793         
5794         var tbody =  this.mainBody;
5795               
5796         if(ds.getCount() > 0){
5797             ds.data.each(function(d,rowIndex){
5798                 var row =  this.renderRow(cm, ds, rowIndex);
5799                 
5800                 tbody.createChild(row);
5801                 
5802                 var _this = this;
5803                 
5804                 if(row.cellObjects.length){
5805                     Roo.each(row.cellObjects, function(r){
5806                         _this.renderCellObject(r);
5807                     })
5808                 }
5809                 
5810             }, this);
5811         }
5812         
5813         Roo.each(this.el.select('tbody td', true).elements, function(e){
5814             e.on('mouseover', _this.onMouseover, _this);
5815         });
5816         
5817         Roo.each(this.el.select('tbody td', true).elements, function(e){
5818             e.on('mouseout', _this.onMouseout, _this);
5819         });
5820         this.fireEvent('rowsrendered', this);
5821         //if(this.loadMask){
5822         //    this.maskEl.hide();
5823         //}
5824     },
5825     
5826     
5827     onUpdate : function(ds,record)
5828     {
5829         this.refreshRow(record);
5830     },
5831     
5832     onRemove : function(ds, record, index, isUpdate){
5833         if(isUpdate !== true){
5834             this.fireEvent("beforerowremoved", this, index, record);
5835         }
5836         var bt = this.mainBody.dom;
5837         
5838         var rows = this.el.select('tbody > tr', true).elements;
5839         
5840         if(typeof(rows[index]) != 'undefined'){
5841             bt.removeChild(rows[index].dom);
5842         }
5843         
5844 //        if(bt.rows[index]){
5845 //            bt.removeChild(bt.rows[index]);
5846 //        }
5847         
5848         if(isUpdate !== true){
5849             //this.stripeRows(index);
5850             //this.syncRowHeights(index, index);
5851             //this.layout();
5852             this.fireEvent("rowremoved", this, index, record);
5853         }
5854     },
5855     
5856     onAdd : function(ds, records, rowIndex)
5857     {
5858         //Roo.log('on Add called');
5859         // - note this does not handle multiple adding very well..
5860         var bt = this.mainBody.dom;
5861         for (var i =0 ; i < records.length;i++) {
5862             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5863             //Roo.log(records[i]);
5864             //Roo.log(this.store.getAt(rowIndex+i));
5865             this.insertRow(this.store, rowIndex + i, false);
5866             return;
5867         }
5868         
5869     },
5870     
5871     
5872     refreshRow : function(record){
5873         var ds = this.store, index;
5874         if(typeof record == 'number'){
5875             index = record;
5876             record = ds.getAt(index);
5877         }else{
5878             index = ds.indexOf(record);
5879         }
5880         this.insertRow(ds, index, true);
5881         this.onRemove(ds, record, index+1, true);
5882         //this.syncRowHeights(index, index);
5883         //this.layout();
5884         this.fireEvent("rowupdated", this, index, record);
5885     },
5886     
5887     insertRow : function(dm, rowIndex, isUpdate){
5888         
5889         if(!isUpdate){
5890             this.fireEvent("beforerowsinserted", this, rowIndex);
5891         }
5892             //var s = this.getScrollState();
5893         var row = this.renderRow(this.cm, this.store, rowIndex);
5894         // insert before rowIndex..
5895         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5896         
5897         var _this = this;
5898                 
5899         if(row.cellObjects.length){
5900             Roo.each(row.cellObjects, function(r){
5901                 _this.renderCellObject(r);
5902             })
5903         }
5904             
5905         if(!isUpdate){
5906             this.fireEvent("rowsinserted", this, rowIndex);
5907             //this.syncRowHeights(firstRow, lastRow);
5908             //this.stripeRows(firstRow);
5909             //this.layout();
5910         }
5911         
5912     },
5913     
5914     
5915     getRowDom : function(rowIndex)
5916     {
5917         var rows = this.el.select('tbody > tr', true).elements;
5918         
5919         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5920         
5921     },
5922     // returns the object tree for a tr..
5923   
5924     
5925     renderRow : function(cm, ds, rowIndex) 
5926     {
5927         
5928         var d = ds.getAt(rowIndex);
5929         
5930         var row = {
5931             tag : 'tr',
5932             cn : []
5933         };
5934             
5935         var cellObjects = [];
5936         
5937         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5938             var config = cm.config[i];
5939             
5940             var renderer = cm.getRenderer(i);
5941             var value = '';
5942             var id = false;
5943             
5944             if(typeof(renderer) !== 'undefined'){
5945                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5946             }
5947             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5948             // and are rendered into the cells after the row is rendered - using the id for the element.
5949             
5950             if(typeof(value) === 'object'){
5951                 id = Roo.id();
5952                 cellObjects.push({
5953                     container : id,
5954                     cfg : value 
5955                 })
5956             }
5957             
5958             var rowcfg = {
5959                 record: d,
5960                 rowIndex : rowIndex,
5961                 colIndex : i,
5962                 rowClass : ''
5963             }
5964
5965             this.fireEvent('rowclass', this, rowcfg);
5966             
5967             var td = {
5968                 tag: 'td',
5969                 cls : rowcfg.rowClass,
5970                 style: '',
5971                 html: (typeof(value) === 'object') ? '' : value
5972             };
5973             
5974             if (id) {
5975                 td.id = id;
5976             }
5977             
5978             if(typeof(config.colspan) != 'undefined'){
5979                 td.colspan = config.colspan;
5980             }
5981             
5982             if(typeof(config.hidden) != 'undefined' && config.hidden){
5983                 td.style += ' display:none;';
5984             }
5985             
5986             if(typeof(config.align) != 'undefined' && config.align.length){
5987                 td.style += ' text-align:' + config.align + ';';
5988             }
5989             
5990             if(typeof(config.width) != 'undefined'){
5991                 td.style += ' width:' +  config.width + 'px;';
5992             }
5993             
5994             if(typeof(config.cursor) != 'undefined'){
5995                 td.style += ' cursor:' +  config.cursor + ';';
5996             }
5997             
5998             if(typeof(config.cls) != 'undefined'){
5999                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6000             }
6001              
6002             row.cn.push(td);
6003            
6004         }
6005         
6006         row.cellObjects = cellObjects;
6007         
6008         return row;
6009           
6010     },
6011     
6012     
6013     
6014     onBeforeLoad : function()
6015     {
6016         //Roo.log('ds onBeforeLoad');
6017         
6018         //this.clear();
6019         
6020         //if(this.loadMask){
6021         //    this.maskEl.show();
6022         //}
6023     },
6024      /**
6025      * Remove all rows
6026      */
6027     clear : function()
6028     {
6029         this.el.select('tbody', true).first().dom.innerHTML = '';
6030     },
6031     /**
6032      * Show or hide a row.
6033      * @param {Number} rowIndex to show or hide
6034      * @param {Boolean} state hide
6035      */
6036     setRowVisibility : function(rowIndex, state)
6037     {
6038         var bt = this.mainBody.dom;
6039         
6040         var rows = this.el.select('tbody > tr', true).elements;
6041         
6042         if(typeof(rows[rowIndex]) == 'undefined'){
6043             return;
6044         }
6045         rows[rowIndex].dom.style.display = state ? '' : 'none';
6046     },
6047     
6048     
6049     getSelectionModel : function(){
6050         if(!this.selModel){
6051             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6052         }
6053         return this.selModel;
6054     },
6055     /*
6056      * Render the Roo.bootstrap object from renderder
6057      */
6058     renderCellObject : function(r)
6059     {
6060         var _this = this;
6061         
6062         var t = r.cfg.render(r.container);
6063         
6064         if(r.cfg.cn){
6065             Roo.each(r.cfg.cn, function(c){
6066                 var child = {
6067                     container: t.getChildContainer(),
6068                     cfg: c
6069                 }
6070                 _this.renderCellObject(child);
6071             })
6072         }
6073     },
6074     
6075     getRowIndex : function(row)
6076     {
6077         var rowIndex = -1;
6078         
6079         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6080             if(el != row){
6081                 return;
6082             }
6083             
6084             rowIndex = index;
6085         });
6086         
6087         return rowIndex;
6088     }
6089    
6090 });
6091
6092  
6093
6094  /*
6095  * - LGPL
6096  *
6097  * table cell
6098  * 
6099  */
6100
6101 /**
6102  * @class Roo.bootstrap.TableCell
6103  * @extends Roo.bootstrap.Component
6104  * Bootstrap TableCell class
6105  * @cfg {String} html cell contain text
6106  * @cfg {String} cls cell class
6107  * @cfg {String} tag cell tag (td|th) default td
6108  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6109  * @cfg {String} align Aligns the content in a cell
6110  * @cfg {String} axis Categorizes cells
6111  * @cfg {String} bgcolor Specifies the background color of a cell
6112  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6113  * @cfg {Number} colspan Specifies the number of columns a cell should span
6114  * @cfg {String} headers Specifies one or more header cells a cell is related to
6115  * @cfg {Number} height Sets the height of a cell
6116  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6117  * @cfg {Number} rowspan Sets the number of rows a cell should span
6118  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6119  * @cfg {String} valign Vertical aligns the content in a cell
6120  * @cfg {Number} width Specifies the width of a cell
6121  * 
6122  * @constructor
6123  * Create a new TableCell
6124  * @param {Object} config The config object
6125  */
6126
6127 Roo.bootstrap.TableCell = function(config){
6128     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6129 };
6130
6131 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6132     
6133     html: false,
6134     cls: false,
6135     tag: false,
6136     abbr: false,
6137     align: false,
6138     axis: false,
6139     bgcolor: false,
6140     charoff: false,
6141     colspan: false,
6142     headers: false,
6143     height: false,
6144     nowrap: false,
6145     rowspan: false,
6146     scope: false,
6147     valign: false,
6148     width: false,
6149     
6150     
6151     getAutoCreate : function(){
6152         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6153         
6154         cfg = {
6155             tag: 'td'
6156         }
6157         
6158         if(this.tag){
6159             cfg.tag = this.tag;
6160         }
6161         
6162         if (this.html) {
6163             cfg.html=this.html
6164         }
6165         if (this.cls) {
6166             cfg.cls=this.cls
6167         }
6168         if (this.abbr) {
6169             cfg.abbr=this.abbr
6170         }
6171         if (this.align) {
6172             cfg.align=this.align
6173         }
6174         if (this.axis) {
6175             cfg.axis=this.axis
6176         }
6177         if (this.bgcolor) {
6178             cfg.bgcolor=this.bgcolor
6179         }
6180         if (this.charoff) {
6181             cfg.charoff=this.charoff
6182         }
6183         if (this.colspan) {
6184             cfg.colspan=this.colspan
6185         }
6186         if (this.headers) {
6187             cfg.headers=this.headers
6188         }
6189         if (this.height) {
6190             cfg.height=this.height
6191         }
6192         if (this.nowrap) {
6193             cfg.nowrap=this.nowrap
6194         }
6195         if (this.rowspan) {
6196             cfg.rowspan=this.rowspan
6197         }
6198         if (this.scope) {
6199             cfg.scope=this.scope
6200         }
6201         if (this.valign) {
6202             cfg.valign=this.valign
6203         }
6204         if (this.width) {
6205             cfg.width=this.width
6206         }
6207         
6208         
6209         return cfg;
6210     }
6211    
6212 });
6213
6214  
6215
6216  /*
6217  * - LGPL
6218  *
6219  * table row
6220  * 
6221  */
6222
6223 /**
6224  * @class Roo.bootstrap.TableRow
6225  * @extends Roo.bootstrap.Component
6226  * Bootstrap TableRow class
6227  * @cfg {String} cls row class
6228  * @cfg {String} align Aligns the content in a table row
6229  * @cfg {String} bgcolor Specifies a background color for a table row
6230  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6231  * @cfg {String} valign Vertical aligns the content in a table row
6232  * 
6233  * @constructor
6234  * Create a new TableRow
6235  * @param {Object} config The config object
6236  */
6237
6238 Roo.bootstrap.TableRow = function(config){
6239     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6240 };
6241
6242 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6243     
6244     cls: false,
6245     align: false,
6246     bgcolor: false,
6247     charoff: false,
6248     valign: false,
6249     
6250     getAutoCreate : function(){
6251         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6252         
6253         cfg = {
6254             tag: 'tr'
6255         }
6256             
6257         if(this.cls){
6258             cfg.cls = this.cls;
6259         }
6260         if(this.align){
6261             cfg.align = this.align;
6262         }
6263         if(this.bgcolor){
6264             cfg.bgcolor = this.bgcolor;
6265         }
6266         if(this.charoff){
6267             cfg.charoff = this.charoff;
6268         }
6269         if(this.valign){
6270             cfg.valign = this.valign;
6271         }
6272         
6273         return cfg;
6274     }
6275    
6276 });
6277
6278  
6279
6280  /*
6281  * - LGPL
6282  *
6283  * table body
6284  * 
6285  */
6286
6287 /**
6288  * @class Roo.bootstrap.TableBody
6289  * @extends Roo.bootstrap.Component
6290  * Bootstrap TableBody class
6291  * @cfg {String} cls element class
6292  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6293  * @cfg {String} align Aligns the content inside the element
6294  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6295  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6296  * 
6297  * @constructor
6298  * Create a new TableBody
6299  * @param {Object} config The config object
6300  */
6301
6302 Roo.bootstrap.TableBody = function(config){
6303     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6304 };
6305
6306 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6307     
6308     cls: false,
6309     tag: false,
6310     align: false,
6311     charoff: false,
6312     valign: false,
6313     
6314     getAutoCreate : function(){
6315         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6316         
6317         cfg = {
6318             tag: 'tbody'
6319         }
6320             
6321         if (this.cls) {
6322             cfg.cls=this.cls
6323         }
6324         if(this.tag){
6325             cfg.tag = this.tag;
6326         }
6327         
6328         if(this.align){
6329             cfg.align = this.align;
6330         }
6331         if(this.charoff){
6332             cfg.charoff = this.charoff;
6333         }
6334         if(this.valign){
6335             cfg.valign = this.valign;
6336         }
6337         
6338         return cfg;
6339     }
6340     
6341     
6342 //    initEvents : function()
6343 //    {
6344 //        
6345 //        if(!this.store){
6346 //            return;
6347 //        }
6348 //        
6349 //        this.store = Roo.factory(this.store, Roo.data);
6350 //        this.store.on('load', this.onLoad, this);
6351 //        
6352 //        this.store.load();
6353 //        
6354 //    },
6355 //    
6356 //    onLoad: function () 
6357 //    {   
6358 //        this.fireEvent('load', this);
6359 //    }
6360 //    
6361 //   
6362 });
6363
6364  
6365
6366  /*
6367  * Based on:
6368  * Ext JS Library 1.1.1
6369  * Copyright(c) 2006-2007, Ext JS, LLC.
6370  *
6371  * Originally Released Under LGPL - original licence link has changed is not relivant.
6372  *
6373  * Fork - LGPL
6374  * <script type="text/javascript">
6375  */
6376
6377 // as we use this in bootstrap.
6378 Roo.namespace('Roo.form');
6379  /**
6380  * @class Roo.form.Action
6381  * Internal Class used to handle form actions
6382  * @constructor
6383  * @param {Roo.form.BasicForm} el The form element or its id
6384  * @param {Object} config Configuration options
6385  */
6386
6387  
6388  
6389 // define the action interface
6390 Roo.form.Action = function(form, options){
6391     this.form = form;
6392     this.options = options || {};
6393 };
6394 /**
6395  * Client Validation Failed
6396  * @const 
6397  */
6398 Roo.form.Action.CLIENT_INVALID = 'client';
6399 /**
6400  * Server Validation Failed
6401  * @const 
6402  */
6403 Roo.form.Action.SERVER_INVALID = 'server';
6404  /**
6405  * Connect to Server Failed
6406  * @const 
6407  */
6408 Roo.form.Action.CONNECT_FAILURE = 'connect';
6409 /**
6410  * Reading Data from Server Failed
6411  * @const 
6412  */
6413 Roo.form.Action.LOAD_FAILURE = 'load';
6414
6415 Roo.form.Action.prototype = {
6416     type : 'default',
6417     failureType : undefined,
6418     response : undefined,
6419     result : undefined,
6420
6421     // interface method
6422     run : function(options){
6423
6424     },
6425
6426     // interface method
6427     success : function(response){
6428
6429     },
6430
6431     // interface method
6432     handleResponse : function(response){
6433
6434     },
6435
6436     // default connection failure
6437     failure : function(response){
6438         
6439         this.response = response;
6440         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6441         this.form.afterAction(this, false);
6442     },
6443
6444     processResponse : function(response){
6445         this.response = response;
6446         if(!response.responseText){
6447             return true;
6448         }
6449         this.result = this.handleResponse(response);
6450         return this.result;
6451     },
6452
6453     // utility functions used internally
6454     getUrl : function(appendParams){
6455         var url = this.options.url || this.form.url || this.form.el.dom.action;
6456         if(appendParams){
6457             var p = this.getParams();
6458             if(p){
6459                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6460             }
6461         }
6462         return url;
6463     },
6464
6465     getMethod : function(){
6466         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6467     },
6468
6469     getParams : function(){
6470         var bp = this.form.baseParams;
6471         var p = this.options.params;
6472         if(p){
6473             if(typeof p == "object"){
6474                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6475             }else if(typeof p == 'string' && bp){
6476                 p += '&' + Roo.urlEncode(bp);
6477             }
6478         }else if(bp){
6479             p = Roo.urlEncode(bp);
6480         }
6481         return p;
6482     },
6483
6484     createCallback : function(){
6485         return {
6486             success: this.success,
6487             failure: this.failure,
6488             scope: this,
6489             timeout: (this.form.timeout*1000),
6490             upload: this.form.fileUpload ? this.success : undefined
6491         };
6492     }
6493 };
6494
6495 Roo.form.Action.Submit = function(form, options){
6496     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6497 };
6498
6499 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6500     type : 'submit',
6501
6502     haveProgress : false,
6503     uploadComplete : false,
6504     
6505     // uploadProgress indicator.
6506     uploadProgress : function()
6507     {
6508         if (!this.form.progressUrl) {
6509             return;
6510         }
6511         
6512         if (!this.haveProgress) {
6513             Roo.MessageBox.progress("Uploading", "Uploading");
6514         }
6515         if (this.uploadComplete) {
6516            Roo.MessageBox.hide();
6517            return;
6518         }
6519         
6520         this.haveProgress = true;
6521    
6522         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6523         
6524         var c = new Roo.data.Connection();
6525         c.request({
6526             url : this.form.progressUrl,
6527             params: {
6528                 id : uid
6529             },
6530             method: 'GET',
6531             success : function(req){
6532                //console.log(data);
6533                 var rdata = false;
6534                 var edata;
6535                 try  {
6536                    rdata = Roo.decode(req.responseText)
6537                 } catch (e) {
6538                     Roo.log("Invalid data from server..");
6539                     Roo.log(edata);
6540                     return;
6541                 }
6542                 if (!rdata || !rdata.success) {
6543                     Roo.log(rdata);
6544                     Roo.MessageBox.alert(Roo.encode(rdata));
6545                     return;
6546                 }
6547                 var data = rdata.data;
6548                 
6549                 if (this.uploadComplete) {
6550                    Roo.MessageBox.hide();
6551                    return;
6552                 }
6553                    
6554                 if (data){
6555                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6556                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6557                     );
6558                 }
6559                 this.uploadProgress.defer(2000,this);
6560             },
6561        
6562             failure: function(data) {
6563                 Roo.log('progress url failed ');
6564                 Roo.log(data);
6565             },
6566             scope : this
6567         });
6568            
6569     },
6570     
6571     
6572     run : function()
6573     {
6574         // run get Values on the form, so it syncs any secondary forms.
6575         this.form.getValues();
6576         
6577         var o = this.options;
6578         var method = this.getMethod();
6579         var isPost = method == 'POST';
6580         if(o.clientValidation === false || this.form.isValid()){
6581             
6582             if (this.form.progressUrl) {
6583                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6584                     (new Date() * 1) + '' + Math.random());
6585                     
6586             } 
6587             
6588             
6589             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6590                 form:this.form.el.dom,
6591                 url:this.getUrl(!isPost),
6592                 method: method,
6593                 params:isPost ? this.getParams() : null,
6594                 isUpload: this.form.fileUpload
6595             }));
6596             
6597             this.uploadProgress();
6598
6599         }else if (o.clientValidation !== false){ // client validation failed
6600             this.failureType = Roo.form.Action.CLIENT_INVALID;
6601             this.form.afterAction(this, false);
6602         }
6603     },
6604
6605     success : function(response)
6606     {
6607         this.uploadComplete= true;
6608         if (this.haveProgress) {
6609             Roo.MessageBox.hide();
6610         }
6611         
6612         
6613         var result = this.processResponse(response);
6614         if(result === true || result.success){
6615             this.form.afterAction(this, true);
6616             return;
6617         }
6618         if(result.errors){
6619             this.form.markInvalid(result.errors);
6620             this.failureType = Roo.form.Action.SERVER_INVALID;
6621         }
6622         this.form.afterAction(this, false);
6623     },
6624     failure : function(response)
6625     {
6626         this.uploadComplete= true;
6627         if (this.haveProgress) {
6628             Roo.MessageBox.hide();
6629         }
6630         
6631         this.response = response;
6632         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6633         this.form.afterAction(this, false);
6634     },
6635     
6636     handleResponse : function(response){
6637         if(this.form.errorReader){
6638             var rs = this.form.errorReader.read(response);
6639             var errors = [];
6640             if(rs.records){
6641                 for(var i = 0, len = rs.records.length; i < len; i++) {
6642                     var r = rs.records[i];
6643                     errors[i] = r.data;
6644                 }
6645             }
6646             if(errors.length < 1){
6647                 errors = null;
6648             }
6649             return {
6650                 success : rs.success,
6651                 errors : errors
6652             };
6653         }
6654         var ret = false;
6655         try {
6656             ret = Roo.decode(response.responseText);
6657         } catch (e) {
6658             ret = {
6659                 success: false,
6660                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6661                 errors : []
6662             };
6663         }
6664         return ret;
6665         
6666     }
6667 });
6668
6669
6670 Roo.form.Action.Load = function(form, options){
6671     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6672     this.reader = this.form.reader;
6673 };
6674
6675 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6676     type : 'load',
6677
6678     run : function(){
6679         
6680         Roo.Ajax.request(Roo.apply(
6681                 this.createCallback(), {
6682                     method:this.getMethod(),
6683                     url:this.getUrl(false),
6684                     params:this.getParams()
6685         }));
6686     },
6687
6688     success : function(response){
6689         
6690         var result = this.processResponse(response);
6691         if(result === true || !result.success || !result.data){
6692             this.failureType = Roo.form.Action.LOAD_FAILURE;
6693             this.form.afterAction(this, false);
6694             return;
6695         }
6696         this.form.clearInvalid();
6697         this.form.setValues(result.data);
6698         this.form.afterAction(this, true);
6699     },
6700
6701     handleResponse : function(response){
6702         if(this.form.reader){
6703             var rs = this.form.reader.read(response);
6704             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6705             return {
6706                 success : rs.success,
6707                 data : data
6708             };
6709         }
6710         return Roo.decode(response.responseText);
6711     }
6712 });
6713
6714 Roo.form.Action.ACTION_TYPES = {
6715     'load' : Roo.form.Action.Load,
6716     'submit' : Roo.form.Action.Submit
6717 };/*
6718  * - LGPL
6719  *
6720  * form
6721  * 
6722  */
6723
6724 /**
6725  * @class Roo.bootstrap.Form
6726  * @extends Roo.bootstrap.Component
6727  * Bootstrap Form class
6728  * @cfg {String} method  GET | POST (default POST)
6729  * @cfg {String} labelAlign top | left (default top)
6730  * @cfg {String} align left  | right - for navbars
6731  * @cfg {Boolean} loadMask load mask when submit (default true)
6732
6733  * 
6734  * @constructor
6735  * Create a new Form
6736  * @param {Object} config The config object
6737  */
6738
6739
6740 Roo.bootstrap.Form = function(config){
6741     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         /**
6744          * @event clientvalidation
6745          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6746          * @param {Form} this
6747          * @param {Boolean} valid true if the form has passed client-side validation
6748          */
6749         clientvalidation: true,
6750         /**
6751          * @event beforeaction
6752          * Fires before any action is performed. Return false to cancel the action.
6753          * @param {Form} this
6754          * @param {Action} action The action to be performed
6755          */
6756         beforeaction: true,
6757         /**
6758          * @event actionfailed
6759          * Fires when an action fails.
6760          * @param {Form} this
6761          * @param {Action} action The action that failed
6762          */
6763         actionfailed : true,
6764         /**
6765          * @event actioncomplete
6766          * Fires when an action is completed.
6767          * @param {Form} this
6768          * @param {Action} action The action that completed
6769          */
6770         actioncomplete : true
6771     });
6772     
6773 };
6774
6775 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6776       
6777      /**
6778      * @cfg {String} method
6779      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6780      */
6781     method : 'POST',
6782     /**
6783      * @cfg {String} url
6784      * The URL to use for form actions if one isn't supplied in the action options.
6785      */
6786     /**
6787      * @cfg {Boolean} fileUpload
6788      * Set to true if this form is a file upload.
6789      */
6790      
6791     /**
6792      * @cfg {Object} baseParams
6793      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6794      */
6795       
6796     /**
6797      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6798      */
6799     timeout: 30,
6800     /**
6801      * @cfg {Sting} align (left|right) for navbar forms
6802      */
6803     align : 'left',
6804
6805     // private
6806     activeAction : null,
6807  
6808     /**
6809      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6810      * element by passing it or its id or mask the form itself by passing in true.
6811      * @type Mixed
6812      */
6813     waitMsgTarget : false,
6814     
6815     loadMask : true,
6816     
6817     getAutoCreate : function(){
6818         
6819         var cfg = {
6820             tag: 'form',
6821             method : this.method || 'POST',
6822             id : this.id || Roo.id(),
6823             cls : ''
6824         }
6825         if (this.parent().xtype.match(/^Nav/)) {
6826             cfg.cls = 'navbar-form navbar-' + this.align;
6827             
6828         }
6829         
6830         if (this.labelAlign == 'left' ) {
6831             cfg.cls += ' form-horizontal';
6832         }
6833         
6834         
6835         return cfg;
6836     },
6837     initEvents : function()
6838     {
6839         this.el.on('submit', this.onSubmit, this);
6840         // this was added as random key presses on the form where triggering form submit.
6841         this.el.on('keypress', function(e) {
6842             if (e.getCharCode() != 13) {
6843                 return true;
6844             }
6845             // we might need to allow it for textareas.. and some other items.
6846             // check e.getTarget().
6847             
6848             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6849                 return true;
6850             }
6851         
6852             Roo.log("keypress blocked");
6853             
6854             e.preventDefault();
6855             return false;
6856         });
6857         
6858     },
6859     // private
6860     onSubmit : function(e){
6861         e.stopEvent();
6862     },
6863     
6864      /**
6865      * Returns true if client-side validation on the form is successful.
6866      * @return Boolean
6867      */
6868     isValid : function(){
6869         var items = this.getItems();
6870         var valid = true;
6871         items.each(function(f){
6872            if(!f.validate()){
6873                valid = false;
6874                
6875            }
6876         });
6877         return valid;
6878     },
6879     /**
6880      * Returns true if any fields in this form have changed since their original load.
6881      * @return Boolean
6882      */
6883     isDirty : function(){
6884         var dirty = false;
6885         var items = this.getItems();
6886         items.each(function(f){
6887            if(f.isDirty()){
6888                dirty = true;
6889                return false;
6890            }
6891            return true;
6892         });
6893         return dirty;
6894     },
6895      /**
6896      * Performs a predefined action (submit or load) or custom actions you define on this form.
6897      * @param {String} actionName The name of the action type
6898      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6899      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6900      * accept other config options):
6901      * <pre>
6902 Property          Type             Description
6903 ----------------  ---------------  ----------------------------------------------------------------------------------
6904 url               String           The url for the action (defaults to the form's url)
6905 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6906 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6907 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6908                                    validate the form on the client (defaults to false)
6909      * </pre>
6910      * @return {BasicForm} this
6911      */
6912     doAction : function(action, options){
6913         if(typeof action == 'string'){
6914             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6915         }
6916         if(this.fireEvent('beforeaction', this, action) !== false){
6917             this.beforeAction(action);
6918             action.run.defer(100, action);
6919         }
6920         return this;
6921     },
6922     
6923     // private
6924     beforeAction : function(action){
6925         var o = action.options;
6926         
6927         if(this.loadMask){
6928             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6929         }
6930         // not really supported yet.. ??
6931         
6932         //if(this.waitMsgTarget === true){
6933         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6934         //}else if(this.waitMsgTarget){
6935         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6936         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6937         //}else {
6938         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6939        // }
6940          
6941     },
6942
6943     // private
6944     afterAction : function(action, success){
6945         this.activeAction = null;
6946         var o = action.options;
6947         
6948         //if(this.waitMsgTarget === true){
6949             this.el.unmask();
6950         //}else if(this.waitMsgTarget){
6951         //    this.waitMsgTarget.unmask();
6952         //}else{
6953         //    Roo.MessageBox.updateProgress(1);
6954         //    Roo.MessageBox.hide();
6955        // }
6956         // 
6957         if(success){
6958             if(o.reset){
6959                 this.reset();
6960             }
6961             Roo.callback(o.success, o.scope, [this, action]);
6962             this.fireEvent('actioncomplete', this, action);
6963             
6964         }else{
6965             
6966             // failure condition..
6967             // we have a scenario where updates need confirming.
6968             // eg. if a locking scenario exists..
6969             // we look for { errors : { needs_confirm : true }} in the response.
6970             if (
6971                 (typeof(action.result) != 'undefined')  &&
6972                 (typeof(action.result.errors) != 'undefined')  &&
6973                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6974            ){
6975                 var _t = this;
6976                 Roo.log("not supported yet");
6977                  /*
6978                 
6979                 Roo.MessageBox.confirm(
6980                     "Change requires confirmation",
6981                     action.result.errorMsg,
6982                     function(r) {
6983                         if (r != 'yes') {
6984                             return;
6985                         }
6986                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6987                     }
6988                     
6989                 );
6990                 */
6991                 
6992                 
6993                 return;
6994             }
6995             
6996             Roo.callback(o.failure, o.scope, [this, action]);
6997             // show an error message if no failed handler is set..
6998             if (!this.hasListener('actionfailed')) {
6999                 Roo.log("need to add dialog support");
7000                 /*
7001                 Roo.MessageBox.alert("Error",
7002                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7003                         action.result.errorMsg :
7004                         "Saving Failed, please check your entries or try again"
7005                 );
7006                 */
7007             }
7008             
7009             this.fireEvent('actionfailed', this, action);
7010         }
7011         
7012     },
7013     /**
7014      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7015      * @param {String} id The value to search for
7016      * @return Field
7017      */
7018     findField : function(id){
7019         var items = this.getItems();
7020         var field = items.get(id);
7021         if(!field){
7022              items.each(function(f){
7023                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7024                     field = f;
7025                     return false;
7026                 }
7027                 return true;
7028             });
7029         }
7030         return field || null;
7031     },
7032      /**
7033      * Mark fields in this form invalid in bulk.
7034      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7035      * @return {BasicForm} this
7036      */
7037     markInvalid : function(errors){
7038         if(errors instanceof Array){
7039             for(var i = 0, len = errors.length; i < len; i++){
7040                 var fieldError = errors[i];
7041                 var f = this.findField(fieldError.id);
7042                 if(f){
7043                     f.markInvalid(fieldError.msg);
7044                 }
7045             }
7046         }else{
7047             var field, id;
7048             for(id in errors){
7049                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7050                     field.markInvalid(errors[id]);
7051                 }
7052             }
7053         }
7054         //Roo.each(this.childForms || [], function (f) {
7055         //    f.markInvalid(errors);
7056         //});
7057         
7058         return this;
7059     },
7060
7061     /**
7062      * Set values for fields in this form in bulk.
7063      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7064      * @return {BasicForm} this
7065      */
7066     setValues : function(values){
7067         if(values instanceof Array){ // array of objects
7068             for(var i = 0, len = values.length; i < len; i++){
7069                 var v = values[i];
7070                 var f = this.findField(v.id);
7071                 if(f){
7072                     f.setValue(v.value);
7073                     if(this.trackResetOnLoad){
7074                         f.originalValue = f.getValue();
7075                     }
7076                 }
7077             }
7078         }else{ // object hash
7079             var field, id;
7080             for(id in values){
7081                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7082                     
7083                     if (field.setFromData && 
7084                         field.valueField && 
7085                         field.displayField &&
7086                         // combos' with local stores can 
7087                         // be queried via setValue()
7088                         // to set their value..
7089                         (field.store && !field.store.isLocal)
7090                         ) {
7091                         // it's a combo
7092                         var sd = { };
7093                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7094                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7095                         field.setFromData(sd);
7096                         
7097                     } else {
7098                         field.setValue(values[id]);
7099                     }
7100                     
7101                     
7102                     if(this.trackResetOnLoad){
7103                         field.originalValue = field.getValue();
7104                     }
7105                 }
7106             }
7107         }
7108          
7109         //Roo.each(this.childForms || [], function (f) {
7110         //    f.setValues(values);
7111         //});
7112                 
7113         return this;
7114     },
7115
7116     /**
7117      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7118      * they are returned as an array.
7119      * @param {Boolean} asString
7120      * @return {Object}
7121      */
7122     getValues : function(asString){
7123         //if (this.childForms) {
7124             // copy values from the child forms
7125         //    Roo.each(this.childForms, function (f) {
7126         //        this.setValues(f.getValues());
7127         //    }, this);
7128         //}
7129         
7130         
7131         
7132         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7133         if(asString === true){
7134             return fs;
7135         }
7136         return Roo.urlDecode(fs);
7137     },
7138     
7139     /**
7140      * Returns the fields in this form as an object with key/value pairs. 
7141      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7142      * @return {Object}
7143      */
7144     getFieldValues : function(with_hidden)
7145     {
7146         var items = this.getItems();
7147         var ret = {};
7148         items.each(function(f){
7149             if (!f.getName()) {
7150                 return;
7151             }
7152             var v = f.getValue();
7153             if (f.inputType =='radio') {
7154                 if (typeof(ret[f.getName()]) == 'undefined') {
7155                     ret[f.getName()] = ''; // empty..
7156                 }
7157                 
7158                 if (!f.el.dom.checked) {
7159                     return;
7160                     
7161                 }
7162                 v = f.el.dom.value;
7163                 
7164             }
7165             
7166             // not sure if this supported any more..
7167             if ((typeof(v) == 'object') && f.getRawValue) {
7168                 v = f.getRawValue() ; // dates..
7169             }
7170             // combo boxes where name != hiddenName...
7171             if (f.name != f.getName()) {
7172                 ret[f.name] = f.getRawValue();
7173             }
7174             ret[f.getName()] = v;
7175         });
7176         
7177         return ret;
7178     },
7179
7180     /**
7181      * Clears all invalid messages in this form.
7182      * @return {BasicForm} this
7183      */
7184     clearInvalid : function(){
7185         var items = this.getItems();
7186         
7187         items.each(function(f){
7188            f.clearInvalid();
7189         });
7190         
7191         
7192         
7193         return this;
7194     },
7195
7196     /**
7197      * Resets this form.
7198      * @return {BasicForm} this
7199      */
7200     reset : function(){
7201         var items = this.getItems();
7202         items.each(function(f){
7203             f.reset();
7204         });
7205         
7206         Roo.each(this.childForms || [], function (f) {
7207             f.reset();
7208         });
7209        
7210         
7211         return this;
7212     },
7213     getItems : function()
7214     {
7215         var r=new Roo.util.MixedCollection(false, function(o){
7216             return o.id || (o.id = Roo.id());
7217         });
7218         var iter = function(el) {
7219             if (el.inputEl) {
7220                 r.add(el);
7221             }
7222             if (!el.items) {
7223                 return;
7224             }
7225             Roo.each(el.items,function(e) {
7226                 iter(e);
7227             });
7228             
7229             
7230         };
7231         
7232         iter(this);
7233         return r;
7234         
7235         
7236         
7237         
7238     }
7239     
7240 });
7241
7242  
7243 /*
7244  * Based on:
7245  * Ext JS Library 1.1.1
7246  * Copyright(c) 2006-2007, Ext JS, LLC.
7247  *
7248  * Originally Released Under LGPL - original licence link has changed is not relivant.
7249  *
7250  * Fork - LGPL
7251  * <script type="text/javascript">
7252  */
7253 /**
7254  * @class Roo.form.VTypes
7255  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7256  * @singleton
7257  */
7258 Roo.form.VTypes = function(){
7259     // closure these in so they are only created once.
7260     var alpha = /^[a-zA-Z_]+$/;
7261     var alphanum = /^[a-zA-Z0-9_]+$/;
7262     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7263     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7264
7265     // All these messages and functions are configurable
7266     return {
7267         /**
7268          * The function used to validate email addresses
7269          * @param {String} value The email address
7270          */
7271         'email' : function(v){
7272             return email.test(v);
7273         },
7274         /**
7275          * The error text to display when the email validation function returns false
7276          * @type String
7277          */
7278         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7279         /**
7280          * The keystroke filter mask to be applied on email input
7281          * @type RegExp
7282          */
7283         'emailMask' : /[a-z0-9_\.\-@]/i,
7284
7285         /**
7286          * The function used to validate URLs
7287          * @param {String} value The URL
7288          */
7289         'url' : function(v){
7290             return url.test(v);
7291         },
7292         /**
7293          * The error text to display when the url validation function returns false
7294          * @type String
7295          */
7296         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7297         
7298         /**
7299          * The function used to validate alpha values
7300          * @param {String} value The value
7301          */
7302         'alpha' : function(v){
7303             return alpha.test(v);
7304         },
7305         /**
7306          * The error text to display when the alpha validation function returns false
7307          * @type String
7308          */
7309         'alphaText' : 'This field should only contain letters and _',
7310         /**
7311          * The keystroke filter mask to be applied on alpha input
7312          * @type RegExp
7313          */
7314         'alphaMask' : /[a-z_]/i,
7315
7316         /**
7317          * The function used to validate alphanumeric values
7318          * @param {String} value The value
7319          */
7320         'alphanum' : function(v){
7321             return alphanum.test(v);
7322         },
7323         /**
7324          * The error text to display when the alphanumeric validation function returns false
7325          * @type String
7326          */
7327         'alphanumText' : 'This field should only contain letters, numbers and _',
7328         /**
7329          * The keystroke filter mask to be applied on alphanumeric input
7330          * @type RegExp
7331          */
7332         'alphanumMask' : /[a-z0-9_]/i
7333     };
7334 }();/*
7335  * - LGPL
7336  *
7337  * Input
7338  * 
7339  */
7340
7341 /**
7342  * @class Roo.bootstrap.Input
7343  * @extends Roo.bootstrap.Component
7344  * Bootstrap Input class
7345  * @cfg {Boolean} disabled is it disabled
7346  * @cfg {String} fieldLabel - the label associated
7347  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7348  * @cfg {String} name name of the input
7349  * @cfg {string} fieldLabel - the label associated
7350  * @cfg {string}  inputType - input / file submit ...
7351  * @cfg {string} placeholder - placeholder to put in text.
7352  * @cfg {string}  before - input group add on before
7353  * @cfg {string} after - input group add on after
7354  * @cfg {string} size - (lg|sm) or leave empty..
7355  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7356  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7357  * @cfg {Number} md colspan out of 12 for computer-sized screens
7358  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7359  * @cfg {string} value default value of the input
7360  * @cfg {Number} labelWidth set the width of label (0-12)
7361  * @cfg {String} labelAlign (top|left)
7362  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7363  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7364
7365  * @cfg {String} align (left|center|right) Default left
7366  * 
7367  * 
7368  * 
7369  * @constructor
7370  * Create a new Input
7371  * @param {Object} config The config object
7372  */
7373
7374 Roo.bootstrap.Input = function(config){
7375     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7376    
7377         this.addEvents({
7378             /**
7379              * @event focus
7380              * Fires when this field receives input focus.
7381              * @param {Roo.form.Field} this
7382              */
7383             focus : true,
7384             /**
7385              * @event blur
7386              * Fires when this field loses input focus.
7387              * @param {Roo.form.Field} this
7388              */
7389             blur : true,
7390             /**
7391              * @event specialkey
7392              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7393              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7394              * @param {Roo.form.Field} this
7395              * @param {Roo.EventObject} e The event object
7396              */
7397             specialkey : true,
7398             /**
7399              * @event change
7400              * Fires just before the field blurs if the field value has changed.
7401              * @param {Roo.form.Field} this
7402              * @param {Mixed} newValue The new value
7403              * @param {Mixed} oldValue The original value
7404              */
7405             change : true,
7406             /**
7407              * @event invalid
7408              * Fires after the field has been marked as invalid.
7409              * @param {Roo.form.Field} this
7410              * @param {String} msg The validation message
7411              */
7412             invalid : true,
7413             /**
7414              * @event valid
7415              * Fires after the field has been validated with no errors.
7416              * @param {Roo.form.Field} this
7417              */
7418             valid : true,
7419              /**
7420              * @event keyup
7421              * Fires after the key up
7422              * @param {Roo.form.Field} this
7423              * @param {Roo.EventObject}  e The event Object
7424              */
7425             keyup : true
7426         });
7427 };
7428
7429 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7430      /**
7431      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7432       automatic validation (defaults to "keyup").
7433      */
7434     validationEvent : "keyup",
7435      /**
7436      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7437      */
7438     validateOnBlur : true,
7439     /**
7440      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7441      */
7442     validationDelay : 250,
7443      /**
7444      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7445      */
7446     focusClass : "x-form-focus",  // not needed???
7447     
7448        
7449     /**
7450      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7451      */
7452     invalidClass : "has-warning",
7453     
7454     /**
7455      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7456      */
7457     validClass : "has-success",
7458     
7459     /**
7460      * @cfg {Boolean} hasFeedback (true|false) default true
7461      */
7462     hasFeedback : true,
7463     
7464     /**
7465      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7466      */
7467     invalidFeedbackClass : "glyphicon-warning-sign",
7468     
7469     /**
7470      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7471      */
7472     validFeedbackClass : "glyphicon-ok",
7473     
7474     /**
7475      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7476      */
7477     selectOnFocus : false,
7478     
7479      /**
7480      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7481      */
7482     maskRe : null,
7483        /**
7484      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7485      */
7486     vtype : null,
7487     
7488       /**
7489      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7490      */
7491     disableKeyFilter : false,
7492     
7493        /**
7494      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7495      */
7496     disabled : false,
7497      /**
7498      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7499      */
7500     allowBlank : true,
7501     /**
7502      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7503      */
7504     blankText : "This field is required",
7505     
7506      /**
7507      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7508      */
7509     minLength : 0,
7510     /**
7511      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7512      */
7513     maxLength : Number.MAX_VALUE,
7514     /**
7515      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7516      */
7517     minLengthText : "The minimum length for this field is {0}",
7518     /**
7519      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7520      */
7521     maxLengthText : "The maximum length for this field is {0}",
7522   
7523     
7524     /**
7525      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7526      * If available, this function will be called only after the basic validators all return true, and will be passed the
7527      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7528      */
7529     validator : null,
7530     /**
7531      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7532      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7533      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7534      */
7535     regex : null,
7536     /**
7537      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7538      */
7539     regexText : "",
7540     
7541     autocomplete: false,
7542     
7543     
7544     fieldLabel : '',
7545     inputType : 'text',
7546     
7547     name : false,
7548     placeholder: false,
7549     before : false,
7550     after : false,
7551     size : false,
7552     hasFocus : false,
7553     preventMark: false,
7554     isFormField : true,
7555     value : '',
7556     labelWidth : 2,
7557     labelAlign : false,
7558     readOnly : false,
7559     align : false,
7560     formatedValue : false,
7561     
7562     parentLabelAlign : function()
7563     {
7564         var parent = this;
7565         while (parent.parent()) {
7566             parent = parent.parent();
7567             if (typeof(parent.labelAlign) !='undefined') {
7568                 return parent.labelAlign;
7569             }
7570         }
7571         return 'left';
7572         
7573     },
7574     
7575     getAutoCreate : function(){
7576         
7577         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7578         
7579         var id = Roo.id();
7580         
7581         var cfg = {};
7582         
7583         if(this.inputType != 'hidden'){
7584             cfg.cls = 'form-group' //input-group
7585         }
7586         
7587         var input =  {
7588             tag: 'input',
7589             id : id,
7590             type : this.inputType,
7591             value : this.value,
7592             cls : 'form-control',
7593             placeholder : this.placeholder || '',
7594             autocomplete : this.autocomplete || 'new-password'
7595         };
7596         
7597         
7598         if(this.align){
7599             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7600         }
7601         
7602         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7603             input.maxLength = this.maxLength;
7604         }
7605         
7606         if (this.disabled) {
7607             input.disabled=true;
7608         }
7609         
7610         if (this.readOnly) {
7611             input.readonly=true;
7612         }
7613         
7614         if (this.name) {
7615             input.name = this.name;
7616         }
7617         if (this.size) {
7618             input.cls += ' input-' + this.size;
7619         }
7620         var settings=this;
7621         ['xs','sm','md','lg'].map(function(size){
7622             if (settings[size]) {
7623                 cfg.cls += ' col-' + size + '-' + settings[size];
7624             }
7625         });
7626         
7627         var inputblock = input;
7628         
7629         var feedback = {
7630             tag: 'span',
7631             cls: 'glyphicon form-control-feedback'
7632         };
7633             
7634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7635             
7636             inputblock = {
7637                 cls : 'has-feedback',
7638                 cn :  [
7639                     input,
7640                     feedback
7641                 ] 
7642             };  
7643         }
7644         
7645         if (this.before || this.after) {
7646             
7647             inputblock = {
7648                 cls : 'input-group',
7649                 cn :  [] 
7650             };
7651             
7652             if (this.before && typeof(this.before) == 'string') {
7653                 
7654                 inputblock.cn.push({
7655                     tag :'span',
7656                     cls : 'roo-input-before input-group-addon',
7657                     html : this.before
7658                 });
7659             }
7660             if (this.before && typeof(this.before) == 'object') {
7661                 this.before = Roo.factory(this.before);
7662                 Roo.log(this.before);
7663                 inputblock.cn.push({
7664                     tag :'span',
7665                     cls : 'roo-input-before input-group-' +
7666                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7667                 });
7668             }
7669             
7670             inputblock.cn.push(input);
7671             
7672             if (this.after && typeof(this.after) == 'string') {
7673                 inputblock.cn.push({
7674                     tag :'span',
7675                     cls : 'roo-input-after input-group-addon',
7676                     html : this.after
7677                 });
7678             }
7679             if (this.after && typeof(this.after) == 'object') {
7680                 this.after = Roo.factory(this.after);
7681                 Roo.log(this.after);
7682                 inputblock.cn.push({
7683                     tag :'span',
7684                     cls : 'roo-input-after input-group-' +
7685                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7686                 });
7687             }
7688             
7689             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7690                 inputblock.cls += ' has-feedback';
7691                 inputblock.cn.push(feedback);
7692             }
7693         };
7694         
7695         if (align ==='left' && this.fieldLabel.length) {
7696                 Roo.log("left and has label");
7697                 cfg.cn = [
7698                     
7699                     {
7700                         tag: 'label',
7701                         'for' :  id,
7702                         cls : 'control-label col-sm-' + this.labelWidth,
7703                         html : this.fieldLabel
7704                         
7705                     },
7706                     {
7707                         cls : "col-sm-" + (12 - this.labelWidth), 
7708                         cn: [
7709                             inputblock
7710                         ]
7711                     }
7712                     
7713                 ];
7714         } else if ( this.fieldLabel.length) {
7715                 Roo.log(" label");
7716                  cfg.cn = [
7717                    
7718                     {
7719                         tag: 'label',
7720                         //cls : 'input-group-addon',
7721                         html : this.fieldLabel
7722                         
7723                     },
7724                     
7725                     inputblock
7726                     
7727                 ];
7728
7729         } else {
7730             
7731                 Roo.log(" no label && no align");
7732                 cfg.cn = [
7733                     
7734                         inputblock
7735                     
7736                 ];
7737                 
7738                 
7739         };
7740         Roo.log('input-parentType: ' + this.parentType);
7741         
7742         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7743            cfg.cls += ' navbar-form';
7744            Roo.log(cfg);
7745         }
7746         
7747         return cfg;
7748         
7749     },
7750     /**
7751      * return the real input element.
7752      */
7753     inputEl: function ()
7754     {
7755         return this.el.select('input.form-control',true).first();
7756     },
7757     
7758     tooltipEl : function()
7759     {
7760         return this.inputEl();
7761     },
7762     
7763     setDisabled : function(v)
7764     {
7765         var i  = this.inputEl().dom;
7766         if (!v) {
7767             i.removeAttribute('disabled');
7768             return;
7769             
7770         }
7771         i.setAttribute('disabled','true');
7772     },
7773     initEvents : function()
7774     {
7775           
7776         this.inputEl().on("keydown" , this.fireKey,  this);
7777         this.inputEl().on("focus", this.onFocus,  this);
7778         this.inputEl().on("blur", this.onBlur,  this);
7779         
7780         this.inputEl().relayEvent('keyup', this);
7781
7782         // reference to original value for reset
7783         this.originalValue = this.getValue();
7784         //Roo.form.TextField.superclass.initEvents.call(this);
7785         if(this.validationEvent == 'keyup'){
7786             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7787             this.inputEl().on('keyup', this.filterValidation, this);
7788         }
7789         else if(this.validationEvent !== false){
7790             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7791         }
7792         
7793         if(this.selectOnFocus){
7794             this.on("focus", this.preFocus, this);
7795             
7796         }
7797         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7798             this.inputEl().on("keypress", this.filterKeys, this);
7799         }
7800        /* if(this.grow){
7801             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7802             this.el.on("click", this.autoSize,  this);
7803         }
7804         */
7805         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7806             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7807         }
7808         
7809         if (typeof(this.before) == 'object') {
7810             this.before.render(this.el.select('.roo-input-before',true).first());
7811         }
7812         if (typeof(this.after) == 'object') {
7813             this.after.render(this.el.select('.roo-input-after',true).first());
7814         }
7815         
7816         
7817     },
7818     filterValidation : function(e){
7819         if(!e.isNavKeyPress()){
7820             this.validationTask.delay(this.validationDelay);
7821         }
7822     },
7823      /**
7824      * Validates the field value
7825      * @return {Boolean} True if the value is valid, else false
7826      */
7827     validate : function(){
7828         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7829         if(this.disabled || this.validateValue(this.getRawValue())){
7830             this.markValid();
7831             return true;
7832         }
7833         
7834         this.markInvalid();
7835         return false;
7836     },
7837     
7838     
7839     /**
7840      * Validates a value according to the field's validation rules and marks the field as invalid
7841      * if the validation fails
7842      * @param {Mixed} value The value to validate
7843      * @return {Boolean} True if the value is valid, else false
7844      */
7845     validateValue : function(value){
7846         if(value.length < 1)  { // if it's blank
7847             if(this.allowBlank){
7848                 return true;
7849             }
7850             return false;
7851         }
7852         
7853         if(value.length < this.minLength){
7854             return false;
7855         }
7856         if(value.length > this.maxLength){
7857             return false;
7858         }
7859         if(this.vtype){
7860             var vt = Roo.form.VTypes;
7861             if(!vt[this.vtype](value, this)){
7862                 return false;
7863             }
7864         }
7865         if(typeof this.validator == "function"){
7866             var msg = this.validator(value);
7867             if(msg !== true){
7868                 return false;
7869             }
7870         }
7871         
7872         if(this.regex && !this.regex.test(value)){
7873             return false;
7874         }
7875         
7876         return true;
7877     },
7878
7879     
7880     
7881      // private
7882     fireKey : function(e){
7883         //Roo.log('field ' + e.getKey());
7884         if(e.isNavKeyPress()){
7885             this.fireEvent("specialkey", this, e);
7886         }
7887     },
7888     focus : function (selectText){
7889         if(this.rendered){
7890             this.inputEl().focus();
7891             if(selectText === true){
7892                 this.inputEl().dom.select();
7893             }
7894         }
7895         return this;
7896     } ,
7897     
7898     onFocus : function(){
7899         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7900            // this.el.addClass(this.focusClass);
7901         }
7902         if(!this.hasFocus){
7903             this.hasFocus = true;
7904             this.startValue = this.getValue();
7905             this.fireEvent("focus", this);
7906         }
7907     },
7908     
7909     beforeBlur : Roo.emptyFn,
7910
7911     
7912     // private
7913     onBlur : function(){
7914         this.beforeBlur();
7915         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7916             //this.el.removeClass(this.focusClass);
7917         }
7918         this.hasFocus = false;
7919         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7920             this.validate();
7921         }
7922         var v = this.getValue();
7923         if(String(v) !== String(this.startValue)){
7924             this.fireEvent('change', this, v, this.startValue);
7925         }
7926         this.fireEvent("blur", this);
7927     },
7928     
7929     /**
7930      * Resets the current field value to the originally loaded value and clears any validation messages
7931      */
7932     reset : function(){
7933         this.setValue(this.originalValue);
7934         this.validate();
7935     },
7936      /**
7937      * Returns the name of the field
7938      * @return {Mixed} name The name field
7939      */
7940     getName: function(){
7941         return this.name;
7942     },
7943      /**
7944      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7945      * @return {Mixed} value The field value
7946      */
7947     getValue : function(){
7948         
7949         var v = this.inputEl().getValue();
7950         
7951         return v;
7952     },
7953     /**
7954      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7955      * @return {Mixed} value The field value
7956      */
7957     getRawValue : function(){
7958         var v = this.inputEl().getValue();
7959         
7960         return v;
7961     },
7962     
7963     /**
7964      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7965      * @param {Mixed} value The value to set
7966      */
7967     setRawValue : function(v){
7968         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7969     },
7970     
7971     selectText : function(start, end){
7972         var v = this.getRawValue();
7973         if(v.length > 0){
7974             start = start === undefined ? 0 : start;
7975             end = end === undefined ? v.length : end;
7976             var d = this.inputEl().dom;
7977             if(d.setSelectionRange){
7978                 d.setSelectionRange(start, end);
7979             }else if(d.createTextRange){
7980                 var range = d.createTextRange();
7981                 range.moveStart("character", start);
7982                 range.moveEnd("character", v.length-end);
7983                 range.select();
7984             }
7985         }
7986     },
7987     
7988     /**
7989      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7990      * @param {Mixed} value The value to set
7991      */
7992     setValue : function(v){
7993         this.value = v;
7994         if(this.rendered){
7995             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7996             this.validate();
7997         }
7998     },
7999     
8000     /*
8001     processValue : function(value){
8002         if(this.stripCharsRe){
8003             var newValue = value.replace(this.stripCharsRe, '');
8004             if(newValue !== value){
8005                 this.setRawValue(newValue);
8006                 return newValue;
8007             }
8008         }
8009         return value;
8010     },
8011   */
8012     preFocus : function(){
8013         
8014         if(this.selectOnFocus){
8015             this.inputEl().dom.select();
8016         }
8017     },
8018     filterKeys : function(e){
8019         var k = e.getKey();
8020         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8021             return;
8022         }
8023         var c = e.getCharCode(), cc = String.fromCharCode(c);
8024         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8025             return;
8026         }
8027         if(!this.maskRe.test(cc)){
8028             e.stopEvent();
8029         }
8030     },
8031      /**
8032      * Clear any invalid styles/messages for this field
8033      */
8034     clearInvalid : function(){
8035         
8036         if(!this.el || this.preventMark){ // not rendered
8037             return;
8038         }
8039         this.el.removeClass(this.invalidClass);
8040         
8041         this.fireEvent('valid', this);
8042     },
8043     
8044      /**
8045      * Mark this field as valid
8046      */
8047     markValid : function(){
8048         if(!this.el  || this.preventMark){ // not rendered
8049             return;
8050         }
8051         
8052         this.el.removeClass([this.invalidClass, this.validClass]);
8053         
8054         if(this.disabled || this.allowBlank){
8055             return;
8056         }
8057         
8058         this.el.addClass(this.validClass);
8059         
8060         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8061             
8062             var feedback = this.el.select('.form-control-feedback', true).first();
8063             
8064             if(feedback){
8065                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8066                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8067             }
8068             
8069         }
8070         
8071         this.fireEvent('valid', this);
8072     },
8073     
8074      /**
8075      * Mark this field as invalid
8076      * @param {String} msg The validation message
8077      */
8078     markInvalid : function(msg){
8079         if(!this.el  || this.preventMark){ // not rendered
8080             return;
8081         }
8082         
8083         this.el.removeClass([this.invalidClass, this.validClass]);
8084         
8085         if(this.disabled || this.allowBlank){
8086             return;
8087         }
8088         
8089         this.el.addClass(this.invalidClass);
8090         
8091         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8092             
8093             var feedback = this.el.select('.form-control-feedback', true).first();
8094             
8095             if(feedback){
8096                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8097                 
8098                 if(this.getValue().length){
8099                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8100                 }
8101                 
8102             }
8103             
8104         }
8105         
8106         this.fireEvent('invalid', this, msg);
8107     },
8108     // private
8109     SafariOnKeyDown : function(event)
8110     {
8111         // this is a workaround for a password hang bug on chrome/ webkit.
8112         
8113         var isSelectAll = false;
8114         
8115         if(this.inputEl().dom.selectionEnd > 0){
8116             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8117         }
8118         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8119             event.preventDefault();
8120             this.setValue('');
8121             return;
8122         }
8123         
8124         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8125             
8126             event.preventDefault();
8127             // this is very hacky as keydown always get's upper case.
8128             //
8129             var cc = String.fromCharCode(event.getCharCode());
8130             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8131             
8132         }
8133     },
8134     adjustWidth : function(tag, w){
8135         tag = tag.toLowerCase();
8136         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8137             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8138                 if(tag == 'input'){
8139                     return w + 2;
8140                 }
8141                 if(tag == 'textarea'){
8142                     return w-2;
8143                 }
8144             }else if(Roo.isOpera){
8145                 if(tag == 'input'){
8146                     return w + 2;
8147                 }
8148                 if(tag == 'textarea'){
8149                     return w-2;
8150                 }
8151             }
8152         }
8153         return w;
8154     }
8155     
8156 });
8157
8158  
8159 /*
8160  * - LGPL
8161  *
8162  * Input
8163  * 
8164  */
8165
8166 /**
8167  * @class Roo.bootstrap.TextArea
8168  * @extends Roo.bootstrap.Input
8169  * Bootstrap TextArea class
8170  * @cfg {Number} cols Specifies the visible width of a text area
8171  * @cfg {Number} rows Specifies the visible number of lines in a text area
8172  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8173  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8174  * @cfg {string} html text
8175  * 
8176  * @constructor
8177  * Create a new TextArea
8178  * @param {Object} config The config object
8179  */
8180
8181 Roo.bootstrap.TextArea = function(config){
8182     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8183    
8184 };
8185
8186 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8187      
8188     cols : false,
8189     rows : 5,
8190     readOnly : false,
8191     warp : 'soft',
8192     resize : false,
8193     value: false,
8194     html: false,
8195     
8196     getAutoCreate : function(){
8197         
8198         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8199         
8200         var id = Roo.id();
8201         
8202         var cfg = {};
8203         
8204         var input =  {
8205             tag: 'textarea',
8206             id : id,
8207             warp : this.warp,
8208             rows : this.rows,
8209             value : this.value || '',
8210             html: this.html || '',
8211             cls : 'form-control',
8212             placeholder : this.placeholder || '' 
8213             
8214         };
8215         
8216         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8217             input.maxLength = this.maxLength;
8218         }
8219         
8220         if(this.resize){
8221             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8222         }
8223         
8224         if(this.cols){
8225             input.cols = this.cols;
8226         }
8227         
8228         if (this.readOnly) {
8229             input.readonly = true;
8230         }
8231         
8232         if (this.name) {
8233             input.name = this.name;
8234         }
8235         
8236         if (this.size) {
8237             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8238         }
8239         
8240         var settings=this;
8241         ['xs','sm','md','lg'].map(function(size){
8242             if (settings[size]) {
8243                 cfg.cls += ' col-' + size + '-' + settings[size];
8244             }
8245         });
8246         
8247         var inputblock = input;
8248         
8249         if(this.hasFeedback && !this.allowBlank){
8250             
8251             var feedback = {
8252                 tag: 'span',
8253                 cls: 'glyphicon form-control-feedback'
8254             };
8255
8256             inputblock = {
8257                 cls : 'has-feedback',
8258                 cn :  [
8259                     input,
8260                     feedback
8261                 ] 
8262             };  
8263         }
8264         
8265         
8266         if (this.before || this.after) {
8267             
8268             inputblock = {
8269                 cls : 'input-group',
8270                 cn :  [] 
8271             };
8272             if (this.before) {
8273                 inputblock.cn.push({
8274                     tag :'span',
8275                     cls : 'input-group-addon',
8276                     html : this.before
8277                 });
8278             }
8279             
8280             inputblock.cn.push(input);
8281             
8282             if(this.hasFeedback && !this.allowBlank){
8283                 inputblock.cls += ' has-feedback';
8284                 inputblock.cn.push(feedback);
8285             }
8286             
8287             if (this.after) {
8288                 inputblock.cn.push({
8289                     tag :'span',
8290                     cls : 'input-group-addon',
8291                     html : this.after
8292                 });
8293             }
8294             
8295         }
8296         
8297         if (align ==='left' && this.fieldLabel.length) {
8298                 Roo.log("left and has label");
8299                 cfg.cn = [
8300                     
8301                     {
8302                         tag: 'label',
8303                         'for' :  id,
8304                         cls : 'control-label col-sm-' + this.labelWidth,
8305                         html : this.fieldLabel
8306                         
8307                     },
8308                     {
8309                         cls : "col-sm-" + (12 - this.labelWidth), 
8310                         cn: [
8311                             inputblock
8312                         ]
8313                     }
8314                     
8315                 ];
8316         } else if ( this.fieldLabel.length) {
8317                 Roo.log(" label");
8318                  cfg.cn = [
8319                    
8320                     {
8321                         tag: 'label',
8322                         //cls : 'input-group-addon',
8323                         html : this.fieldLabel
8324                         
8325                     },
8326                     
8327                     inputblock
8328                     
8329                 ];
8330
8331         } else {
8332             
8333                    Roo.log(" no label && no align");
8334                 cfg.cn = [
8335                     
8336                         inputblock
8337                     
8338                 ];
8339                 
8340                 
8341         }
8342         
8343         if (this.disabled) {
8344             input.disabled=true;
8345         }
8346         
8347         return cfg;
8348         
8349     },
8350     /**
8351      * return the real textarea element.
8352      */
8353     inputEl: function ()
8354     {
8355         return this.el.select('textarea.form-control',true).first();
8356     }
8357 });
8358
8359  
8360 /*
8361  * - LGPL
8362  *
8363  * trigger field - base class for combo..
8364  * 
8365  */
8366  
8367 /**
8368  * @class Roo.bootstrap.TriggerField
8369  * @extends Roo.bootstrap.Input
8370  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8371  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8372  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8373  * for which you can provide a custom implementation.  For example:
8374  * <pre><code>
8375 var trigger = new Roo.bootstrap.TriggerField();
8376 trigger.onTriggerClick = myTriggerFn;
8377 trigger.applyTo('my-field');
8378 </code></pre>
8379  *
8380  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8381  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8382  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8383  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8384  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8385
8386  * @constructor
8387  * Create a new TriggerField.
8388  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8389  * to the base TextField)
8390  */
8391 Roo.bootstrap.TriggerField = function(config){
8392     this.mimicing = false;
8393     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8394 };
8395
8396 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8397     /**
8398      * @cfg {String} triggerClass A CSS class to apply to the trigger
8399      */
8400      /**
8401      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8402      */
8403     hideTrigger:false,
8404
8405     /** @cfg {Boolean} grow @hide */
8406     /** @cfg {Number} growMin @hide */
8407     /** @cfg {Number} growMax @hide */
8408
8409     /**
8410      * @hide 
8411      * @method
8412      */
8413     autoSize: Roo.emptyFn,
8414     // private
8415     monitorTab : true,
8416     // private
8417     deferHeight : true,
8418
8419     
8420     actionMode : 'wrap',
8421     
8422     caret : false,
8423     
8424     
8425     getAutoCreate : function(){
8426        
8427         var align = this.labelAlign || this.parentLabelAlign();
8428         
8429         var id = Roo.id();
8430         
8431         var cfg = {
8432             cls: 'form-group' //input-group
8433         };
8434         
8435         
8436         var input =  {
8437             tag: 'input',
8438             id : id,
8439             type : this.inputType,
8440             cls : 'form-control',
8441             autocomplete: 'new-password',
8442             placeholder : this.placeholder || '' 
8443             
8444         };
8445         if (this.name) {
8446             input.name = this.name;
8447         }
8448         if (this.size) {
8449             input.cls += ' input-' + this.size;
8450         }
8451         
8452         if (this.disabled) {
8453             input.disabled=true;
8454         }
8455         
8456         var inputblock = input;
8457         
8458         if(this.hasFeedback && !this.allowBlank){
8459             
8460             var feedback = {
8461                 tag: 'span',
8462                 cls: 'glyphicon form-control-feedback'
8463             };
8464
8465             inputblock = {
8466                 cls : 'has-feedback',
8467                 cn :  [
8468                     input,
8469                     feedback
8470                 ] 
8471             };  
8472         }
8473         
8474         if (this.before || this.after) {
8475             
8476             inputblock = {
8477                 cls : 'input-group',
8478                 cn :  [] 
8479             };
8480             if (this.before) {
8481                 inputblock.cn.push({
8482                     tag :'span',
8483                     cls : 'input-group-addon',
8484                     html : this.before
8485                 });
8486             }
8487             
8488             inputblock.cn.push(input);
8489             
8490             if(this.hasFeedback && !this.allowBlank){
8491                 inputblock.cls += ' has-feedback';
8492                 inputblock.cn.push(feedback);
8493             }
8494             
8495             if (this.after) {
8496                 inputblock.cn.push({
8497                     tag :'span',
8498                     cls : 'input-group-addon',
8499                     html : this.after
8500                 });
8501             }
8502             
8503         };
8504         
8505         var box = {
8506             tag: 'div',
8507             cn: [
8508                 {
8509                     tag: 'input',
8510                     type : 'hidden',
8511                     cls: 'form-hidden-field'
8512                 },
8513                 inputblock
8514             ]
8515             
8516         };
8517         
8518         if(this.multiple){
8519             Roo.log('multiple');
8520             
8521             box = {
8522                 tag: 'div',
8523                 cn: [
8524                     {
8525                         tag: 'input',
8526                         type : 'hidden',
8527                         cls: 'form-hidden-field'
8528                     },
8529                     {
8530                         tag: 'ul',
8531                         cls: 'select2-choices',
8532                         cn:[
8533                             {
8534                                 tag: 'li',
8535                                 cls: 'select2-search-field',
8536                                 cn: [
8537
8538                                     inputblock
8539                                 ]
8540                             }
8541                         ]
8542                     }
8543                 ]
8544             }
8545         };
8546         
8547         var combobox = {
8548             cls: 'select2-container input-group',
8549             cn: [
8550                 box
8551 //                {
8552 //                    tag: 'ul',
8553 //                    cls: 'typeahead typeahead-long dropdown-menu',
8554 //                    style: 'display:none'
8555 //                }
8556             ]
8557         };
8558         
8559         if(!this.multiple && this.showToggleBtn){
8560             
8561             var caret = {
8562                         tag: 'span',
8563                         cls: 'caret'
8564              };
8565             if (this.caret != false) {
8566                 caret = {
8567                      tag: 'i',
8568                      cls: 'fa fa-' + this.caret
8569                 };
8570                 
8571             }
8572             
8573             combobox.cn.push({
8574                 tag :'span',
8575                 cls : 'input-group-addon btn dropdown-toggle',
8576                 cn : [
8577                     caret,
8578                     {
8579                         tag: 'span',
8580                         cls: 'combobox-clear',
8581                         cn  : [
8582                             {
8583                                 tag : 'i',
8584                                 cls: 'icon-remove'
8585                             }
8586                         ]
8587                     }
8588                 ]
8589
8590             })
8591         }
8592         
8593         if(this.multiple){
8594             combobox.cls += ' select2-container-multi';
8595         }
8596         
8597         if (align ==='left' && this.fieldLabel.length) {
8598             
8599                 Roo.log("left and has label");
8600                 cfg.cn = [
8601                     
8602                     {
8603                         tag: 'label',
8604                         'for' :  id,
8605                         cls : 'control-label col-sm-' + this.labelWidth,
8606                         html : this.fieldLabel
8607                         
8608                     },
8609                     {
8610                         cls : "col-sm-" + (12 - this.labelWidth), 
8611                         cn: [
8612                             combobox
8613                         ]
8614                     }
8615                     
8616                 ];
8617         } else if ( this.fieldLabel.length) {
8618                 Roo.log(" label");
8619                  cfg.cn = [
8620                    
8621                     {
8622                         tag: 'label',
8623                         //cls : 'input-group-addon',
8624                         html : this.fieldLabel
8625                         
8626                     },
8627                     
8628                     combobox
8629                     
8630                 ];
8631
8632         } else {
8633             
8634                 Roo.log(" no label && no align");
8635                 cfg = combobox
8636                      
8637                 
8638         }
8639          
8640         var settings=this;
8641         ['xs','sm','md','lg'].map(function(size){
8642             if (settings[size]) {
8643                 cfg.cls += ' col-' + size + '-' + settings[size];
8644             }
8645         });
8646         
8647         return cfg;
8648         
8649     },
8650     
8651     
8652     
8653     // private
8654     onResize : function(w, h){
8655 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8656 //        if(typeof w == 'number'){
8657 //            var x = w - this.trigger.getWidth();
8658 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8659 //            this.trigger.setStyle('left', x+'px');
8660 //        }
8661     },
8662
8663     // private
8664     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8665
8666     // private
8667     getResizeEl : function(){
8668         return this.inputEl();
8669     },
8670
8671     // private
8672     getPositionEl : function(){
8673         return this.inputEl();
8674     },
8675
8676     // private
8677     alignErrorIcon : function(){
8678         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8679     },
8680
8681     // private
8682     initEvents : function(){
8683         
8684         this.createList();
8685         
8686         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8687         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8688         if(!this.multiple && this.showToggleBtn){
8689             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8690             if(this.hideTrigger){
8691                 this.trigger.setDisplayed(false);
8692             }
8693             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8694         }
8695         
8696         if(this.multiple){
8697             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8698         }
8699         
8700         //this.trigger.addClassOnOver('x-form-trigger-over');
8701         //this.trigger.addClassOnClick('x-form-trigger-click');
8702         
8703         //if(!this.width){
8704         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8705         //}
8706     },
8707     
8708     createList : function()
8709     {
8710         this.list = Roo.get(document.body).createChild({
8711             tag: 'ul',
8712             cls: 'typeahead typeahead-long dropdown-menu',
8713             style: 'display:none'
8714         });
8715         
8716         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8717         
8718     },
8719
8720     // private
8721     initTrigger : function(){
8722        
8723     },
8724
8725     // private
8726     onDestroy : function(){
8727         if(this.trigger){
8728             this.trigger.removeAllListeners();
8729           //  this.trigger.remove();
8730         }
8731         //if(this.wrap){
8732         //    this.wrap.remove();
8733         //}
8734         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8735     },
8736
8737     // private
8738     onFocus : function(){
8739         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8740         /*
8741         if(!this.mimicing){
8742             this.wrap.addClass('x-trigger-wrap-focus');
8743             this.mimicing = true;
8744             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8745             if(this.monitorTab){
8746                 this.el.on("keydown", this.checkTab, this);
8747             }
8748         }
8749         */
8750     },
8751
8752     // private
8753     checkTab : function(e){
8754         if(e.getKey() == e.TAB){
8755             this.triggerBlur();
8756         }
8757     },
8758
8759     // private
8760     onBlur : function(){
8761         // do nothing
8762     },
8763
8764     // private
8765     mimicBlur : function(e, t){
8766         /*
8767         if(!this.wrap.contains(t) && this.validateBlur()){
8768             this.triggerBlur();
8769         }
8770         */
8771     },
8772
8773     // private
8774     triggerBlur : function(){
8775         this.mimicing = false;
8776         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8777         if(this.monitorTab){
8778             this.el.un("keydown", this.checkTab, this);
8779         }
8780         //this.wrap.removeClass('x-trigger-wrap-focus');
8781         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8782     },
8783
8784     // private
8785     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8786     validateBlur : function(e, t){
8787         return true;
8788     },
8789
8790     // private
8791     onDisable : function(){
8792         this.inputEl().dom.disabled = true;
8793         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8794         //if(this.wrap){
8795         //    this.wrap.addClass('x-item-disabled');
8796         //}
8797     },
8798
8799     // private
8800     onEnable : function(){
8801         this.inputEl().dom.disabled = false;
8802         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8803         //if(this.wrap){
8804         //    this.el.removeClass('x-item-disabled');
8805         //}
8806     },
8807
8808     // private
8809     onShow : function(){
8810         var ae = this.getActionEl();
8811         
8812         if(ae){
8813             ae.dom.style.display = '';
8814             ae.dom.style.visibility = 'visible';
8815         }
8816     },
8817
8818     // private
8819     
8820     onHide : function(){
8821         var ae = this.getActionEl();
8822         ae.dom.style.display = 'none';
8823     },
8824
8825     /**
8826      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8827      * by an implementing function.
8828      * @method
8829      * @param {EventObject} e
8830      */
8831     onTriggerClick : Roo.emptyFn
8832 });
8833  /*
8834  * Based on:
8835  * Ext JS Library 1.1.1
8836  * Copyright(c) 2006-2007, Ext JS, LLC.
8837  *
8838  * Originally Released Under LGPL - original licence link has changed is not relivant.
8839  *
8840  * Fork - LGPL
8841  * <script type="text/javascript">
8842  */
8843
8844
8845 /**
8846  * @class Roo.data.SortTypes
8847  * @singleton
8848  * Defines the default sorting (casting?) comparison functions used when sorting data.
8849  */
8850 Roo.data.SortTypes = {
8851     /**
8852      * Default sort that does nothing
8853      * @param {Mixed} s The value being converted
8854      * @return {Mixed} The comparison value
8855      */
8856     none : function(s){
8857         return s;
8858     },
8859     
8860     /**
8861      * The regular expression used to strip tags
8862      * @type {RegExp}
8863      * @property
8864      */
8865     stripTagsRE : /<\/?[^>]+>/gi,
8866     
8867     /**
8868      * Strips all HTML tags to sort on text only
8869      * @param {Mixed} s The value being converted
8870      * @return {String} The comparison value
8871      */
8872     asText : function(s){
8873         return String(s).replace(this.stripTagsRE, "");
8874     },
8875     
8876     /**
8877      * Strips all HTML tags to sort on text only - Case insensitive
8878      * @param {Mixed} s The value being converted
8879      * @return {String} The comparison value
8880      */
8881     asUCText : function(s){
8882         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8883     },
8884     
8885     /**
8886      * Case insensitive string
8887      * @param {Mixed} s The value being converted
8888      * @return {String} The comparison value
8889      */
8890     asUCString : function(s) {
8891         return String(s).toUpperCase();
8892     },
8893     
8894     /**
8895      * Date sorting
8896      * @param {Mixed} s The value being converted
8897      * @return {Number} The comparison value
8898      */
8899     asDate : function(s) {
8900         if(!s){
8901             return 0;
8902         }
8903         if(s instanceof Date){
8904             return s.getTime();
8905         }
8906         return Date.parse(String(s));
8907     },
8908     
8909     /**
8910      * Float sorting
8911      * @param {Mixed} s The value being converted
8912      * @return {Float} The comparison value
8913      */
8914     asFloat : function(s) {
8915         var val = parseFloat(String(s).replace(/,/g, ""));
8916         if(isNaN(val)) val = 0;
8917         return val;
8918     },
8919     
8920     /**
8921      * Integer sorting
8922      * @param {Mixed} s The value being converted
8923      * @return {Number} The comparison value
8924      */
8925     asInt : function(s) {
8926         var val = parseInt(String(s).replace(/,/g, ""));
8927         if(isNaN(val)) val = 0;
8928         return val;
8929     }
8930 };/*
8931  * Based on:
8932  * Ext JS Library 1.1.1
8933  * Copyright(c) 2006-2007, Ext JS, LLC.
8934  *
8935  * Originally Released Under LGPL - original licence link has changed is not relivant.
8936  *
8937  * Fork - LGPL
8938  * <script type="text/javascript">
8939  */
8940
8941 /**
8942 * @class Roo.data.Record
8943  * Instances of this class encapsulate both record <em>definition</em> information, and record
8944  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8945  * to access Records cached in an {@link Roo.data.Store} object.<br>
8946  * <p>
8947  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8948  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8949  * objects.<br>
8950  * <p>
8951  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8952  * @constructor
8953  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8954  * {@link #create}. The parameters are the same.
8955  * @param {Array} data An associative Array of data values keyed by the field name.
8956  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8957  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8958  * not specified an integer id is generated.
8959  */
8960 Roo.data.Record = function(data, id){
8961     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8962     this.data = data;
8963 };
8964
8965 /**
8966  * Generate a constructor for a specific record layout.
8967  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8968  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8969  * Each field definition object may contain the following properties: <ul>
8970  * <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,
8971  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8972  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8973  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8974  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8975  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8976  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8977  * this may be omitted.</p></li>
8978  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8979  * <ul><li>auto (Default, implies no conversion)</li>
8980  * <li>string</li>
8981  * <li>int</li>
8982  * <li>float</li>
8983  * <li>boolean</li>
8984  * <li>date</li></ul></p></li>
8985  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8986  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8987  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8988  * by the Reader into an object that will be stored in the Record. It is passed the
8989  * following parameters:<ul>
8990  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8991  * </ul></p></li>
8992  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8993  * </ul>
8994  * <br>usage:<br><pre><code>
8995 var TopicRecord = Roo.data.Record.create(
8996     {name: 'title', mapping: 'topic_title'},
8997     {name: 'author', mapping: 'username'},
8998     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8999     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9000     {name: 'lastPoster', mapping: 'user2'},
9001     {name: 'excerpt', mapping: 'post_text'}
9002 );
9003
9004 var myNewRecord = new TopicRecord({
9005     title: 'Do my job please',
9006     author: 'noobie',
9007     totalPosts: 1,
9008     lastPost: new Date(),
9009     lastPoster: 'Animal',
9010     excerpt: 'No way dude!'
9011 });
9012 myStore.add(myNewRecord);
9013 </code></pre>
9014  * @method create
9015  * @static
9016  */
9017 Roo.data.Record.create = function(o){
9018     var f = function(){
9019         f.superclass.constructor.apply(this, arguments);
9020     };
9021     Roo.extend(f, Roo.data.Record);
9022     var p = f.prototype;
9023     p.fields = new Roo.util.MixedCollection(false, function(field){
9024         return field.name;
9025     });
9026     for(var i = 0, len = o.length; i < len; i++){
9027         p.fields.add(new Roo.data.Field(o[i]));
9028     }
9029     f.getField = function(name){
9030         return p.fields.get(name);  
9031     };
9032     return f;
9033 };
9034
9035 Roo.data.Record.AUTO_ID = 1000;
9036 Roo.data.Record.EDIT = 'edit';
9037 Roo.data.Record.REJECT = 'reject';
9038 Roo.data.Record.COMMIT = 'commit';
9039
9040 Roo.data.Record.prototype = {
9041     /**
9042      * Readonly flag - true if this record has been modified.
9043      * @type Boolean
9044      */
9045     dirty : false,
9046     editing : false,
9047     error: null,
9048     modified: null,
9049
9050     // private
9051     join : function(store){
9052         this.store = store;
9053     },
9054
9055     /**
9056      * Set the named field to the specified value.
9057      * @param {String} name The name of the field to set.
9058      * @param {Object} value The value to set the field to.
9059      */
9060     set : function(name, value){
9061         if(this.data[name] == value){
9062             return;
9063         }
9064         this.dirty = true;
9065         if(!this.modified){
9066             this.modified = {};
9067         }
9068         if(typeof this.modified[name] == 'undefined'){
9069             this.modified[name] = this.data[name];
9070         }
9071         this.data[name] = value;
9072         if(!this.editing && this.store){
9073             this.store.afterEdit(this);
9074         }       
9075     },
9076
9077     /**
9078      * Get the value of the named field.
9079      * @param {String} name The name of the field to get the value of.
9080      * @return {Object} The value of the field.
9081      */
9082     get : function(name){
9083         return this.data[name]; 
9084     },
9085
9086     // private
9087     beginEdit : function(){
9088         this.editing = true;
9089         this.modified = {}; 
9090     },
9091
9092     // private
9093     cancelEdit : function(){
9094         this.editing = false;
9095         delete this.modified;
9096     },
9097
9098     // private
9099     endEdit : function(){
9100         this.editing = false;
9101         if(this.dirty && this.store){
9102             this.store.afterEdit(this);
9103         }
9104     },
9105
9106     /**
9107      * Usually called by the {@link Roo.data.Store} which owns the Record.
9108      * Rejects all changes made to the Record since either creation, or the last commit operation.
9109      * Modified fields are reverted to their original values.
9110      * <p>
9111      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9112      * of reject operations.
9113      */
9114     reject : function(){
9115         var m = this.modified;
9116         for(var n in m){
9117             if(typeof m[n] != "function"){
9118                 this.data[n] = m[n];
9119             }
9120         }
9121         this.dirty = false;
9122         delete this.modified;
9123         this.editing = false;
9124         if(this.store){
9125             this.store.afterReject(this);
9126         }
9127     },
9128
9129     /**
9130      * Usually called by the {@link Roo.data.Store} which owns the Record.
9131      * Commits all changes made to the Record since either creation, or the last commit operation.
9132      * <p>
9133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9134      * of commit operations.
9135      */
9136     commit : function(){
9137         this.dirty = false;
9138         delete this.modified;
9139         this.editing = false;
9140         if(this.store){
9141             this.store.afterCommit(this);
9142         }
9143     },
9144
9145     // private
9146     hasError : function(){
9147         return this.error != null;
9148     },
9149
9150     // private
9151     clearError : function(){
9152         this.error = null;
9153     },
9154
9155     /**
9156      * Creates a copy of this record.
9157      * @param {String} id (optional) A new record id if you don't want to use this record's id
9158      * @return {Record}
9159      */
9160     copy : function(newId) {
9161         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9162     }
9163 };/*
9164  * Based on:
9165  * Ext JS Library 1.1.1
9166  * Copyright(c) 2006-2007, Ext JS, LLC.
9167  *
9168  * Originally Released Under LGPL - original licence link has changed is not relivant.
9169  *
9170  * Fork - LGPL
9171  * <script type="text/javascript">
9172  */
9173
9174
9175
9176 /**
9177  * @class Roo.data.Store
9178  * @extends Roo.util.Observable
9179  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9180  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9181  * <p>
9182  * 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
9183  * has no knowledge of the format of the data returned by the Proxy.<br>
9184  * <p>
9185  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9186  * instances from the data object. These records are cached and made available through accessor functions.
9187  * @constructor
9188  * Creates a new Store.
9189  * @param {Object} config A config object containing the objects needed for the Store to access data,
9190  * and read the data into Records.
9191  */
9192 Roo.data.Store = function(config){
9193     this.data = new Roo.util.MixedCollection(false);
9194     this.data.getKey = function(o){
9195         return o.id;
9196     };
9197     this.baseParams = {};
9198     // private
9199     this.paramNames = {
9200         "start" : "start",
9201         "limit" : "limit",
9202         "sort" : "sort",
9203         "dir" : "dir",
9204         "multisort" : "_multisort"
9205     };
9206
9207     if(config && config.data){
9208         this.inlineData = config.data;
9209         delete config.data;
9210     }
9211
9212     Roo.apply(this, config);
9213     
9214     if(this.reader){ // reader passed
9215         this.reader = Roo.factory(this.reader, Roo.data);
9216         this.reader.xmodule = this.xmodule || false;
9217         if(!this.recordType){
9218             this.recordType = this.reader.recordType;
9219         }
9220         if(this.reader.onMetaChange){
9221             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9222         }
9223     }
9224
9225     if(this.recordType){
9226         this.fields = this.recordType.prototype.fields;
9227     }
9228     this.modified = [];
9229
9230     this.addEvents({
9231         /**
9232          * @event datachanged
9233          * Fires when the data cache has changed, and a widget which is using this Store
9234          * as a Record cache should refresh its view.
9235          * @param {Store} this
9236          */
9237         datachanged : true,
9238         /**
9239          * @event metachange
9240          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9241          * @param {Store} this
9242          * @param {Object} meta The JSON metadata
9243          */
9244         metachange : true,
9245         /**
9246          * @event add
9247          * Fires when Records have been added to the Store
9248          * @param {Store} this
9249          * @param {Roo.data.Record[]} records The array of Records added
9250          * @param {Number} index The index at which the record(s) were added
9251          */
9252         add : true,
9253         /**
9254          * @event remove
9255          * Fires when a Record has been removed from the Store
9256          * @param {Store} this
9257          * @param {Roo.data.Record} record The Record that was removed
9258          * @param {Number} index The index at which the record was removed
9259          */
9260         remove : true,
9261         /**
9262          * @event update
9263          * Fires when a Record has been updated
9264          * @param {Store} this
9265          * @param {Roo.data.Record} record The Record that was updated
9266          * @param {String} operation The update operation being performed.  Value may be one of:
9267          * <pre><code>
9268  Roo.data.Record.EDIT
9269  Roo.data.Record.REJECT
9270  Roo.data.Record.COMMIT
9271          * </code></pre>
9272          */
9273         update : true,
9274         /**
9275          * @event clear
9276          * Fires when the data cache has been cleared.
9277          * @param {Store} this
9278          */
9279         clear : true,
9280         /**
9281          * @event beforeload
9282          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9283          * the load action will be canceled.
9284          * @param {Store} this
9285          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9286          */
9287         beforeload : true,
9288         /**
9289          * @event beforeloadadd
9290          * Fires after a new set of Records has been loaded.
9291          * @param {Store} this
9292          * @param {Roo.data.Record[]} records The Records that were loaded
9293          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9294          */
9295         beforeloadadd : true,
9296         /**
9297          * @event load
9298          * Fires after a new set of Records has been loaded, before they are added to the store.
9299          * @param {Store} this
9300          * @param {Roo.data.Record[]} records The Records that were loaded
9301          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9302          * @params {Object} return from reader
9303          */
9304         load : true,
9305         /**
9306          * @event loadexception
9307          * Fires if an exception occurs in the Proxy during loading.
9308          * Called with the signature of the Proxy's "loadexception" event.
9309          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9310          * 
9311          * @param {Proxy} 
9312          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9313          * @param {Object} load options 
9314          * @param {Object} jsonData from your request (normally this contains the Exception)
9315          */
9316         loadexception : true
9317     });
9318     
9319     if(this.proxy){
9320         this.proxy = Roo.factory(this.proxy, Roo.data);
9321         this.proxy.xmodule = this.xmodule || false;
9322         this.relayEvents(this.proxy,  ["loadexception"]);
9323     }
9324     this.sortToggle = {};
9325     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9326
9327     Roo.data.Store.superclass.constructor.call(this);
9328
9329     if(this.inlineData){
9330         this.loadData(this.inlineData);
9331         delete this.inlineData;
9332     }
9333 };
9334
9335 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9336      /**
9337     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9338     * without a remote query - used by combo/forms at present.
9339     */
9340     
9341     /**
9342     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9343     */
9344     /**
9345     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9346     */
9347     /**
9348     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9349     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9350     */
9351     /**
9352     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9353     * on any HTTP request
9354     */
9355     /**
9356     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9357     */
9358     /**
9359     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9360     */
9361     multiSort: false,
9362     /**
9363     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9364     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9365     */
9366     remoteSort : false,
9367
9368     /**
9369     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9370      * loaded or when a record is removed. (defaults to false).
9371     */
9372     pruneModifiedRecords : false,
9373
9374     // private
9375     lastOptions : null,
9376
9377     /**
9378      * Add Records to the Store and fires the add event.
9379      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9380      */
9381     add : function(records){
9382         records = [].concat(records);
9383         for(var i = 0, len = records.length; i < len; i++){
9384             records[i].join(this);
9385         }
9386         var index = this.data.length;
9387         this.data.addAll(records);
9388         this.fireEvent("add", this, records, index);
9389     },
9390
9391     /**
9392      * Remove a Record from the Store and fires the remove event.
9393      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9394      */
9395     remove : function(record){
9396         var index = this.data.indexOf(record);
9397         this.data.removeAt(index);
9398         if(this.pruneModifiedRecords){
9399             this.modified.remove(record);
9400         }
9401         this.fireEvent("remove", this, record, index);
9402     },
9403
9404     /**
9405      * Remove all Records from the Store and fires the clear event.
9406      */
9407     removeAll : function(){
9408         this.data.clear();
9409         if(this.pruneModifiedRecords){
9410             this.modified = [];
9411         }
9412         this.fireEvent("clear", this);
9413     },
9414
9415     /**
9416      * Inserts Records to the Store at the given index and fires the add event.
9417      * @param {Number} index The start index at which to insert the passed Records.
9418      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9419      */
9420     insert : function(index, records){
9421         records = [].concat(records);
9422         for(var i = 0, len = records.length; i < len; i++){
9423             this.data.insert(index, records[i]);
9424             records[i].join(this);
9425         }
9426         this.fireEvent("add", this, records, index);
9427     },
9428
9429     /**
9430      * Get the index within the cache of the passed Record.
9431      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9432      * @return {Number} The index of the passed Record. Returns -1 if not found.
9433      */
9434     indexOf : function(record){
9435         return this.data.indexOf(record);
9436     },
9437
9438     /**
9439      * Get the index within the cache of the Record with the passed id.
9440      * @param {String} id The id of the Record to find.
9441      * @return {Number} The index of the Record. Returns -1 if not found.
9442      */
9443     indexOfId : function(id){
9444         return this.data.indexOfKey(id);
9445     },
9446
9447     /**
9448      * Get the Record with the specified id.
9449      * @param {String} id The id of the Record to find.
9450      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9451      */
9452     getById : function(id){
9453         return this.data.key(id);
9454     },
9455
9456     /**
9457      * Get the Record at the specified index.
9458      * @param {Number} index The index of the Record to find.
9459      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9460      */
9461     getAt : function(index){
9462         return this.data.itemAt(index);
9463     },
9464
9465     /**
9466      * Returns a range of Records between specified indices.
9467      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9468      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9469      * @return {Roo.data.Record[]} An array of Records
9470      */
9471     getRange : function(start, end){
9472         return this.data.getRange(start, end);
9473     },
9474
9475     // private
9476     storeOptions : function(o){
9477         o = Roo.apply({}, o);
9478         delete o.callback;
9479         delete o.scope;
9480         this.lastOptions = o;
9481     },
9482
9483     /**
9484      * Loads the Record cache from the configured Proxy using the configured Reader.
9485      * <p>
9486      * If using remote paging, then the first load call must specify the <em>start</em>
9487      * and <em>limit</em> properties in the options.params property to establish the initial
9488      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9489      * <p>
9490      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9491      * and this call will return before the new data has been loaded. Perform any post-processing
9492      * in a callback function, or in a "load" event handler.</strong>
9493      * <p>
9494      * @param {Object} options An object containing properties which control loading options:<ul>
9495      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9496      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9497      * passed the following arguments:<ul>
9498      * <li>r : Roo.data.Record[]</li>
9499      * <li>options: Options object from the load call</li>
9500      * <li>success: Boolean success indicator</li></ul></li>
9501      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9502      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9503      * </ul>
9504      */
9505     load : function(options){
9506         options = options || {};
9507         if(this.fireEvent("beforeload", this, options) !== false){
9508             this.storeOptions(options);
9509             var p = Roo.apply(options.params || {}, this.baseParams);
9510             // if meta was not loaded from remote source.. try requesting it.
9511             if (!this.reader.metaFromRemote) {
9512                 p._requestMeta = 1;
9513             }
9514             if(this.sortInfo && this.remoteSort){
9515                 var pn = this.paramNames;
9516                 p[pn["sort"]] = this.sortInfo.field;
9517                 p[pn["dir"]] = this.sortInfo.direction;
9518             }
9519             if (this.multiSort) {
9520                 var pn = this.paramNames;
9521                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9522             }
9523             
9524             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9525         }
9526     },
9527
9528     /**
9529      * Reloads the Record cache from the configured Proxy using the configured Reader and
9530      * the options from the last load operation performed.
9531      * @param {Object} options (optional) An object containing properties which may override the options
9532      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9533      * the most recently used options are reused).
9534      */
9535     reload : function(options){
9536         this.load(Roo.applyIf(options||{}, this.lastOptions));
9537     },
9538
9539     // private
9540     // Called as a callback by the Reader during a load operation.
9541     loadRecords : function(o, options, success){
9542         if(!o || success === false){
9543             if(success !== false){
9544                 this.fireEvent("load", this, [], options, o);
9545             }
9546             if(options.callback){
9547                 options.callback.call(options.scope || this, [], options, false);
9548             }
9549             return;
9550         }
9551         // if data returned failure - throw an exception.
9552         if (o.success === false) {
9553             // show a message if no listener is registered.
9554             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9555                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9556             }
9557             // loadmask wil be hooked into this..
9558             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9559             return;
9560         }
9561         var r = o.records, t = o.totalRecords || r.length;
9562         
9563         this.fireEvent("beforeloadadd", this, r, options, o);
9564         
9565         if(!options || options.add !== true){
9566             if(this.pruneModifiedRecords){
9567                 this.modified = [];
9568             }
9569             for(var i = 0, len = r.length; i < len; i++){
9570                 r[i].join(this);
9571             }
9572             if(this.snapshot){
9573                 this.data = this.snapshot;
9574                 delete this.snapshot;
9575             }
9576             this.data.clear();
9577             this.data.addAll(r);
9578             this.totalLength = t;
9579             this.applySort();
9580             this.fireEvent("datachanged", this);
9581         }else{
9582             this.totalLength = Math.max(t, this.data.length+r.length);
9583             this.add(r);
9584         }
9585         this.fireEvent("load", this, r, options, o);
9586         if(options.callback){
9587             options.callback.call(options.scope || this, r, options, true);
9588         }
9589     },
9590
9591
9592     /**
9593      * Loads data from a passed data block. A Reader which understands the format of the data
9594      * must have been configured in the constructor.
9595      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9596      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9597      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9598      */
9599     loadData : function(o, append){
9600         var r = this.reader.readRecords(o);
9601         this.loadRecords(r, {add: append}, true);
9602     },
9603
9604     /**
9605      * Gets the number of cached records.
9606      * <p>
9607      * <em>If using paging, this may not be the total size of the dataset. If the data object
9608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9609      * the data set size</em>
9610      */
9611     getCount : function(){
9612         return this.data.length || 0;
9613     },
9614
9615     /**
9616      * Gets the total number of records in the dataset as returned by the server.
9617      * <p>
9618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9619      * the dataset size</em>
9620      */
9621     getTotalCount : function(){
9622         return this.totalLength || 0;
9623     },
9624
9625     /**
9626      * Returns the sort state of the Store as an object with two properties:
9627      * <pre><code>
9628  field {String} The name of the field by which the Records are sorted
9629  direction {String} The sort order, "ASC" or "DESC"
9630      * </code></pre>
9631      */
9632     getSortState : function(){
9633         return this.sortInfo;
9634     },
9635
9636     // private
9637     applySort : function(){
9638         if(this.sortInfo && !this.remoteSort){
9639             var s = this.sortInfo, f = s.field;
9640             var st = this.fields.get(f).sortType;
9641             var fn = function(r1, r2){
9642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9644             };
9645             this.data.sort(s.direction, fn);
9646             if(this.snapshot && this.snapshot != this.data){
9647                 this.snapshot.sort(s.direction, fn);
9648             }
9649         }
9650     },
9651
9652     /**
9653      * Sets the default sort column and order to be used by the next load operation.
9654      * @param {String} fieldName The name of the field to sort by.
9655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9656      */
9657     setDefaultSort : function(field, dir){
9658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9659     },
9660
9661     /**
9662      * Sort the Records.
9663      * If remote sorting is used, the sort is performed on the server, and the cache is
9664      * reloaded. If local sorting is used, the cache is sorted internally.
9665      * @param {String} fieldName The name of the field to sort by.
9666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9667      */
9668     sort : function(fieldName, dir){
9669         var f = this.fields.get(fieldName);
9670         if(!dir){
9671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9672             
9673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9675             }else{
9676                 dir = f.sortDir;
9677             }
9678         }
9679         this.sortToggle[f.name] = dir;
9680         this.sortInfo = {field: f.name, direction: dir};
9681         if(!this.remoteSort){
9682             this.applySort();
9683             this.fireEvent("datachanged", this);
9684         }else{
9685             this.load(this.lastOptions);
9686         }
9687     },
9688
9689     /**
9690      * Calls the specified function for each of the Records in the cache.
9691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9692      * Returning <em>false</em> aborts and exits the iteration.
9693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9694      */
9695     each : function(fn, scope){
9696         this.data.each(fn, scope);
9697     },
9698
9699     /**
9700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9701      * (e.g., during paging).
9702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9703      */
9704     getModifiedRecords : function(){
9705         return this.modified;
9706     },
9707
9708     // private
9709     createFilterFn : function(property, value, anyMatch){
9710         if(!value.exec){ // not a regex
9711             value = String(value);
9712             if(value.length == 0){
9713                 return false;
9714             }
9715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9716         }
9717         return function(r){
9718             return value.test(r.data[property]);
9719         };
9720     },
9721
9722     /**
9723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9724      * @param {String} property A field on your records
9725      * @param {Number} start The record index to start at (defaults to 0)
9726      * @param {Number} end The last record index to include (defaults to length - 1)
9727      * @return {Number} The sum
9728      */
9729     sum : function(property, start, end){
9730         var rs = this.data.items, v = 0;
9731         start = start || 0;
9732         end = (end || end === 0) ? end : rs.length-1;
9733
9734         for(var i = start; i <= end; i++){
9735             v += (rs[i].data[property] || 0);
9736         }
9737         return v;
9738     },
9739
9740     /**
9741      * Filter the records by a specified property.
9742      * @param {String} field A field on your records
9743      * @param {String/RegExp} value Either a string that the field
9744      * should start with or a RegExp to test against the field
9745      * @param {Boolean} anyMatch True to match any part not just the beginning
9746      */
9747     filter : function(property, value, anyMatch){
9748         var fn = this.createFilterFn(property, value, anyMatch);
9749         return fn ? this.filterBy(fn) : this.clearFilter();
9750     },
9751
9752     /**
9753      * Filter by a function. The specified function will be called with each
9754      * record in this data source. If the function returns true the record is included,
9755      * otherwise it is filtered.
9756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9757      * @param {Object} scope (optional) The scope of the function (defaults to this)
9758      */
9759     filterBy : function(fn, scope){
9760         this.snapshot = this.snapshot || this.data;
9761         this.data = this.queryBy(fn, scope||this);
9762         this.fireEvent("datachanged", this);
9763     },
9764
9765     /**
9766      * Query the records by a specified property.
9767      * @param {String} field A field on your records
9768      * @param {String/RegExp} value Either a string that the field
9769      * should start with or a RegExp to test against the field
9770      * @param {Boolean} anyMatch True to match any part not just the beginning
9771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9772      */
9773     query : function(property, value, anyMatch){
9774         var fn = this.createFilterFn(property, value, anyMatch);
9775         return fn ? this.queryBy(fn) : this.data.clone();
9776     },
9777
9778     /**
9779      * Query by a function. The specified function will be called with each
9780      * record in this data source. If the function returns true the record is included
9781      * in the results.
9782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9783      * @param {Object} scope (optional) The scope of the function (defaults to this)
9784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9785      **/
9786     queryBy : function(fn, scope){
9787         var data = this.snapshot || this.data;
9788         return data.filterBy(fn, scope||this);
9789     },
9790
9791     /**
9792      * Collects unique values for a particular dataIndex from this store.
9793      * @param {String} dataIndex The property to collect
9794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9796      * @return {Array} An array of the unique values
9797      **/
9798     collect : function(dataIndex, allowNull, bypassFilter){
9799         var d = (bypassFilter === true && this.snapshot) ?
9800                 this.snapshot.items : this.data.items;
9801         var v, sv, r = [], l = {};
9802         for(var i = 0, len = d.length; i < len; i++){
9803             v = d[i].data[dataIndex];
9804             sv = String(v);
9805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9806                 l[sv] = true;
9807                 r[r.length] = v;
9808             }
9809         }
9810         return r;
9811     },
9812
9813     /**
9814      * Revert to a view of the Record cache with no filtering applied.
9815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9816      */
9817     clearFilter : function(suppressEvent){
9818         if(this.snapshot && this.snapshot != this.data){
9819             this.data = this.snapshot;
9820             delete this.snapshot;
9821             if(suppressEvent !== true){
9822                 this.fireEvent("datachanged", this);
9823             }
9824         }
9825     },
9826
9827     // private
9828     afterEdit : function(record){
9829         if(this.modified.indexOf(record) == -1){
9830             this.modified.push(record);
9831         }
9832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9833     },
9834     
9835     // private
9836     afterReject : function(record){
9837         this.modified.remove(record);
9838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9839     },
9840
9841     // private
9842     afterCommit : function(record){
9843         this.modified.remove(record);
9844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9845     },
9846
9847     /**
9848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9850      */
9851     commitChanges : function(){
9852         var m = this.modified.slice(0);
9853         this.modified = [];
9854         for(var i = 0, len = m.length; i < len; i++){
9855             m[i].commit();
9856         }
9857     },
9858
9859     /**
9860      * Cancel outstanding changes on all changed records.
9861      */
9862     rejectChanges : function(){
9863         var m = this.modified.slice(0);
9864         this.modified = [];
9865         for(var i = 0, len = m.length; i < len; i++){
9866             m[i].reject();
9867         }
9868     },
9869
9870     onMetaChange : function(meta, rtype, o){
9871         this.recordType = rtype;
9872         this.fields = rtype.prototype.fields;
9873         delete this.snapshot;
9874         this.sortInfo = meta.sortInfo || this.sortInfo;
9875         this.modified = [];
9876         this.fireEvent('metachange', this, this.reader.meta);
9877     },
9878     
9879     moveIndex : function(data, type)
9880     {
9881         var index = this.indexOf(data);
9882         
9883         var newIndex = index + type;
9884         
9885         this.remove(data);
9886         
9887         this.insert(newIndex, data);
9888         
9889     }
9890 });/*
9891  * Based on:
9892  * Ext JS Library 1.1.1
9893  * Copyright(c) 2006-2007, Ext JS, LLC.
9894  *
9895  * Originally Released Under LGPL - original licence link has changed is not relivant.
9896  *
9897  * Fork - LGPL
9898  * <script type="text/javascript">
9899  */
9900
9901 /**
9902  * @class Roo.data.SimpleStore
9903  * @extends Roo.data.Store
9904  * Small helper class to make creating Stores from Array data easier.
9905  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9906  * @cfg {Array} fields An array of field definition objects, or field name strings.
9907  * @cfg {Array} data The multi-dimensional array of data
9908  * @constructor
9909  * @param {Object} config
9910  */
9911 Roo.data.SimpleStore = function(config){
9912     Roo.data.SimpleStore.superclass.constructor.call(this, {
9913         isLocal : true,
9914         reader: new Roo.data.ArrayReader({
9915                 id: config.id
9916             },
9917             Roo.data.Record.create(config.fields)
9918         ),
9919         proxy : new Roo.data.MemoryProxy(config.data)
9920     });
9921     this.load();
9922 };
9923 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9924  * Based on:
9925  * Ext JS Library 1.1.1
9926  * Copyright(c) 2006-2007, Ext JS, LLC.
9927  *
9928  * Originally Released Under LGPL - original licence link has changed is not relivant.
9929  *
9930  * Fork - LGPL
9931  * <script type="text/javascript">
9932  */
9933
9934 /**
9935 /**
9936  * @extends Roo.data.Store
9937  * @class Roo.data.JsonStore
9938  * Small helper class to make creating Stores for JSON data easier. <br/>
9939 <pre><code>
9940 var store = new Roo.data.JsonStore({
9941     url: 'get-images.php',
9942     root: 'images',
9943     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9944 });
9945 </code></pre>
9946  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9947  * JsonReader and HttpProxy (unless inline data is provided).</b>
9948  * @cfg {Array} fields An array of field definition objects, or field name strings.
9949  * @constructor
9950  * @param {Object} config
9951  */
9952 Roo.data.JsonStore = function(c){
9953     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9954         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9955         reader: new Roo.data.JsonReader(c, c.fields)
9956     }));
9957 };
9958 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9959  * Based on:
9960  * Ext JS Library 1.1.1
9961  * Copyright(c) 2006-2007, Ext JS, LLC.
9962  *
9963  * Originally Released Under LGPL - original licence link has changed is not relivant.
9964  *
9965  * Fork - LGPL
9966  * <script type="text/javascript">
9967  */
9968
9969  
9970 Roo.data.Field = function(config){
9971     if(typeof config == "string"){
9972         config = {name: config};
9973     }
9974     Roo.apply(this, config);
9975     
9976     if(!this.type){
9977         this.type = "auto";
9978     }
9979     
9980     var st = Roo.data.SortTypes;
9981     // named sortTypes are supported, here we look them up
9982     if(typeof this.sortType == "string"){
9983         this.sortType = st[this.sortType];
9984     }
9985     
9986     // set default sortType for strings and dates
9987     if(!this.sortType){
9988         switch(this.type){
9989             case "string":
9990                 this.sortType = st.asUCString;
9991                 break;
9992             case "date":
9993                 this.sortType = st.asDate;
9994                 break;
9995             default:
9996                 this.sortType = st.none;
9997         }
9998     }
9999
10000     // define once
10001     var stripRe = /[\$,%]/g;
10002
10003     // prebuilt conversion function for this field, instead of
10004     // switching every time we're reading a value
10005     if(!this.convert){
10006         var cv, dateFormat = this.dateFormat;
10007         switch(this.type){
10008             case "":
10009             case "auto":
10010             case undefined:
10011                 cv = function(v){ return v; };
10012                 break;
10013             case "string":
10014                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10015                 break;
10016             case "int":
10017                 cv = function(v){
10018                     return v !== undefined && v !== null && v !== '' ?
10019                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10020                     };
10021                 break;
10022             case "float":
10023                 cv = function(v){
10024                     return v !== undefined && v !== null && v !== '' ?
10025                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10026                     };
10027                 break;
10028             case "bool":
10029             case "boolean":
10030                 cv = function(v){ return v === true || v === "true" || v == 1; };
10031                 break;
10032             case "date":
10033                 cv = function(v){
10034                     if(!v){
10035                         return '';
10036                     }
10037                     if(v instanceof Date){
10038                         return v;
10039                     }
10040                     if(dateFormat){
10041                         if(dateFormat == "timestamp"){
10042                             return new Date(v*1000);
10043                         }
10044                         return Date.parseDate(v, dateFormat);
10045                     }
10046                     var parsed = Date.parse(v);
10047                     return parsed ? new Date(parsed) : null;
10048                 };
10049              break;
10050             
10051         }
10052         this.convert = cv;
10053     }
10054 };
10055
10056 Roo.data.Field.prototype = {
10057     dateFormat: null,
10058     defaultValue: "",
10059     mapping: null,
10060     sortType : null,
10061     sortDir : "ASC"
10062 };/*
10063  * Based on:
10064  * Ext JS Library 1.1.1
10065  * Copyright(c) 2006-2007, Ext JS, LLC.
10066  *
10067  * Originally Released Under LGPL - original licence link has changed is not relivant.
10068  *
10069  * Fork - LGPL
10070  * <script type="text/javascript">
10071  */
10072  
10073 // Base class for reading structured data from a data source.  This class is intended to be
10074 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10075
10076 /**
10077  * @class Roo.data.DataReader
10078  * Base class for reading structured data from a data source.  This class is intended to be
10079  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10080  */
10081
10082 Roo.data.DataReader = function(meta, recordType){
10083     
10084     this.meta = meta;
10085     
10086     this.recordType = recordType instanceof Array ? 
10087         Roo.data.Record.create(recordType) : recordType;
10088 };
10089
10090 Roo.data.DataReader.prototype = {
10091      /**
10092      * Create an empty record
10093      * @param {Object} data (optional) - overlay some values
10094      * @return {Roo.data.Record} record created.
10095      */
10096     newRow :  function(d) {
10097         var da =  {};
10098         this.recordType.prototype.fields.each(function(c) {
10099             switch( c.type) {
10100                 case 'int' : da[c.name] = 0; break;
10101                 case 'date' : da[c.name] = new Date(); break;
10102                 case 'float' : da[c.name] = 0.0; break;
10103                 case 'boolean' : da[c.name] = false; break;
10104                 default : da[c.name] = ""; break;
10105             }
10106             
10107         });
10108         return new this.recordType(Roo.apply(da, d));
10109     }
10110     
10111 };/*
10112  * Based on:
10113  * Ext JS Library 1.1.1
10114  * Copyright(c) 2006-2007, Ext JS, LLC.
10115  *
10116  * Originally Released Under LGPL - original licence link has changed is not relivant.
10117  *
10118  * Fork - LGPL
10119  * <script type="text/javascript">
10120  */
10121
10122 /**
10123  * @class Roo.data.DataProxy
10124  * @extends Roo.data.Observable
10125  * This class is an abstract base class for implementations which provide retrieval of
10126  * unformatted data objects.<br>
10127  * <p>
10128  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10129  * (of the appropriate type which knows how to parse the data object) to provide a block of
10130  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10131  * <p>
10132  * Custom implementations must implement the load method as described in
10133  * {@link Roo.data.HttpProxy#load}.
10134  */
10135 Roo.data.DataProxy = function(){
10136     this.addEvents({
10137         /**
10138          * @event beforeload
10139          * Fires before a network request is made to retrieve a data object.
10140          * @param {Object} This DataProxy object.
10141          * @param {Object} params The params parameter to the load function.
10142          */
10143         beforeload : true,
10144         /**
10145          * @event load
10146          * Fires before the load method's callback is called.
10147          * @param {Object} This DataProxy object.
10148          * @param {Object} o The data object.
10149          * @param {Object} arg The callback argument object passed to the load function.
10150          */
10151         load : true,
10152         /**
10153          * @event loadexception
10154          * Fires if an Exception occurs during data retrieval.
10155          * @param {Object} This DataProxy object.
10156          * @param {Object} o The data object.
10157          * @param {Object} arg The callback argument object passed to the load function.
10158          * @param {Object} e The Exception.
10159          */
10160         loadexception : true
10161     });
10162     Roo.data.DataProxy.superclass.constructor.call(this);
10163 };
10164
10165 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10166
10167     /**
10168      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10169      */
10170 /*
10171  * Based on:
10172  * Ext JS Library 1.1.1
10173  * Copyright(c) 2006-2007, Ext JS, LLC.
10174  *
10175  * Originally Released Under LGPL - original licence link has changed is not relivant.
10176  *
10177  * Fork - LGPL
10178  * <script type="text/javascript">
10179  */
10180 /**
10181  * @class Roo.data.MemoryProxy
10182  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10183  * to the Reader when its load method is called.
10184  * @constructor
10185  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10186  */
10187 Roo.data.MemoryProxy = function(data){
10188     if (data.data) {
10189         data = data.data;
10190     }
10191     Roo.data.MemoryProxy.superclass.constructor.call(this);
10192     this.data = data;
10193 };
10194
10195 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10196     /**
10197      * Load data from the requested source (in this case an in-memory
10198      * data object passed to the constructor), read the data object into
10199      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10200      * process that block using the passed callback.
10201      * @param {Object} params This parameter is not used by the MemoryProxy class.
10202      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10203      * object into a block of Roo.data.Records.
10204      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10205      * The function must be passed <ul>
10206      * <li>The Record block object</li>
10207      * <li>The "arg" argument from the load function</li>
10208      * <li>A boolean success indicator</li>
10209      * </ul>
10210      * @param {Object} scope The scope in which to call the callback
10211      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10212      */
10213     load : function(params, reader, callback, scope, arg){
10214         params = params || {};
10215         var result;
10216         try {
10217             result = reader.readRecords(this.data);
10218         }catch(e){
10219             this.fireEvent("loadexception", this, arg, null, e);
10220             callback.call(scope, null, arg, false);
10221             return;
10222         }
10223         callback.call(scope, result, arg, true);
10224     },
10225     
10226     // private
10227     update : function(params, records){
10228         
10229     }
10230 });/*
10231  * Based on:
10232  * Ext JS Library 1.1.1
10233  * Copyright(c) 2006-2007, Ext JS, LLC.
10234  *
10235  * Originally Released Under LGPL - original licence link has changed is not relivant.
10236  *
10237  * Fork - LGPL
10238  * <script type="text/javascript">
10239  */
10240 /**
10241  * @class Roo.data.HttpProxy
10242  * @extends Roo.data.DataProxy
10243  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10244  * configured to reference a certain URL.<br><br>
10245  * <p>
10246  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10247  * from which the running page was served.<br><br>
10248  * <p>
10249  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10250  * <p>
10251  * Be aware that to enable the browser to parse an XML document, the server must set
10252  * the Content-Type header in the HTTP response to "text/xml".
10253  * @constructor
10254  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10255  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10256  * will be used to make the request.
10257  */
10258 Roo.data.HttpProxy = function(conn){
10259     Roo.data.HttpProxy.superclass.constructor.call(this);
10260     // is conn a conn config or a real conn?
10261     this.conn = conn;
10262     this.useAjax = !conn || !conn.events;
10263   
10264 };
10265
10266 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10267     // thse are take from connection...
10268     
10269     /**
10270      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10271      */
10272     /**
10273      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10274      * extra parameters to each request made by this object. (defaults to undefined)
10275      */
10276     /**
10277      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10278      *  to each request made by this object. (defaults to undefined)
10279      */
10280     /**
10281      * @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)
10282      */
10283     /**
10284      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10285      */
10286      /**
10287      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10288      * @type Boolean
10289      */
10290   
10291
10292     /**
10293      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10294      * @type Boolean
10295      */
10296     /**
10297      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10298      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10299      * a finer-grained basis than the DataProxy events.
10300      */
10301     getConnection : function(){
10302         return this.useAjax ? Roo.Ajax : this.conn;
10303     },
10304
10305     /**
10306      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10307      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10308      * process that block using the passed callback.
10309      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10310      * for the request to the remote server.
10311      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10312      * object into a block of Roo.data.Records.
10313      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10314      * The function must be passed <ul>
10315      * <li>The Record block object</li>
10316      * <li>The "arg" argument from the load function</li>
10317      * <li>A boolean success indicator</li>
10318      * </ul>
10319      * @param {Object} scope The scope in which to call the callback
10320      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10321      */
10322     load : function(params, reader, callback, scope, arg){
10323         if(this.fireEvent("beforeload", this, params) !== false){
10324             var  o = {
10325                 params : params || {},
10326                 request: {
10327                     callback : callback,
10328                     scope : scope,
10329                     arg : arg
10330                 },
10331                 reader: reader,
10332                 callback : this.loadResponse,
10333                 scope: this
10334             };
10335             if(this.useAjax){
10336                 Roo.applyIf(o, this.conn);
10337                 if(this.activeRequest){
10338                     Roo.Ajax.abort(this.activeRequest);
10339                 }
10340                 this.activeRequest = Roo.Ajax.request(o);
10341             }else{
10342                 this.conn.request(o);
10343             }
10344         }else{
10345             callback.call(scope||this, null, arg, false);
10346         }
10347     },
10348
10349     // private
10350     loadResponse : function(o, success, response){
10351         delete this.activeRequest;
10352         if(!success){
10353             this.fireEvent("loadexception", this, o, response);
10354             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10355             return;
10356         }
10357         var result;
10358         try {
10359             result = o.reader.read(response);
10360         }catch(e){
10361             this.fireEvent("loadexception", this, o, response, e);
10362             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10363             return;
10364         }
10365         
10366         this.fireEvent("load", this, o, o.request.arg);
10367         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10368     },
10369
10370     // private
10371     update : function(dataSet){
10372
10373     },
10374
10375     // private
10376     updateResponse : function(dataSet){
10377
10378     }
10379 });/*
10380  * Based on:
10381  * Ext JS Library 1.1.1
10382  * Copyright(c) 2006-2007, Ext JS, LLC.
10383  *
10384  * Originally Released Under LGPL - original licence link has changed is not relivant.
10385  *
10386  * Fork - LGPL
10387  * <script type="text/javascript">
10388  */
10389
10390 /**
10391  * @class Roo.data.ScriptTagProxy
10392  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10393  * other than the originating domain of the running page.<br><br>
10394  * <p>
10395  * <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
10396  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10397  * <p>
10398  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10399  * source code that is used as the source inside a &lt;script> tag.<br><br>
10400  * <p>
10401  * In order for the browser to process the returned data, the server must wrap the data object
10402  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10403  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10404  * depending on whether the callback name was passed:
10405  * <p>
10406  * <pre><code>
10407 boolean scriptTag = false;
10408 String cb = request.getParameter("callback");
10409 if (cb != null) {
10410     scriptTag = true;
10411     response.setContentType("text/javascript");
10412 } else {
10413     response.setContentType("application/x-json");
10414 }
10415 Writer out = response.getWriter();
10416 if (scriptTag) {
10417     out.write(cb + "(");
10418 }
10419 out.print(dataBlock.toJsonString());
10420 if (scriptTag) {
10421     out.write(");");
10422 }
10423 </pre></code>
10424  *
10425  * @constructor
10426  * @param {Object} config A configuration object.
10427  */
10428 Roo.data.ScriptTagProxy = function(config){
10429     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10430     Roo.apply(this, config);
10431     this.head = document.getElementsByTagName("head")[0];
10432 };
10433
10434 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10435
10436 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10437     /**
10438      * @cfg {String} url The URL from which to request the data object.
10439      */
10440     /**
10441      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10442      */
10443     timeout : 30000,
10444     /**
10445      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10446      * the server the name of the callback function set up by the load call to process the returned data object.
10447      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10448      * javascript output which calls this named function passing the data object as its only parameter.
10449      */
10450     callbackParam : "callback",
10451     /**
10452      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10453      * name to the request.
10454      */
10455     nocache : true,
10456
10457     /**
10458      * Load data from the configured URL, read the data object into
10459      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10460      * process that block using the passed callback.
10461      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10462      * for the request to the remote server.
10463      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10464      * object into a block of Roo.data.Records.
10465      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10466      * The function must be passed <ul>
10467      * <li>The Record block object</li>
10468      * <li>The "arg" argument from the load function</li>
10469      * <li>A boolean success indicator</li>
10470      * </ul>
10471      * @param {Object} scope The scope in which to call the callback
10472      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10473      */
10474     load : function(params, reader, callback, scope, arg){
10475         if(this.fireEvent("beforeload", this, params) !== false){
10476
10477             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10478
10479             var url = this.url;
10480             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10481             if(this.nocache){
10482                 url += "&_dc=" + (new Date().getTime());
10483             }
10484             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10485             var trans = {
10486                 id : transId,
10487                 cb : "stcCallback"+transId,
10488                 scriptId : "stcScript"+transId,
10489                 params : params,
10490                 arg : arg,
10491                 url : url,
10492                 callback : callback,
10493                 scope : scope,
10494                 reader : reader
10495             };
10496             var conn = this;
10497
10498             window[trans.cb] = function(o){
10499                 conn.handleResponse(o, trans);
10500             };
10501
10502             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10503
10504             if(this.autoAbort !== false){
10505                 this.abort();
10506             }
10507
10508             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10509
10510             var script = document.createElement("script");
10511             script.setAttribute("src", url);
10512             script.setAttribute("type", "text/javascript");
10513             script.setAttribute("id", trans.scriptId);
10514             this.head.appendChild(script);
10515
10516             this.trans = trans;
10517         }else{
10518             callback.call(scope||this, null, arg, false);
10519         }
10520     },
10521
10522     // private
10523     isLoading : function(){
10524         return this.trans ? true : false;
10525     },
10526
10527     /**
10528      * Abort the current server request.
10529      */
10530     abort : function(){
10531         if(this.isLoading()){
10532             this.destroyTrans(this.trans);
10533         }
10534     },
10535
10536     // private
10537     destroyTrans : function(trans, isLoaded){
10538         this.head.removeChild(document.getElementById(trans.scriptId));
10539         clearTimeout(trans.timeoutId);
10540         if(isLoaded){
10541             window[trans.cb] = undefined;
10542             try{
10543                 delete window[trans.cb];
10544             }catch(e){}
10545         }else{
10546             // if hasn't been loaded, wait for load to remove it to prevent script error
10547             window[trans.cb] = function(){
10548                 window[trans.cb] = undefined;
10549                 try{
10550                     delete window[trans.cb];
10551                 }catch(e){}
10552             };
10553         }
10554     },
10555
10556     // private
10557     handleResponse : function(o, trans){
10558         this.trans = false;
10559         this.destroyTrans(trans, true);
10560         var result;
10561         try {
10562             result = trans.reader.readRecords(o);
10563         }catch(e){
10564             this.fireEvent("loadexception", this, o, trans.arg, e);
10565             trans.callback.call(trans.scope||window, null, trans.arg, false);
10566             return;
10567         }
10568         this.fireEvent("load", this, o, trans.arg);
10569         trans.callback.call(trans.scope||window, result, trans.arg, true);
10570     },
10571
10572     // private
10573     handleFailure : function(trans){
10574         this.trans = false;
10575         this.destroyTrans(trans, false);
10576         this.fireEvent("loadexception", this, null, trans.arg);
10577         trans.callback.call(trans.scope||window, null, trans.arg, false);
10578     }
10579 });/*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589
10590 /**
10591  * @class Roo.data.JsonReader
10592  * @extends Roo.data.DataReader
10593  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10594  * based on mappings in a provided Roo.data.Record constructor.
10595  * 
10596  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10597  * in the reply previously. 
10598  * 
10599  * <p>
10600  * Example code:
10601  * <pre><code>
10602 var RecordDef = Roo.data.Record.create([
10603     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10604     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10605 ]);
10606 var myReader = new Roo.data.JsonReader({
10607     totalProperty: "results",    // The property which contains the total dataset size (optional)
10608     root: "rows",                // The property which contains an Array of row objects
10609     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10610 }, RecordDef);
10611 </code></pre>
10612  * <p>
10613  * This would consume a JSON file like this:
10614  * <pre><code>
10615 { 'results': 2, 'rows': [
10616     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10617     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10618 }
10619 </code></pre>
10620  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10621  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10622  * paged from the remote server.
10623  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10624  * @cfg {String} root name of the property which contains the Array of row objects.
10625  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10626  * @constructor
10627  * Create a new JsonReader
10628  * @param {Object} meta Metadata configuration options
10629  * @param {Object} recordType Either an Array of field definition objects,
10630  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10631  */
10632 Roo.data.JsonReader = function(meta, recordType){
10633     
10634     meta = meta || {};
10635     // set some defaults:
10636     Roo.applyIf(meta, {
10637         totalProperty: 'total',
10638         successProperty : 'success',
10639         root : 'data',
10640         id : 'id'
10641     });
10642     
10643     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10644 };
10645 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10646     
10647     /**
10648      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10649      * Used by Store query builder to append _requestMeta to params.
10650      * 
10651      */
10652     metaFromRemote : false,
10653     /**
10654      * This method is only used by a DataProxy which has retrieved data from a remote server.
10655      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10656      * @return {Object} data A data block which is used by an Roo.data.Store object as
10657      * a cache of Roo.data.Records.
10658      */
10659     read : function(response){
10660         var json = response.responseText;
10661        
10662         var o = /* eval:var:o */ eval("("+json+")");
10663         if(!o) {
10664             throw {message: "JsonReader.read: Json object not found"};
10665         }
10666         
10667         if(o.metaData){
10668             
10669             delete this.ef;
10670             this.metaFromRemote = true;
10671             this.meta = o.metaData;
10672             this.recordType = Roo.data.Record.create(o.metaData.fields);
10673             this.onMetaChange(this.meta, this.recordType, o);
10674         }
10675         return this.readRecords(o);
10676     },
10677
10678     // private function a store will implement
10679     onMetaChange : function(meta, recordType, o){
10680
10681     },
10682
10683     /**
10684          * @ignore
10685          */
10686     simpleAccess: function(obj, subsc) {
10687         return obj[subsc];
10688     },
10689
10690         /**
10691          * @ignore
10692          */
10693     getJsonAccessor: function(){
10694         var re = /[\[\.]/;
10695         return function(expr) {
10696             try {
10697                 return(re.test(expr))
10698                     ? new Function("obj", "return obj." + expr)
10699                     : function(obj){
10700                         return obj[expr];
10701                     };
10702             } catch(e){}
10703             return Roo.emptyFn;
10704         };
10705     }(),
10706
10707     /**
10708      * Create a data block containing Roo.data.Records from an XML document.
10709      * @param {Object} o An object which contains an Array of row objects in the property specified
10710      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10711      * which contains the total size of the dataset.
10712      * @return {Object} data A data block which is used by an Roo.data.Store object as
10713      * a cache of Roo.data.Records.
10714      */
10715     readRecords : function(o){
10716         /**
10717          * After any data loads, the raw JSON data is available for further custom processing.
10718          * @type Object
10719          */
10720         this.o = o;
10721         var s = this.meta, Record = this.recordType,
10722             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10723
10724 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10725         if (!this.ef) {
10726             if(s.totalProperty) {
10727                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10728                 }
10729                 if(s.successProperty) {
10730                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10731                 }
10732                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10733                 if (s.id) {
10734                         var g = this.getJsonAccessor(s.id);
10735                         this.getId = function(rec) {
10736                                 var r = g(rec);  
10737                                 return (r === undefined || r === "") ? null : r;
10738                         };
10739                 } else {
10740                         this.getId = function(){return null;};
10741                 }
10742             this.ef = [];
10743             for(var jj = 0; jj < fl; jj++){
10744                 f = fi[jj];
10745                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10746                 this.ef[jj] = this.getJsonAccessor(map);
10747             }
10748         }
10749
10750         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10751         if(s.totalProperty){
10752             var vt = parseInt(this.getTotal(o), 10);
10753             if(!isNaN(vt)){
10754                 totalRecords = vt;
10755             }
10756         }
10757         if(s.successProperty){
10758             var vs = this.getSuccess(o);
10759             if(vs === false || vs === 'false'){
10760                 success = false;
10761             }
10762         }
10763         var records = [];
10764         for(var i = 0; i < c; i++){
10765                 var n = root[i];
10766             var values = {};
10767             var id = this.getId(n);
10768             for(var j = 0; j < fl; j++){
10769                 f = fi[j];
10770             var v = this.ef[j](n);
10771             if (!f.convert) {
10772                 Roo.log('missing convert for ' + f.name);
10773                 Roo.log(f);
10774                 continue;
10775             }
10776             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10777             }
10778             var record = new Record(values, id);
10779             record.json = n;
10780             records[i] = record;
10781         }
10782         return {
10783             raw : o,
10784             success : success,
10785             records : records,
10786             totalRecords : totalRecords
10787         };
10788     }
10789 });/*
10790  * Based on:
10791  * Ext JS Library 1.1.1
10792  * Copyright(c) 2006-2007, Ext JS, LLC.
10793  *
10794  * Originally Released Under LGPL - original licence link has changed is not relivant.
10795  *
10796  * Fork - LGPL
10797  * <script type="text/javascript">
10798  */
10799
10800 /**
10801  * @class Roo.data.ArrayReader
10802  * @extends Roo.data.DataReader
10803  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10804  * Each element of that Array represents a row of data fields. The
10805  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10806  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10807  * <p>
10808  * Example code:.
10809  * <pre><code>
10810 var RecordDef = Roo.data.Record.create([
10811     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10812     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10813 ]);
10814 var myReader = new Roo.data.ArrayReader({
10815     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10816 }, RecordDef);
10817 </code></pre>
10818  * <p>
10819  * This would consume an Array like this:
10820  * <pre><code>
10821 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10822   </code></pre>
10823  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10824  * @constructor
10825  * Create a new JsonReader
10826  * @param {Object} meta Metadata configuration options.
10827  * @param {Object} recordType Either an Array of field definition objects
10828  * as specified to {@link Roo.data.Record#create},
10829  * or an {@link Roo.data.Record} object
10830  * created using {@link Roo.data.Record#create}.
10831  */
10832 Roo.data.ArrayReader = function(meta, recordType){
10833     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10834 };
10835
10836 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10837     /**
10838      * Create a data block containing Roo.data.Records from an XML document.
10839      * @param {Object} o An Array of row objects which represents the dataset.
10840      * @return {Object} data A data block which is used by an Roo.data.Store object as
10841      * a cache of Roo.data.Records.
10842      */
10843     readRecords : function(o){
10844         var sid = this.meta ? this.meta.id : null;
10845         var recordType = this.recordType, fields = recordType.prototype.fields;
10846         var records = [];
10847         var root = o;
10848             for(var i = 0; i < root.length; i++){
10849                     var n = root[i];
10850                 var values = {};
10851                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10852                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10853                 var f = fields.items[j];
10854                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10855                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10856                 v = f.convert(v);
10857                 values[f.name] = v;
10858             }
10859                 var record = new recordType(values, id);
10860                 record.json = n;
10861                 records[records.length] = record;
10862             }
10863             return {
10864                 records : records,
10865                 totalRecords : records.length
10866             };
10867     }
10868 });/*
10869  * - LGPL
10870  * * 
10871  */
10872
10873 /**
10874  * @class Roo.bootstrap.ComboBox
10875  * @extends Roo.bootstrap.TriggerField
10876  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10877  * @cfg {Boolean} append (true|false) default false
10878  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10879  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10880  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10881  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10882  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10883  * @constructor
10884  * Create a new ComboBox.
10885  * @param {Object} config Configuration options
10886  */
10887 Roo.bootstrap.ComboBox = function(config){
10888     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10889     this.addEvents({
10890         /**
10891          * @event expand
10892          * Fires when the dropdown list is expanded
10893              * @param {Roo.bootstrap.ComboBox} combo This combo box
10894              */
10895         'expand' : true,
10896         /**
10897          * @event collapse
10898          * Fires when the dropdown list is collapsed
10899              * @param {Roo.bootstrap.ComboBox} combo This combo box
10900              */
10901         'collapse' : true,
10902         /**
10903          * @event beforeselect
10904          * Fires before a list item is selected. Return false to cancel the selection.
10905              * @param {Roo.bootstrap.ComboBox} combo This combo box
10906              * @param {Roo.data.Record} record The data record returned from the underlying store
10907              * @param {Number} index The index of the selected item in the dropdown list
10908              */
10909         'beforeselect' : true,
10910         /**
10911          * @event select
10912          * Fires when a list item is selected
10913              * @param {Roo.bootstrap.ComboBox} combo This combo box
10914              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10915              * @param {Number} index The index of the selected item in the dropdown list
10916              */
10917         'select' : true,
10918         /**
10919          * @event beforequery
10920          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10921          * The event object passed has these properties:
10922              * @param {Roo.bootstrap.ComboBox} combo This combo box
10923              * @param {String} query The query
10924              * @param {Boolean} forceAll true to force "all" query
10925              * @param {Boolean} cancel true to cancel the query
10926              * @param {Object} e The query event object
10927              */
10928         'beforequery': true,
10929          /**
10930          * @event add
10931          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10932              * @param {Roo.bootstrap.ComboBox} combo This combo box
10933              */
10934         'add' : true,
10935         /**
10936          * @event edit
10937          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10938              * @param {Roo.bootstrap.ComboBox} combo This combo box
10939              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10940              */
10941         'edit' : true,
10942         /**
10943          * @event remove
10944          * Fires when the remove value from the combobox array
10945              * @param {Roo.bootstrap.ComboBox} combo This combo box
10946              */
10947         'remove' : true,
10948         /**
10949          * @event specialfilter
10950          * Fires when specialfilter
10951             * @param {Roo.bootstrap.ComboBox} combo This combo box
10952             */
10953         'specialfilter' : true
10954         
10955     });
10956     
10957     this.item = [];
10958     this.tickItems = [];
10959     
10960     this.selectedIndex = -1;
10961     if(this.mode == 'local'){
10962         if(config.queryDelay === undefined){
10963             this.queryDelay = 10;
10964         }
10965         if(config.minChars === undefined){
10966             this.minChars = 0;
10967         }
10968     }
10969 };
10970
10971 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10972      
10973     /**
10974      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10975      * rendering into an Roo.Editor, defaults to false)
10976      */
10977     /**
10978      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10979      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10980      */
10981     /**
10982      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10983      */
10984     /**
10985      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10986      * the dropdown list (defaults to undefined, with no header element)
10987      */
10988
10989      /**
10990      * @cfg {String/Roo.Template} tpl The template to use to render the output
10991      */
10992      
10993      /**
10994      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10995      */
10996     listWidth: undefined,
10997     /**
10998      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10999      * mode = 'remote' or 'text' if mode = 'local')
11000      */
11001     displayField: undefined,
11002     
11003     /**
11004      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11005      * mode = 'remote' or 'value' if mode = 'local'). 
11006      * Note: use of a valueField requires the user make a selection
11007      * in order for a value to be mapped.
11008      */
11009     valueField: undefined,
11010     
11011     
11012     /**
11013      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11014      * field's data value (defaults to the underlying DOM element's name)
11015      */
11016     hiddenName: undefined,
11017     /**
11018      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11019      */
11020     listClass: '',
11021     /**
11022      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11023      */
11024     selectedClass: 'active',
11025     
11026     /**
11027      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11028      */
11029     shadow:'sides',
11030     /**
11031      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11032      * anchor positions (defaults to 'tl-bl')
11033      */
11034     listAlign: 'tl-bl?',
11035     /**
11036      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11037      */
11038     maxHeight: 300,
11039     /**
11040      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11041      * query specified by the allQuery config option (defaults to 'query')
11042      */
11043     triggerAction: 'query',
11044     /**
11045      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11046      * (defaults to 4, does not apply if editable = false)
11047      */
11048     minChars : 4,
11049     /**
11050      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11051      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11052      */
11053     typeAhead: false,
11054     /**
11055      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11056      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11057      */
11058     queryDelay: 500,
11059     /**
11060      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11061      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11062      */
11063     pageSize: 0,
11064     /**
11065      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11066      * when editable = true (defaults to false)
11067      */
11068     selectOnFocus:false,
11069     /**
11070      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11071      */
11072     queryParam: 'query',
11073     /**
11074      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11075      * when mode = 'remote' (defaults to 'Loading...')
11076      */
11077     loadingText: 'Loading...',
11078     /**
11079      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11080      */
11081     resizable: false,
11082     /**
11083      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11084      */
11085     handleHeight : 8,
11086     /**
11087      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11088      * traditional select (defaults to true)
11089      */
11090     editable: true,
11091     /**
11092      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11093      */
11094     allQuery: '',
11095     /**
11096      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11097      */
11098     mode: 'remote',
11099     /**
11100      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11101      * listWidth has a higher value)
11102      */
11103     minListWidth : 70,
11104     /**
11105      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11106      * allow the user to set arbitrary text into the field (defaults to false)
11107      */
11108     forceSelection:false,
11109     /**
11110      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11111      * if typeAhead = true (defaults to 250)
11112      */
11113     typeAheadDelay : 250,
11114     /**
11115      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11116      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11117      */
11118     valueNotFoundText : undefined,
11119     /**
11120      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11121      */
11122     blockFocus : false,
11123     
11124     /**
11125      * @cfg {Boolean} disableClear Disable showing of clear button.
11126      */
11127     disableClear : false,
11128     /**
11129      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11130      */
11131     alwaysQuery : false,
11132     
11133     /**
11134      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11135      */
11136     multiple : false,
11137     
11138     /**
11139      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11140      */
11141     invalidClass : "has-warning",
11142     
11143     /**
11144      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11145      */
11146     validClass : "has-success",
11147     
11148     /**
11149      * @cfg {Boolean} specialFilter (true|false) special filter default false
11150      */
11151     specialFilter : false,
11152     
11153     //private
11154     addicon : false,
11155     editicon: false,
11156     
11157     page: 0,
11158     hasQuery: false,
11159     append: false,
11160     loadNext: false,
11161     autoFocus : true,
11162     tickable : false,
11163     btnPosition : 'right',
11164     triggerList : true,
11165     showToggleBtn : true,
11166     // element that contains real text value.. (when hidden is used..)
11167     
11168     getAutoCreate : function()
11169     {
11170         var cfg = false;
11171         
11172         /*
11173          *  Normal ComboBox
11174          */
11175         if(!this.tickable){
11176             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11177             return cfg;
11178         }
11179         
11180         /*
11181          *  ComboBox with tickable selections
11182          */
11183              
11184         var align = this.labelAlign || this.parentLabelAlign();
11185         
11186         cfg = {
11187             cls : 'form-group roo-combobox-tickable' //input-group
11188         };
11189         
11190         var buttons = {
11191             tag : 'div',
11192             cls : 'tickable-buttons',
11193             cn : [
11194                 {
11195                     tag : 'button',
11196                     type : 'button',
11197                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11198                     html : 'Edit'
11199                 },
11200                 {
11201                     tag : 'button',
11202                     type : 'button',
11203                     name : 'ok',
11204                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11205                     html : 'Done'
11206                 },
11207                 {
11208                     tag : 'button',
11209                     type : 'button',
11210                     name : 'cancel',
11211                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11212                     html : 'Cancel'
11213                 }
11214             ]
11215         };
11216         
11217         if(this.editable){
11218             buttons.cn.unshift({
11219                 tag: 'input',
11220                 cls: 'select2-search-field-input'
11221             });
11222         }
11223         
11224         var _this = this;
11225         
11226         Roo.each(buttons.cn, function(c){
11227             if (_this.size) {
11228                 c.cls += ' btn-' + _this.size;
11229             }
11230
11231             if (_this.disabled) {
11232                 c.disabled = true;
11233             }
11234         });
11235         
11236         var box = {
11237             tag: 'div',
11238             cn: [
11239                 {
11240                     tag: 'input',
11241                     type : 'hidden',
11242                     cls: 'form-hidden-field'
11243                 },
11244                 {
11245                     tag: 'ul',
11246                     cls: 'select2-choices',
11247                     cn:[
11248                         {
11249                             tag: 'li',
11250                             cls: 'select2-search-field',
11251                             cn: [
11252
11253                                 buttons
11254                             ]
11255                         }
11256                     ]
11257                 }
11258             ]
11259         }
11260         
11261         var combobox = {
11262             cls: 'select2-container input-group select2-container-multi',
11263             cn: [
11264                 box
11265 //                {
11266 //                    tag: 'ul',
11267 //                    cls: 'typeahead typeahead-long dropdown-menu',
11268 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11269 //                }
11270             ]
11271         };
11272         
11273         if(this.hasFeedback && !this.allowBlank){
11274             
11275             var feedback = {
11276                 tag: 'span',
11277                 cls: 'glyphicon form-control-feedback'
11278             };
11279
11280             combobox.cn.push(feedback);
11281         }
11282         
11283         if (align ==='left' && this.fieldLabel.length) {
11284             
11285                 Roo.log("left and has label");
11286                 cfg.cn = [
11287                     
11288                     {
11289                         tag: 'label',
11290                         'for' :  id,
11291                         cls : 'control-label col-sm-' + this.labelWidth,
11292                         html : this.fieldLabel
11293                         
11294                     },
11295                     {
11296                         cls : "col-sm-" + (12 - this.labelWidth), 
11297                         cn: [
11298                             combobox
11299                         ]
11300                     }
11301                     
11302                 ];
11303         } else if ( this.fieldLabel.length) {
11304                 Roo.log(" label");
11305                  cfg.cn = [
11306                    
11307                     {
11308                         tag: 'label',
11309                         //cls : 'input-group-addon',
11310                         html : this.fieldLabel
11311                         
11312                     },
11313                     
11314                     combobox
11315                     
11316                 ];
11317
11318         } else {
11319             
11320                 Roo.log(" no label && no align");
11321                 cfg = combobox
11322                      
11323                 
11324         }
11325          
11326         var settings=this;
11327         ['xs','sm','md','lg'].map(function(size){
11328             if (settings[size]) {
11329                 cfg.cls += ' col-' + size + '-' + settings[size];
11330             }
11331         });
11332         
11333         return cfg;
11334         
11335     },
11336     
11337     // private
11338     initEvents: function()
11339     {
11340         
11341         if (!this.store) {
11342             throw "can not find store for combo";
11343         }
11344         this.store = Roo.factory(this.store, Roo.data);
11345         
11346         if(this.tickable){
11347             this.initTickableEvents();
11348             return;
11349         }
11350         
11351         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11352         
11353         if(this.hiddenName){
11354             
11355             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11356             
11357             this.hiddenField.dom.value =
11358                 this.hiddenValue !== undefined ? this.hiddenValue :
11359                 this.value !== undefined ? this.value : '';
11360
11361             // prevent input submission
11362             this.el.dom.removeAttribute('name');
11363             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11364              
11365              
11366         }
11367         //if(Roo.isGecko){
11368         //    this.el.dom.setAttribute('autocomplete', 'off');
11369         //}
11370         
11371         var cls = 'x-combo-list';
11372         
11373         //this.list = new Roo.Layer({
11374         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11375         //});
11376         
11377         var _this = this;
11378         
11379         (function(){
11380             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11381             _this.list.setWidth(lw);
11382         }).defer(100);
11383         
11384         this.list.on('mouseover', this.onViewOver, this);
11385         this.list.on('mousemove', this.onViewMove, this);
11386         
11387         this.list.on('scroll', this.onViewScroll, this);
11388         
11389         /*
11390         this.list.swallowEvent('mousewheel');
11391         this.assetHeight = 0;
11392
11393         if(this.title){
11394             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11395             this.assetHeight += this.header.getHeight();
11396         }
11397
11398         this.innerList = this.list.createChild({cls:cls+'-inner'});
11399         this.innerList.on('mouseover', this.onViewOver, this);
11400         this.innerList.on('mousemove', this.onViewMove, this);
11401         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11402         
11403         if(this.allowBlank && !this.pageSize && !this.disableClear){
11404             this.footer = this.list.createChild({cls:cls+'-ft'});
11405             this.pageTb = new Roo.Toolbar(this.footer);
11406            
11407         }
11408         if(this.pageSize){
11409             this.footer = this.list.createChild({cls:cls+'-ft'});
11410             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11411                     {pageSize: this.pageSize});
11412             
11413         }
11414         
11415         if (this.pageTb && this.allowBlank && !this.disableClear) {
11416             var _this = this;
11417             this.pageTb.add(new Roo.Toolbar.Fill(), {
11418                 cls: 'x-btn-icon x-btn-clear',
11419                 text: '&#160;',
11420                 handler: function()
11421                 {
11422                     _this.collapse();
11423                     _this.clearValue();
11424                     _this.onSelect(false, -1);
11425                 }
11426             });
11427         }
11428         if (this.footer) {
11429             this.assetHeight += this.footer.getHeight();
11430         }
11431         */
11432             
11433         if(!this.tpl){
11434             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11435         }
11436
11437         this.view = new Roo.View(this.list, this.tpl, {
11438             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11439         });
11440         //this.view.wrapEl.setDisplayed(false);
11441         this.view.on('click', this.onViewClick, this);
11442         
11443         
11444         
11445         this.store.on('beforeload', this.onBeforeLoad, this);
11446         this.store.on('load', this.onLoad, this);
11447         this.store.on('loadexception', this.onLoadException, this);
11448         /*
11449         if(this.resizable){
11450             this.resizer = new Roo.Resizable(this.list,  {
11451                pinned:true, handles:'se'
11452             });
11453             this.resizer.on('resize', function(r, w, h){
11454                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11455                 this.listWidth = w;
11456                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11457                 this.restrictHeight();
11458             }, this);
11459             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11460         }
11461         */
11462         if(!this.editable){
11463             this.editable = true;
11464             this.setEditable(false);
11465         }
11466         
11467         /*
11468         
11469         if (typeof(this.events.add.listeners) != 'undefined') {
11470             
11471             this.addicon = this.wrap.createChild(
11472                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11473        
11474             this.addicon.on('click', function(e) {
11475                 this.fireEvent('add', this);
11476             }, this);
11477         }
11478         if (typeof(this.events.edit.listeners) != 'undefined') {
11479             
11480             this.editicon = this.wrap.createChild(
11481                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11482             if (this.addicon) {
11483                 this.editicon.setStyle('margin-left', '40px');
11484             }
11485             this.editicon.on('click', function(e) {
11486                 
11487                 // we fire even  if inothing is selected..
11488                 this.fireEvent('edit', this, this.lastData );
11489                 
11490             }, this);
11491         }
11492         */
11493         
11494         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11495             "up" : function(e){
11496                 this.inKeyMode = true;
11497                 this.selectPrev();
11498             },
11499
11500             "down" : function(e){
11501                 if(!this.isExpanded()){
11502                     this.onTriggerClick();
11503                 }else{
11504                     this.inKeyMode = true;
11505                     this.selectNext();
11506                 }
11507             },
11508
11509             "enter" : function(e){
11510 //                this.onViewClick();
11511                 //return true;
11512                 this.collapse();
11513                 
11514                 if(this.fireEvent("specialkey", this, e)){
11515                     this.onViewClick(false);
11516                 }
11517                 
11518                 return true;
11519             },
11520
11521             "esc" : function(e){
11522                 this.collapse();
11523             },
11524
11525             "tab" : function(e){
11526                 this.collapse();
11527                 
11528                 if(this.fireEvent("specialkey", this, e)){
11529                     this.onViewClick(false);
11530                 }
11531                 
11532                 return true;
11533             },
11534
11535             scope : this,
11536
11537             doRelay : function(foo, bar, hname){
11538                 if(hname == 'down' || this.scope.isExpanded()){
11539                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11540                 }
11541                 return true;
11542             },
11543
11544             forceKeyDown: true
11545         });
11546         
11547         
11548         this.queryDelay = Math.max(this.queryDelay || 10,
11549                 this.mode == 'local' ? 10 : 250);
11550         
11551         
11552         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11553         
11554         if(this.typeAhead){
11555             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11556         }
11557         if(this.editable !== false){
11558             this.inputEl().on("keyup", this.onKeyUp, this);
11559         }
11560         if(this.forceSelection){
11561             this.inputEl().on('blur', this.doForce, this);
11562         }
11563         
11564         if(this.multiple){
11565             this.choices = this.el.select('ul.select2-choices', true).first();
11566             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11567         }
11568     },
11569     
11570     initTickableEvents: function()
11571     {   
11572         this.createList();
11573         
11574         if(this.hiddenName){
11575             
11576             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11577             
11578             this.hiddenField.dom.value =
11579                 this.hiddenValue !== undefined ? this.hiddenValue :
11580                 this.value !== undefined ? this.value : '';
11581
11582             // prevent input submission
11583             this.el.dom.removeAttribute('name');
11584             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11585              
11586              
11587         }
11588         
11589 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11590         
11591         this.choices = this.el.select('ul.select2-choices', true).first();
11592         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11593         if(this.triggerList){
11594             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11595         }
11596          
11597         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11598         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11599         
11600         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11601         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11602         
11603         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11604         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11605         
11606         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11607         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11608         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11609         
11610         this.okBtn.hide();
11611         this.cancelBtn.hide();
11612         
11613         var _this = this;
11614         
11615         (function(){
11616             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11617             _this.list.setWidth(lw);
11618         }).defer(100);
11619         
11620         this.list.on('mouseover', this.onViewOver, this);
11621         this.list.on('mousemove', this.onViewMove, this);
11622         
11623         this.list.on('scroll', this.onViewScroll, this);
11624         
11625         if(!this.tpl){
11626             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>';
11627         }
11628
11629         this.view = new Roo.View(this.list, this.tpl, {
11630             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11631         });
11632         
11633         //this.view.wrapEl.setDisplayed(false);
11634         this.view.on('click', this.onViewClick, this);
11635         
11636         
11637         
11638         this.store.on('beforeload', this.onBeforeLoad, this);
11639         this.store.on('load', this.onLoad, this);
11640         this.store.on('loadexception', this.onLoadException, this);
11641         
11642         if(this.editable){
11643             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11644                 "up" : function(e){
11645                     this.inKeyMode = true;
11646                     this.selectPrev();
11647                 },
11648
11649                 "down" : function(e){
11650                     this.inKeyMode = true;
11651                     this.selectNext();
11652                 },
11653
11654                 "enter" : function(e){
11655                     if(this.fireEvent("specialkey", this, e)){
11656                         this.onViewClick(false);
11657                     }
11658                     
11659                     return true;
11660                 },
11661
11662                 "esc" : function(e){
11663                     this.onTickableFooterButtonClick(e, false, false);
11664                 },
11665
11666                 "tab" : function(e){
11667                     this.fireEvent("specialkey", this, e);
11668                     
11669                     this.onTickableFooterButtonClick(e, false, false);
11670                     
11671                     return true;
11672                 },
11673
11674                 scope : this,
11675
11676                 doRelay : function(e, fn, key){
11677                     if(this.scope.isExpanded()){
11678                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11679                     }
11680                     return true;
11681                 },
11682
11683                 forceKeyDown: true
11684             });
11685         }
11686         
11687         this.queryDelay = Math.max(this.queryDelay || 10,
11688                 this.mode == 'local' ? 10 : 250);
11689         
11690         
11691         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11692         
11693         if(this.typeAhead){
11694             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11695         }
11696         
11697         if(this.editable !== false){
11698             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11699         }
11700         
11701     },
11702
11703     onDestroy : function(){
11704         if(this.view){
11705             this.view.setStore(null);
11706             this.view.el.removeAllListeners();
11707             this.view.el.remove();
11708             this.view.purgeListeners();
11709         }
11710         if(this.list){
11711             this.list.dom.innerHTML  = '';
11712         }
11713         
11714         if(this.store){
11715             this.store.un('beforeload', this.onBeforeLoad, this);
11716             this.store.un('load', this.onLoad, this);
11717             this.store.un('loadexception', this.onLoadException, this);
11718         }
11719         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11720     },
11721
11722     // private
11723     fireKey : function(e){
11724         if(e.isNavKeyPress() && !this.list.isVisible()){
11725             this.fireEvent("specialkey", this, e);
11726         }
11727     },
11728
11729     // private
11730     onResize: function(w, h){
11731 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11732 //        
11733 //        if(typeof w != 'number'){
11734 //            // we do not handle it!?!?
11735 //            return;
11736 //        }
11737 //        var tw = this.trigger.getWidth();
11738 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11739 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11740 //        var x = w - tw;
11741 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11742 //            
11743 //        //this.trigger.setStyle('left', x+'px');
11744 //        
11745 //        if(this.list && this.listWidth === undefined){
11746 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11747 //            this.list.setWidth(lw);
11748 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11749 //        }
11750         
11751     
11752         
11753     },
11754
11755     /**
11756      * Allow or prevent the user from directly editing the field text.  If false is passed,
11757      * the user will only be able to select from the items defined in the dropdown list.  This method
11758      * is the runtime equivalent of setting the 'editable' config option at config time.
11759      * @param {Boolean} value True to allow the user to directly edit the field text
11760      */
11761     setEditable : function(value){
11762         if(value == this.editable){
11763             return;
11764         }
11765         this.editable = value;
11766         if(!value){
11767             this.inputEl().dom.setAttribute('readOnly', true);
11768             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11769             this.inputEl().addClass('x-combo-noedit');
11770         }else{
11771             this.inputEl().dom.setAttribute('readOnly', false);
11772             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11773             this.inputEl().removeClass('x-combo-noedit');
11774         }
11775     },
11776
11777     // private
11778     
11779     onBeforeLoad : function(combo,opts){
11780         if(!this.hasFocus){
11781             return;
11782         }
11783          if (!opts.add) {
11784             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11785          }
11786         this.restrictHeight();
11787         this.selectedIndex = -1;
11788     },
11789
11790     // private
11791     onLoad : function(){
11792         
11793         this.hasQuery = false;
11794         
11795         if(!this.hasFocus){
11796             return;
11797         }
11798         
11799         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11800             this.loading.hide();
11801         }
11802              
11803         if(this.store.getCount() > 0){
11804             this.expand();
11805             this.restrictHeight();
11806             if(this.lastQuery == this.allQuery){
11807                 if(this.editable && !this.tickable){
11808                     this.inputEl().dom.select();
11809                 }
11810                 
11811                 if(
11812                     !this.selectByValue(this.value, true) &&
11813                     this.autoFocus && 
11814                     (
11815                         !this.store.lastOptions ||
11816                         typeof(this.store.lastOptions.add) == 'undefined' || 
11817                         this.store.lastOptions.add != true
11818                     )
11819                 ){
11820                     this.select(0, true);
11821                 }
11822             }else{
11823                 if(this.autoFocus){
11824                     this.selectNext();
11825                 }
11826                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11827                     this.taTask.delay(this.typeAheadDelay);
11828                 }
11829             }
11830         }else{
11831             this.onEmptyResults();
11832         }
11833         
11834         //this.el.focus();
11835     },
11836     // private
11837     onLoadException : function()
11838     {
11839         this.hasQuery = false;
11840         
11841         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11842             this.loading.hide();
11843         }
11844         
11845         if(this.tickable && this.editable){
11846             return;
11847         }
11848         
11849         this.collapse();
11850         
11851         Roo.log(this.store.reader.jsonData);
11852         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11853             // fixme
11854             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11855         }
11856         
11857         
11858     },
11859     // private
11860     onTypeAhead : function(){
11861         if(this.store.getCount() > 0){
11862             var r = this.store.getAt(0);
11863             var newValue = r.data[this.displayField];
11864             var len = newValue.length;
11865             var selStart = this.getRawValue().length;
11866             
11867             if(selStart != len){
11868                 this.setRawValue(newValue);
11869                 this.selectText(selStart, newValue.length);
11870             }
11871         }
11872     },
11873
11874     // private
11875     onSelect : function(record, index){
11876         
11877         if(this.fireEvent('beforeselect', this, record, index) !== false){
11878         
11879             this.setFromData(index > -1 ? record.data : false);
11880             
11881             this.collapse();
11882             this.fireEvent('select', this, record, index);
11883         }
11884     },
11885
11886     /**
11887      * Returns the currently selected field value or empty string if no value is set.
11888      * @return {String} value The selected value
11889      */
11890     getValue : function(){
11891         
11892         if(this.multiple){
11893             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11894         }
11895         
11896         if(this.valueField){
11897             return typeof this.value != 'undefined' ? this.value : '';
11898         }else{
11899             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11900         }
11901     },
11902
11903     /**
11904      * Clears any text/value currently set in the field
11905      */
11906     clearValue : function(){
11907         if(this.hiddenField){
11908             this.hiddenField.dom.value = '';
11909         }
11910         this.value = '';
11911         this.setRawValue('');
11912         this.lastSelectionText = '';
11913         this.lastData = false;
11914         
11915     },
11916
11917     /**
11918      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11919      * will be displayed in the field.  If the value does not match the data value of an existing item,
11920      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11921      * Otherwise the field will be blank (although the value will still be set).
11922      * @param {String} value The value to match
11923      */
11924     setValue : function(v){
11925         if(this.multiple){
11926             this.syncValue();
11927             return;
11928         }
11929         
11930         var text = v;
11931         if(this.valueField){
11932             var r = this.findRecord(this.valueField, v);
11933             if(r){
11934                 text = r.data[this.displayField];
11935             }else if(this.valueNotFoundText !== undefined){
11936                 text = this.valueNotFoundText;
11937             }
11938         }
11939         this.lastSelectionText = text;
11940         if(this.hiddenField){
11941             this.hiddenField.dom.value = v;
11942         }
11943         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11944         this.value = v;
11945     },
11946     /**
11947      * @property {Object} the last set data for the element
11948      */
11949     
11950     lastData : false,
11951     /**
11952      * Sets the value of the field based on a object which is related to the record format for the store.
11953      * @param {Object} value the value to set as. or false on reset?
11954      */
11955     setFromData : function(o){
11956         
11957         if(this.multiple){
11958             this.addItem(o);
11959             return;
11960         }
11961             
11962         var dv = ''; // display value
11963         var vv = ''; // value value..
11964         this.lastData = o;
11965         if (this.displayField) {
11966             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11967         } else {
11968             // this is an error condition!!!
11969             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11970         }
11971         
11972         if(this.valueField){
11973             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11974         }
11975         
11976         if(this.hiddenField){
11977             this.hiddenField.dom.value = vv;
11978             
11979             this.lastSelectionText = dv;
11980             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11981             this.value = vv;
11982             return;
11983         }
11984         // no hidden field.. - we store the value in 'value', but still display
11985         // display field!!!!
11986         this.lastSelectionText = dv;
11987         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11988         this.value = vv;
11989         
11990         
11991     },
11992     // private
11993     reset : function(){
11994         // overridden so that last data is reset..
11995         
11996         if(this.multiple){
11997             this.clearItem();
11998             return;
11999         }
12000         
12001         this.setValue(this.originalValue);
12002         this.clearInvalid();
12003         this.lastData = false;
12004         if (this.view) {
12005             this.view.clearSelections();
12006         }
12007     },
12008     // private
12009     findRecord : function(prop, value){
12010         var record;
12011         if(this.store.getCount() > 0){
12012             this.store.each(function(r){
12013                 if(r.data[prop] == value){
12014                     record = r;
12015                     return false;
12016                 }
12017                 return true;
12018             });
12019         }
12020         return record;
12021     },
12022     
12023     getName: function()
12024     {
12025         // returns hidden if it's set..
12026         if (!this.rendered) {return ''};
12027         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12028         
12029     },
12030     // private
12031     onViewMove : function(e, t){
12032         this.inKeyMode = false;
12033     },
12034
12035     // private
12036     onViewOver : function(e, t){
12037         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12038             return;
12039         }
12040         var item = this.view.findItemFromChild(t);
12041         
12042         if(item){
12043             var index = this.view.indexOf(item);
12044             this.select(index, false);
12045         }
12046     },
12047
12048     // private
12049     onViewClick : function(view, doFocus, el, e)
12050     {
12051         var index = this.view.getSelectedIndexes()[0];
12052         
12053         var r = this.store.getAt(index);
12054         
12055         if(this.tickable){
12056             
12057             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12058                 return;
12059             }
12060             
12061             var rm = false;
12062             var _this = this;
12063             
12064             Roo.each(this.tickItems, function(v,k){
12065                 
12066                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12067                     _this.tickItems.splice(k, 1);
12068                     
12069                     if(typeof(e) == 'undefined' && view == false){
12070                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12071                     }
12072                     
12073                     rm = true;
12074                     return;
12075                 }
12076             });
12077             
12078             if(rm){
12079                 return;
12080             }
12081             
12082             this.tickItems.push(r.data);
12083             
12084             if(typeof(e) == 'undefined' && view == false){
12085                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12086             }
12087                     
12088             return;
12089         }
12090         
12091         if(r){
12092             this.onSelect(r, index);
12093         }
12094         if(doFocus !== false && !this.blockFocus){
12095             this.inputEl().focus();
12096         }
12097     },
12098
12099     // private
12100     restrictHeight : function(){
12101         //this.innerList.dom.style.height = '';
12102         //var inner = this.innerList.dom;
12103         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12104         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12105         //this.list.beginUpdate();
12106         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12107         this.list.alignTo(this.inputEl(), this.listAlign);
12108         this.list.alignTo(this.inputEl(), this.listAlign);
12109         //this.list.endUpdate();
12110     },
12111
12112     // private
12113     onEmptyResults : function(){
12114         
12115         if(this.tickable && this.editable){
12116             this.restrictHeight();
12117             return;
12118         }
12119         
12120         this.collapse();
12121     },
12122
12123     /**
12124      * Returns true if the dropdown list is expanded, else false.
12125      */
12126     isExpanded : function(){
12127         return this.list.isVisible();
12128     },
12129
12130     /**
12131      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12132      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12133      * @param {String} value The data value of the item to select
12134      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12135      * selected item if it is not currently in view (defaults to true)
12136      * @return {Boolean} True if the value matched an item in the list, else false
12137      */
12138     selectByValue : function(v, scrollIntoView){
12139         if(v !== undefined && v !== null){
12140             var r = this.findRecord(this.valueField || this.displayField, v);
12141             if(r){
12142                 this.select(this.store.indexOf(r), scrollIntoView);
12143                 return true;
12144             }
12145         }
12146         return false;
12147     },
12148
12149     /**
12150      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12151      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12152      * @param {Number} index The zero-based index of the list item to select
12153      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12154      * selected item if it is not currently in view (defaults to true)
12155      */
12156     select : function(index, scrollIntoView){
12157         this.selectedIndex = index;
12158         this.view.select(index);
12159         if(scrollIntoView !== false){
12160             var el = this.view.getNode(index);
12161             /*
12162              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12163              */
12164             if(el){
12165                 this.list.scrollChildIntoView(el, false);
12166             }
12167         }
12168     },
12169
12170     // private
12171     selectNext : function(){
12172         var ct = this.store.getCount();
12173         if(ct > 0){
12174             if(this.selectedIndex == -1){
12175                 this.select(0);
12176             }else if(this.selectedIndex < ct-1){
12177                 this.select(this.selectedIndex+1);
12178             }
12179         }
12180     },
12181
12182     // private
12183     selectPrev : function(){
12184         var ct = this.store.getCount();
12185         if(ct > 0){
12186             if(this.selectedIndex == -1){
12187                 this.select(0);
12188             }else if(this.selectedIndex != 0){
12189                 this.select(this.selectedIndex-1);
12190             }
12191         }
12192     },
12193
12194     // private
12195     onKeyUp : function(e){
12196         if(this.editable !== false && !e.isSpecialKey()){
12197             this.lastKey = e.getKey();
12198             this.dqTask.delay(this.queryDelay);
12199         }
12200     },
12201
12202     // private
12203     validateBlur : function(){
12204         return !this.list || !this.list.isVisible();   
12205     },
12206
12207     // private
12208     initQuery : function(){
12209         
12210         var v = this.getRawValue();
12211         
12212         if(this.tickable && this.editable){
12213             v = this.tickableInputEl().getValue();
12214         }
12215         
12216         this.doQuery(v);
12217     },
12218
12219     // private
12220     doForce : function(){
12221         if(this.inputEl().dom.value.length > 0){
12222             this.inputEl().dom.value =
12223                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12224              
12225         }
12226     },
12227
12228     /**
12229      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12230      * query allowing the query action to be canceled if needed.
12231      * @param {String} query The SQL query to execute
12232      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12233      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12234      * saved in the current store (defaults to false)
12235      */
12236     doQuery : function(q, forceAll){
12237         
12238         if(q === undefined || q === null){
12239             q = '';
12240         }
12241         var qe = {
12242             query: q,
12243             forceAll: forceAll,
12244             combo: this,
12245             cancel:false
12246         };
12247         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12248             return false;
12249         }
12250         q = qe.query;
12251         
12252         forceAll = qe.forceAll;
12253         if(forceAll === true || (q.length >= this.minChars)){
12254             
12255             this.hasQuery = true;
12256             
12257             if(this.lastQuery != q || this.alwaysQuery){
12258                 this.lastQuery = q;
12259                 if(this.mode == 'local'){
12260                     this.selectedIndex = -1;
12261                     if(forceAll){
12262                         this.store.clearFilter();
12263                     }else{
12264                         
12265                         if(this.specialFilter){
12266                             this.fireEvent('specialfilter', this);
12267                             this.onLoad();
12268                             return;
12269                         }
12270                         
12271                         this.store.filter(this.displayField, q);
12272                     }
12273                     
12274                     this.store.fireEvent("datachanged", this.store);
12275                     
12276                     this.onLoad();
12277                     
12278                     
12279                 }else{
12280                     
12281                     this.store.baseParams[this.queryParam] = q;
12282                     
12283                     var options = {params : this.getParams(q)};
12284                     
12285                     if(this.loadNext){
12286                         options.add = true;
12287                         options.params.start = this.page * this.pageSize;
12288                     }
12289                     
12290                     this.store.load(options);
12291                     
12292                     /*
12293                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12294                      *  we should expand the list on onLoad
12295                      *  so command out it
12296                      */
12297 //                    this.expand();
12298                 }
12299             }else{
12300                 this.selectedIndex = -1;
12301                 this.onLoad();   
12302             }
12303         }
12304         
12305         this.loadNext = false;
12306     },
12307     
12308     // private
12309     getParams : function(q){
12310         var p = {};
12311         //p[this.queryParam] = q;
12312         
12313         if(this.pageSize){
12314             p.start = 0;
12315             p.limit = this.pageSize;
12316         }
12317         return p;
12318     },
12319
12320     /**
12321      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12322      */
12323     collapse : function(){
12324         if(!this.isExpanded()){
12325             return;
12326         }
12327         
12328         this.list.hide();
12329         
12330         if(this.tickable){
12331             this.hasFocus = false;
12332             this.okBtn.hide();
12333             this.cancelBtn.hide();
12334             this.trigger.show();
12335             
12336             if(this.editable){
12337                 this.tickableInputEl().dom.value = '';
12338                 this.tickableInputEl().blur();
12339             }
12340             
12341         }
12342         
12343         Roo.get(document).un('mousedown', this.collapseIf, this);
12344         Roo.get(document).un('mousewheel', this.collapseIf, this);
12345         if (!this.editable) {
12346             Roo.get(document).un('keydown', this.listKeyPress, this);
12347         }
12348         this.fireEvent('collapse', this);
12349     },
12350
12351     // private
12352     collapseIf : function(e){
12353         var in_combo  = e.within(this.el);
12354         var in_list =  e.within(this.list);
12355         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12356         
12357         if (in_combo || in_list || is_list) {
12358             //e.stopPropagation();
12359             return;
12360         }
12361         
12362         if(this.tickable){
12363             this.onTickableFooterButtonClick(e, false, false);
12364         }
12365
12366         this.collapse();
12367         
12368     },
12369
12370     /**
12371      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12372      */
12373     expand : function(){
12374        
12375         if(this.isExpanded() || !this.hasFocus){
12376             return;
12377         }
12378         
12379         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12380         this.list.setWidth(lw);
12381         
12382         
12383          Roo.log('expand');
12384         
12385         this.list.show();
12386         
12387         this.restrictHeight();
12388         
12389         if(this.tickable){
12390             
12391             this.tickItems = Roo.apply([], this.item);
12392             
12393             this.okBtn.show();
12394             this.cancelBtn.show();
12395             this.trigger.hide();
12396             
12397             if(this.editable){
12398                 this.tickableInputEl().focus();
12399             }
12400             
12401         }
12402         
12403         Roo.get(document).on('mousedown', this.collapseIf, this);
12404         Roo.get(document).on('mousewheel', this.collapseIf, this);
12405         if (!this.editable) {
12406             Roo.get(document).on('keydown', this.listKeyPress, this);
12407         }
12408         
12409         this.fireEvent('expand', this);
12410     },
12411
12412     // private
12413     // Implements the default empty TriggerField.onTriggerClick function
12414     onTriggerClick : function(e)
12415     {
12416         Roo.log('trigger click');
12417         
12418         if(this.disabled || !this.triggerList){
12419             return;
12420         }
12421         
12422         this.page = 0;
12423         this.loadNext = false;
12424         
12425         if(this.isExpanded()){
12426             this.collapse();
12427             if (!this.blockFocus) {
12428                 this.inputEl().focus();
12429             }
12430             
12431         }else {
12432             this.hasFocus = true;
12433             if(this.triggerAction == 'all') {
12434                 this.doQuery(this.allQuery, true);
12435             } else {
12436                 this.doQuery(this.getRawValue());
12437             }
12438             if (!this.blockFocus) {
12439                 this.inputEl().focus();
12440             }
12441         }
12442     },
12443     
12444     onTickableTriggerClick : function(e)
12445     {
12446         if(this.disabled){
12447             return;
12448         }
12449         
12450         this.page = 0;
12451         this.loadNext = false;
12452         this.hasFocus = true;
12453         
12454         if(this.triggerAction == 'all') {
12455             this.doQuery(this.allQuery, true);
12456         } else {
12457             this.doQuery(this.getRawValue());
12458         }
12459     },
12460     
12461     onSearchFieldClick : function(e)
12462     {
12463         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12464             this.onTickableFooterButtonClick(e, false, false);
12465             return;
12466         }
12467         
12468         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12469             return;
12470         }
12471         
12472         this.page = 0;
12473         this.loadNext = false;
12474         this.hasFocus = true;
12475         
12476         if(this.triggerAction == 'all') {
12477             this.doQuery(this.allQuery, true);
12478         } else {
12479             this.doQuery(this.getRawValue());
12480         }
12481     },
12482     
12483     listKeyPress : function(e)
12484     {
12485         //Roo.log('listkeypress');
12486         // scroll to first matching element based on key pres..
12487         if (e.isSpecialKey()) {
12488             return false;
12489         }
12490         var k = String.fromCharCode(e.getKey()).toUpperCase();
12491         //Roo.log(k);
12492         var match  = false;
12493         var csel = this.view.getSelectedNodes();
12494         var cselitem = false;
12495         if (csel.length) {
12496             var ix = this.view.indexOf(csel[0]);
12497             cselitem  = this.store.getAt(ix);
12498             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12499                 cselitem = false;
12500             }
12501             
12502         }
12503         
12504         this.store.each(function(v) { 
12505             if (cselitem) {
12506                 // start at existing selection.
12507                 if (cselitem.id == v.id) {
12508                     cselitem = false;
12509                 }
12510                 return true;
12511             }
12512                 
12513             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12514                 match = this.store.indexOf(v);
12515                 return false;
12516             }
12517             return true;
12518         }, this);
12519         
12520         if (match === false) {
12521             return true; // no more action?
12522         }
12523         // scroll to?
12524         this.view.select(match);
12525         var sn = Roo.get(this.view.getSelectedNodes()[0])
12526         sn.scrollIntoView(sn.dom.parentNode, false);
12527     },
12528     
12529     onViewScroll : function(e, t){
12530         
12531         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){
12532             return;
12533         }
12534         
12535         this.hasQuery = true;
12536         
12537         this.loading = this.list.select('.loading', true).first();
12538         
12539         if(this.loading === null){
12540             this.list.createChild({
12541                 tag: 'div',
12542                 cls: 'loading select2-more-results select2-active',
12543                 html: 'Loading more results...'
12544             })
12545             
12546             this.loading = this.list.select('.loading', true).first();
12547             
12548             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12549             
12550             this.loading.hide();
12551         }
12552         
12553         this.loading.show();
12554         
12555         var _combo = this;
12556         
12557         this.page++;
12558         this.loadNext = true;
12559         
12560         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12561         
12562         return;
12563     },
12564     
12565     addItem : function(o)
12566     {   
12567         var dv = ''; // display value
12568         
12569         if (this.displayField) {
12570             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12571         } else {
12572             // this is an error condition!!!
12573             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12574         }
12575         
12576         if(!dv.length){
12577             return;
12578         }
12579         
12580         var choice = this.choices.createChild({
12581             tag: 'li',
12582             cls: 'select2-search-choice',
12583             cn: [
12584                 {
12585                     tag: 'div',
12586                     html: dv
12587                 },
12588                 {
12589                     tag: 'a',
12590                     href: '#',
12591                     cls: 'select2-search-choice-close',
12592                     tabindex: '-1'
12593                 }
12594             ]
12595             
12596         }, this.searchField);
12597         
12598         var close = choice.select('a.select2-search-choice-close', true).first()
12599         
12600         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12601         
12602         this.item.push(o);
12603         
12604         this.lastData = o;
12605         
12606         this.syncValue();
12607         
12608         this.inputEl().dom.value = '';
12609         
12610         this.validate();
12611     },
12612     
12613     onRemoveItem : function(e, _self, o)
12614     {
12615         e.preventDefault();
12616         
12617         this.lastItem = Roo.apply([], this.item);
12618         
12619         var index = this.item.indexOf(o.data) * 1;
12620         
12621         if( index < 0){
12622             Roo.log('not this item?!');
12623             return;
12624         }
12625         
12626         this.item.splice(index, 1);
12627         o.item.remove();
12628         
12629         this.syncValue();
12630         
12631         this.fireEvent('remove', this, e);
12632         
12633         this.validate();
12634         
12635     },
12636     
12637     syncValue : function()
12638     {
12639         if(!this.item.length){
12640             this.clearValue();
12641             return;
12642         }
12643             
12644         var value = [];
12645         var _this = this;
12646         Roo.each(this.item, function(i){
12647             if(_this.valueField){
12648                 value.push(i[_this.valueField]);
12649                 return;
12650             }
12651
12652             value.push(i);
12653         });
12654
12655         this.value = value.join(',');
12656
12657         if(this.hiddenField){
12658             this.hiddenField.dom.value = this.value;
12659         }
12660         
12661         this.store.fireEvent("datachanged", this.store);
12662     },
12663     
12664     clearItem : function()
12665     {
12666         if(!this.multiple){
12667             return;
12668         }
12669         
12670         this.item = [];
12671         
12672         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12673            c.remove();
12674         });
12675         
12676         this.syncValue();
12677         
12678         this.validate();
12679     },
12680     
12681     inputEl: function ()
12682     {
12683         if(this.tickable){
12684             return this.searchField;
12685         }
12686         return this.el.select('input.form-control',true).first();
12687     },
12688     
12689     
12690     onTickableFooterButtonClick : function(e, btn, el)
12691     {
12692         e.preventDefault();
12693         
12694         this.lastItem = Roo.apply([], this.item);
12695         
12696         if(btn && btn.name == 'cancel'){
12697             this.tickItems = Roo.apply([], this.item);
12698             this.collapse();
12699             return;
12700         }
12701         
12702         this.clearItem();
12703         
12704         var _this = this;
12705         
12706         Roo.each(this.tickItems, function(o){
12707             _this.addItem(o);
12708         });
12709         
12710         this.collapse();
12711         
12712     },
12713     
12714     validate : function()
12715     {
12716         var v = this.getRawValue();
12717         
12718         if(this.multiple){
12719             v = this.getValue();
12720         }
12721         
12722         if(this.disabled || this.allowBlank || v.length){
12723             this.markValid();
12724             return true;
12725         }
12726         
12727         this.markInvalid();
12728         return false;
12729     },
12730     
12731     tickableInputEl : function()
12732     {
12733         if(!this.tickable || !this.editable){
12734             return this.inputEl();
12735         }
12736         
12737         return this.inputEl().select('.select2-search-field-input', true).first();
12738     }
12739     
12740     
12741
12742     /** 
12743     * @cfg {Boolean} grow 
12744     * @hide 
12745     */
12746     /** 
12747     * @cfg {Number} growMin 
12748     * @hide 
12749     */
12750     /** 
12751     * @cfg {Number} growMax 
12752     * @hide 
12753     */
12754     /**
12755      * @hide
12756      * @method autoSize
12757      */
12758 });
12759 /*
12760  * Based on:
12761  * Ext JS Library 1.1.1
12762  * Copyright(c) 2006-2007, Ext JS, LLC.
12763  *
12764  * Originally Released Under LGPL - original licence link has changed is not relivant.
12765  *
12766  * Fork - LGPL
12767  * <script type="text/javascript">
12768  */
12769
12770 /**
12771  * @class Roo.View
12772  * @extends Roo.util.Observable
12773  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12774  * This class also supports single and multi selection modes. <br>
12775  * Create a data model bound view:
12776  <pre><code>
12777  var store = new Roo.data.Store(...);
12778
12779  var view = new Roo.View({
12780     el : "my-element",
12781     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12782  
12783     singleSelect: true,
12784     selectedClass: "ydataview-selected",
12785     store: store
12786  });
12787
12788  // listen for node click?
12789  view.on("click", function(vw, index, node, e){
12790  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12791  });
12792
12793  // load XML data
12794  dataModel.load("foobar.xml");
12795  </code></pre>
12796  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12797  * <br><br>
12798  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12799  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12800  * 
12801  * Note: old style constructor is still suported (container, template, config)
12802  * 
12803  * @constructor
12804  * Create a new View
12805  * @param {Object} config The config object
12806  * 
12807  */
12808 Roo.View = function(config, depreciated_tpl, depreciated_config){
12809     
12810     this.parent = false;
12811     
12812     if (typeof(depreciated_tpl) == 'undefined') {
12813         // new way.. - universal constructor.
12814         Roo.apply(this, config);
12815         this.el  = Roo.get(this.el);
12816     } else {
12817         // old format..
12818         this.el  = Roo.get(config);
12819         this.tpl = depreciated_tpl;
12820         Roo.apply(this, depreciated_config);
12821     }
12822     this.wrapEl  = this.el.wrap().wrap();
12823     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12824     
12825     
12826     if(typeof(this.tpl) == "string"){
12827         this.tpl = new Roo.Template(this.tpl);
12828     } else {
12829         // support xtype ctors..
12830         this.tpl = new Roo.factory(this.tpl, Roo);
12831     }
12832     
12833     
12834     this.tpl.compile();
12835     
12836     /** @private */
12837     this.addEvents({
12838         /**
12839          * @event beforeclick
12840          * Fires before a click is processed. Returns false to cancel the default action.
12841          * @param {Roo.View} this
12842          * @param {Number} index The index of the target node
12843          * @param {HTMLElement} node The target node
12844          * @param {Roo.EventObject} e The raw event object
12845          */
12846             "beforeclick" : true,
12847         /**
12848          * @event click
12849          * Fires when a template node is clicked.
12850          * @param {Roo.View} this
12851          * @param {Number} index The index of the target node
12852          * @param {HTMLElement} node The target node
12853          * @param {Roo.EventObject} e The raw event object
12854          */
12855             "click" : true,
12856         /**
12857          * @event dblclick
12858          * Fires when a template node is double clicked.
12859          * @param {Roo.View} this
12860          * @param {Number} index The index of the target node
12861          * @param {HTMLElement} node The target node
12862          * @param {Roo.EventObject} e The raw event object
12863          */
12864             "dblclick" : true,
12865         /**
12866          * @event contextmenu
12867          * Fires when a template node is right clicked.
12868          * @param {Roo.View} this
12869          * @param {Number} index The index of the target node
12870          * @param {HTMLElement} node The target node
12871          * @param {Roo.EventObject} e The raw event object
12872          */
12873             "contextmenu" : true,
12874         /**
12875          * @event selectionchange
12876          * Fires when the selected nodes change.
12877          * @param {Roo.View} this
12878          * @param {Array} selections Array of the selected nodes
12879          */
12880             "selectionchange" : true,
12881     
12882         /**
12883          * @event beforeselect
12884          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12885          * @param {Roo.View} this
12886          * @param {HTMLElement} node The node to be selected
12887          * @param {Array} selections Array of currently selected nodes
12888          */
12889             "beforeselect" : true,
12890         /**
12891          * @event preparedata
12892          * Fires on every row to render, to allow you to change the data.
12893          * @param {Roo.View} this
12894          * @param {Object} data to be rendered (change this)
12895          */
12896           "preparedata" : true
12897           
12898           
12899         });
12900
12901
12902
12903     this.el.on({
12904         "click": this.onClick,
12905         "dblclick": this.onDblClick,
12906         "contextmenu": this.onContextMenu,
12907         scope:this
12908     });
12909
12910     this.selections = [];
12911     this.nodes = [];
12912     this.cmp = new Roo.CompositeElementLite([]);
12913     if(this.store){
12914         this.store = Roo.factory(this.store, Roo.data);
12915         this.setStore(this.store, true);
12916     }
12917     
12918     if ( this.footer && this.footer.xtype) {
12919            
12920          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12921         
12922         this.footer.dataSource = this.store
12923         this.footer.container = fctr;
12924         this.footer = Roo.factory(this.footer, Roo);
12925         fctr.insertFirst(this.el);
12926         
12927         // this is a bit insane - as the paging toolbar seems to detach the el..
12928 //        dom.parentNode.parentNode.parentNode
12929          // they get detached?
12930     }
12931     
12932     
12933     Roo.View.superclass.constructor.call(this);
12934     
12935     
12936 };
12937
12938 Roo.extend(Roo.View, Roo.util.Observable, {
12939     
12940      /**
12941      * @cfg {Roo.data.Store} store Data store to load data from.
12942      */
12943     store : false,
12944     
12945     /**
12946      * @cfg {String|Roo.Element} el The container element.
12947      */
12948     el : '',
12949     
12950     /**
12951      * @cfg {String|Roo.Template} tpl The template used by this View 
12952      */
12953     tpl : false,
12954     /**
12955      * @cfg {String} dataName the named area of the template to use as the data area
12956      *                          Works with domtemplates roo-name="name"
12957      */
12958     dataName: false,
12959     /**
12960      * @cfg {String} selectedClass The css class to add to selected nodes
12961      */
12962     selectedClass : "x-view-selected",
12963      /**
12964      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12965      */
12966     emptyText : "",
12967     
12968     /**
12969      * @cfg {String} text to display on mask (default Loading)
12970      */
12971     mask : false,
12972     /**
12973      * @cfg {Boolean} multiSelect Allow multiple selection
12974      */
12975     multiSelect : false,
12976     /**
12977      * @cfg {Boolean} singleSelect Allow single selection
12978      */
12979     singleSelect:  false,
12980     
12981     /**
12982      * @cfg {Boolean} toggleSelect - selecting 
12983      */
12984     toggleSelect : false,
12985     
12986     /**
12987      * @cfg {Boolean} tickable - selecting 
12988      */
12989     tickable : false,
12990     
12991     /**
12992      * Returns the element this view is bound to.
12993      * @return {Roo.Element}
12994      */
12995     getEl : function(){
12996         return this.wrapEl;
12997     },
12998     
12999     
13000
13001     /**
13002      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13003      */
13004     refresh : function(){
13005         //Roo.log('refresh');
13006         var t = this.tpl;
13007         
13008         // if we are using something like 'domtemplate', then
13009         // the what gets used is:
13010         // t.applySubtemplate(NAME, data, wrapping data..)
13011         // the outer template then get' applied with
13012         //     the store 'extra data'
13013         // and the body get's added to the
13014         //      roo-name="data" node?
13015         //      <span class='roo-tpl-{name}'></span> ?????
13016         
13017         
13018         
13019         this.clearSelections();
13020         this.el.update("");
13021         var html = [];
13022         var records = this.store.getRange();
13023         if(records.length < 1) {
13024             
13025             // is this valid??  = should it render a template??
13026             
13027             this.el.update(this.emptyText);
13028             return;
13029         }
13030         var el = this.el;
13031         if (this.dataName) {
13032             this.el.update(t.apply(this.store.meta)); //????
13033             el = this.el.child('.roo-tpl-' + this.dataName);
13034         }
13035         
13036         for(var i = 0, len = records.length; i < len; i++){
13037             var data = this.prepareData(records[i].data, i, records[i]);
13038             this.fireEvent("preparedata", this, data, i, records[i]);
13039             
13040             var d = Roo.apply({}, data);
13041             
13042             if(this.tickable){
13043                 Roo.apply(d, {'roo-id' : Roo.id()});
13044                 
13045                 var _this = this;
13046             
13047                 Roo.each(this.parent.item, function(item){
13048                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13049                         return;
13050                     }
13051                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13052                 });
13053             }
13054             
13055             html[html.length] = Roo.util.Format.trim(
13056                 this.dataName ?
13057                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13058                     t.apply(d)
13059             );
13060         }
13061         
13062         
13063         
13064         el.update(html.join(""));
13065         this.nodes = el.dom.childNodes;
13066         this.updateIndexes(0);
13067     },
13068     
13069
13070     /**
13071      * Function to override to reformat the data that is sent to
13072      * the template for each node.
13073      * DEPRICATED - use the preparedata event handler.
13074      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13075      * a JSON object for an UpdateManager bound view).
13076      */
13077     prepareData : function(data, index, record)
13078     {
13079         this.fireEvent("preparedata", this, data, index, record);
13080         return data;
13081     },
13082
13083     onUpdate : function(ds, record){
13084         // Roo.log('on update');   
13085         this.clearSelections();
13086         var index = this.store.indexOf(record);
13087         var n = this.nodes[index];
13088         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13089         n.parentNode.removeChild(n);
13090         this.updateIndexes(index, index);
13091     },
13092
13093     
13094     
13095 // --------- FIXME     
13096     onAdd : function(ds, records, index)
13097     {
13098         //Roo.log(['on Add', ds, records, index] );        
13099         this.clearSelections();
13100         if(this.nodes.length == 0){
13101             this.refresh();
13102             return;
13103         }
13104         var n = this.nodes[index];
13105         for(var i = 0, len = records.length; i < len; i++){
13106             var d = this.prepareData(records[i].data, i, records[i]);
13107             if(n){
13108                 this.tpl.insertBefore(n, d);
13109             }else{
13110                 
13111                 this.tpl.append(this.el, d);
13112             }
13113         }
13114         this.updateIndexes(index);
13115     },
13116
13117     onRemove : function(ds, record, index){
13118        // Roo.log('onRemove');
13119         this.clearSelections();
13120         var el = this.dataName  ?
13121             this.el.child('.roo-tpl-' + this.dataName) :
13122             this.el; 
13123         
13124         el.dom.removeChild(this.nodes[index]);
13125         this.updateIndexes(index);
13126     },
13127
13128     /**
13129      * Refresh an individual node.
13130      * @param {Number} index
13131      */
13132     refreshNode : function(index){
13133         this.onUpdate(this.store, this.store.getAt(index));
13134     },
13135
13136     updateIndexes : function(startIndex, endIndex){
13137         var ns = this.nodes;
13138         startIndex = startIndex || 0;
13139         endIndex = endIndex || ns.length - 1;
13140         for(var i = startIndex; i <= endIndex; i++){
13141             ns[i].nodeIndex = i;
13142         }
13143     },
13144
13145     /**
13146      * Changes the data store this view uses and refresh the view.
13147      * @param {Store} store
13148      */
13149     setStore : function(store, initial){
13150         if(!initial && this.store){
13151             this.store.un("datachanged", this.refresh);
13152             this.store.un("add", this.onAdd);
13153             this.store.un("remove", this.onRemove);
13154             this.store.un("update", this.onUpdate);
13155             this.store.un("clear", this.refresh);
13156             this.store.un("beforeload", this.onBeforeLoad);
13157             this.store.un("load", this.onLoad);
13158             this.store.un("loadexception", this.onLoad);
13159         }
13160         if(store){
13161           
13162             store.on("datachanged", this.refresh, this);
13163             store.on("add", this.onAdd, this);
13164             store.on("remove", this.onRemove, this);
13165             store.on("update", this.onUpdate, this);
13166             store.on("clear", this.refresh, this);
13167             store.on("beforeload", this.onBeforeLoad, this);
13168             store.on("load", this.onLoad, this);
13169             store.on("loadexception", this.onLoad, this);
13170         }
13171         
13172         if(store){
13173             this.refresh();
13174         }
13175     },
13176     /**
13177      * onbeforeLoad - masks the loading area.
13178      *
13179      */
13180     onBeforeLoad : function(store,opts)
13181     {
13182          //Roo.log('onBeforeLoad');   
13183         if (!opts.add) {
13184             this.el.update("");
13185         }
13186         this.el.mask(this.mask ? this.mask : "Loading" ); 
13187     },
13188     onLoad : function ()
13189     {
13190         this.el.unmask();
13191     },
13192     
13193
13194     /**
13195      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13196      * @param {HTMLElement} node
13197      * @return {HTMLElement} The template node
13198      */
13199     findItemFromChild : function(node){
13200         var el = this.dataName  ?
13201             this.el.child('.roo-tpl-' + this.dataName,true) :
13202             this.el.dom; 
13203         
13204         if(!node || node.parentNode == el){
13205                     return node;
13206             }
13207             var p = node.parentNode;
13208             while(p && p != el){
13209             if(p.parentNode == el){
13210                 return p;
13211             }
13212             p = p.parentNode;
13213         }
13214             return null;
13215     },
13216
13217     /** @ignore */
13218     onClick : function(e){
13219         var item = this.findItemFromChild(e.getTarget());
13220         if(item){
13221             var index = this.indexOf(item);
13222             if(this.onItemClick(item, index, e) !== false){
13223                 this.fireEvent("click", this, index, item, e);
13224             }
13225         }else{
13226             this.clearSelections();
13227         }
13228     },
13229
13230     /** @ignore */
13231     onContextMenu : function(e){
13232         var item = this.findItemFromChild(e.getTarget());
13233         if(item){
13234             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13235         }
13236     },
13237
13238     /** @ignore */
13239     onDblClick : function(e){
13240         var item = this.findItemFromChild(e.getTarget());
13241         if(item){
13242             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13243         }
13244     },
13245
13246     onItemClick : function(item, index, e)
13247     {
13248         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13249             return false;
13250         }
13251         if (this.toggleSelect) {
13252             var m = this.isSelected(item) ? 'unselect' : 'select';
13253             //Roo.log(m);
13254             var _t = this;
13255             _t[m](item, true, false);
13256             return true;
13257         }
13258         if(this.multiSelect || this.singleSelect){
13259             if(this.multiSelect && e.shiftKey && this.lastSelection){
13260                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13261             }else{
13262                 this.select(item, this.multiSelect && e.ctrlKey);
13263                 this.lastSelection = item;
13264             }
13265             
13266             if(!this.tickable){
13267                 e.preventDefault();
13268             }
13269             
13270         }
13271         return true;
13272     },
13273
13274     /**
13275      * Get the number of selected nodes.
13276      * @return {Number}
13277      */
13278     getSelectionCount : function(){
13279         return this.selections.length;
13280     },
13281
13282     /**
13283      * Get the currently selected nodes.
13284      * @return {Array} An array of HTMLElements
13285      */
13286     getSelectedNodes : function(){
13287         return this.selections;
13288     },
13289
13290     /**
13291      * Get the indexes of the selected nodes.
13292      * @return {Array}
13293      */
13294     getSelectedIndexes : function(){
13295         var indexes = [], s = this.selections;
13296         for(var i = 0, len = s.length; i < len; i++){
13297             indexes.push(s[i].nodeIndex);
13298         }
13299         return indexes;
13300     },
13301
13302     /**
13303      * Clear all selections
13304      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13305      */
13306     clearSelections : function(suppressEvent){
13307         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13308             this.cmp.elements = this.selections;
13309             this.cmp.removeClass(this.selectedClass);
13310             this.selections = [];
13311             if(!suppressEvent){
13312                 this.fireEvent("selectionchange", this, this.selections);
13313             }
13314         }
13315     },
13316
13317     /**
13318      * Returns true if the passed node is selected
13319      * @param {HTMLElement/Number} node The node or node index
13320      * @return {Boolean}
13321      */
13322     isSelected : function(node){
13323         var s = this.selections;
13324         if(s.length < 1){
13325             return false;
13326         }
13327         node = this.getNode(node);
13328         return s.indexOf(node) !== -1;
13329     },
13330
13331     /**
13332      * Selects nodes.
13333      * @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
13334      * @param {Boolean} keepExisting (optional) true to keep existing selections
13335      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13336      */
13337     select : function(nodeInfo, keepExisting, suppressEvent){
13338         if(nodeInfo instanceof Array){
13339             if(!keepExisting){
13340                 this.clearSelections(true);
13341             }
13342             for(var i = 0, len = nodeInfo.length; i < len; i++){
13343                 this.select(nodeInfo[i], true, true);
13344             }
13345             return;
13346         } 
13347         var node = this.getNode(nodeInfo);
13348         if(!node || this.isSelected(node)){
13349             return; // already selected.
13350         }
13351         if(!keepExisting){
13352             this.clearSelections(true);
13353         }
13354         
13355         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13356             Roo.fly(node).addClass(this.selectedClass);
13357             this.selections.push(node);
13358             if(!suppressEvent){
13359                 this.fireEvent("selectionchange", this, this.selections);
13360             }
13361         }
13362         
13363         
13364     },
13365       /**
13366      * Unselects nodes.
13367      * @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
13368      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13369      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13370      */
13371     unselect : function(nodeInfo, keepExisting, suppressEvent)
13372     {
13373         if(nodeInfo instanceof Array){
13374             Roo.each(this.selections, function(s) {
13375                 this.unselect(s, nodeInfo);
13376             }, this);
13377             return;
13378         }
13379         var node = this.getNode(nodeInfo);
13380         if(!node || !this.isSelected(node)){
13381             //Roo.log("not selected");
13382             return; // not selected.
13383         }
13384         // fireevent???
13385         var ns = [];
13386         Roo.each(this.selections, function(s) {
13387             if (s == node ) {
13388                 Roo.fly(node).removeClass(this.selectedClass);
13389
13390                 return;
13391             }
13392             ns.push(s);
13393         },this);
13394         
13395         this.selections= ns;
13396         this.fireEvent("selectionchange", this, this.selections);
13397     },
13398
13399     /**
13400      * Gets a template node.
13401      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13402      * @return {HTMLElement} The node or null if it wasn't found
13403      */
13404     getNode : function(nodeInfo){
13405         if(typeof nodeInfo == "string"){
13406             return document.getElementById(nodeInfo);
13407         }else if(typeof nodeInfo == "number"){
13408             return this.nodes[nodeInfo];
13409         }
13410         return nodeInfo;
13411     },
13412
13413     /**
13414      * Gets a range template nodes.
13415      * @param {Number} startIndex
13416      * @param {Number} endIndex
13417      * @return {Array} An array of nodes
13418      */
13419     getNodes : function(start, end){
13420         var ns = this.nodes;
13421         start = start || 0;
13422         end = typeof end == "undefined" ? ns.length - 1 : end;
13423         var nodes = [];
13424         if(start <= end){
13425             for(var i = start; i <= end; i++){
13426                 nodes.push(ns[i]);
13427             }
13428         } else{
13429             for(var i = start; i >= end; i--){
13430                 nodes.push(ns[i]);
13431             }
13432         }
13433         return nodes;
13434     },
13435
13436     /**
13437      * Finds the index of the passed node
13438      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13439      * @return {Number} The index of the node or -1
13440      */
13441     indexOf : function(node){
13442         node = this.getNode(node);
13443         if(typeof node.nodeIndex == "number"){
13444             return node.nodeIndex;
13445         }
13446         var ns = this.nodes;
13447         for(var i = 0, len = ns.length; i < len; i++){
13448             if(ns[i] == node){
13449                 return i;
13450             }
13451         }
13452         return -1;
13453     }
13454 });
13455 /*
13456  * - LGPL
13457  *
13458  * based on jquery fullcalendar
13459  * 
13460  */
13461
13462 Roo.bootstrap = Roo.bootstrap || {};
13463 /**
13464  * @class Roo.bootstrap.Calendar
13465  * @extends Roo.bootstrap.Component
13466  * Bootstrap Calendar class
13467  * @cfg {Boolean} loadMask (true|false) default false
13468  * @cfg {Object} header generate the user specific header of the calendar, default false
13469
13470  * @constructor
13471  * Create a new Container
13472  * @param {Object} config The config object
13473  */
13474
13475
13476
13477 Roo.bootstrap.Calendar = function(config){
13478     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13479      this.addEvents({
13480         /**
13481              * @event select
13482              * Fires when a date is selected
13483              * @param {DatePicker} this
13484              * @param {Date} date The selected date
13485              */
13486         'select': true,
13487         /**
13488              * @event monthchange
13489              * Fires when the displayed month changes 
13490              * @param {DatePicker} this
13491              * @param {Date} date The selected month
13492              */
13493         'monthchange': true,
13494         /**
13495              * @event evententer
13496              * Fires when mouse over an event
13497              * @param {Calendar} this
13498              * @param {event} Event
13499              */
13500         'evententer': true,
13501         /**
13502              * @event eventleave
13503              * Fires when the mouse leaves an
13504              * @param {Calendar} this
13505              * @param {event}
13506              */
13507         'eventleave': true,
13508         /**
13509              * @event eventclick
13510              * Fires when the mouse click an
13511              * @param {Calendar} this
13512              * @param {event}
13513              */
13514         'eventclick': true
13515         
13516     });
13517
13518 };
13519
13520 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13521     
13522      /**
13523      * @cfg {Number} startDay
13524      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13525      */
13526     startDay : 0,
13527     
13528     loadMask : false,
13529     
13530     header : false,
13531       
13532     getAutoCreate : function(){
13533         
13534         
13535         var fc_button = function(name, corner, style, content ) {
13536             return Roo.apply({},{
13537                 tag : 'span',
13538                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13539                          (corner.length ?
13540                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13541                             ''
13542                         ),
13543                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13544                 unselectable: 'on'
13545             });
13546         };
13547         
13548         var header = {};
13549         
13550         if(!this.header){
13551             header = {
13552                 tag : 'table',
13553                 cls : 'fc-header',
13554                 style : 'width:100%',
13555                 cn : [
13556                     {
13557                         tag: 'tr',
13558                         cn : [
13559                             {
13560                                 tag : 'td',
13561                                 cls : 'fc-header-left',
13562                                 cn : [
13563                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13564                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13565                                     { tag: 'span', cls: 'fc-header-space' },
13566                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13567
13568
13569                                 ]
13570                             },
13571
13572                             {
13573                                 tag : 'td',
13574                                 cls : 'fc-header-center',
13575                                 cn : [
13576                                     {
13577                                         tag: 'span',
13578                                         cls: 'fc-header-title',
13579                                         cn : {
13580                                             tag: 'H2',
13581                                             html : 'month / year'
13582                                         }
13583                                     }
13584
13585                                 ]
13586                             },
13587                             {
13588                                 tag : 'td',
13589                                 cls : 'fc-header-right',
13590                                 cn : [
13591                               /*      fc_button('month', 'left', '', 'month' ),
13592                                     fc_button('week', '', '', 'week' ),
13593                                     fc_button('day', 'right', '', 'day' )
13594                                 */    
13595
13596                                 ]
13597                             }
13598
13599                         ]
13600                     }
13601                 ]
13602             };
13603         }
13604         
13605         header = this.header;
13606         
13607        
13608         var cal_heads = function() {
13609             var ret = [];
13610             // fixme - handle this.
13611             
13612             for (var i =0; i < Date.dayNames.length; i++) {
13613                 var d = Date.dayNames[i];
13614                 ret.push({
13615                     tag: 'th',
13616                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13617                     html : d.substring(0,3)
13618                 });
13619                 
13620             }
13621             ret[0].cls += ' fc-first';
13622             ret[6].cls += ' fc-last';
13623             return ret;
13624         };
13625         var cal_cell = function(n) {
13626             return  {
13627                 tag: 'td',
13628                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13629                 cn : [
13630                     {
13631                         cn : [
13632                             {
13633                                 cls: 'fc-day-number',
13634                                 html: 'D'
13635                             },
13636                             {
13637                                 cls: 'fc-day-content',
13638                              
13639                                 cn : [
13640                                      {
13641                                         style: 'position: relative;' // height: 17px;
13642                                     }
13643                                 ]
13644                             }
13645                             
13646                             
13647                         ]
13648                     }
13649                 ]
13650                 
13651             }
13652         };
13653         var cal_rows = function() {
13654             
13655             var ret = [];
13656             for (var r = 0; r < 6; r++) {
13657                 var row= {
13658                     tag : 'tr',
13659                     cls : 'fc-week',
13660                     cn : []
13661                 };
13662                 
13663                 for (var i =0; i < Date.dayNames.length; i++) {
13664                     var d = Date.dayNames[i];
13665                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13666
13667                 }
13668                 row.cn[0].cls+=' fc-first';
13669                 row.cn[0].cn[0].style = 'min-height:90px';
13670                 row.cn[6].cls+=' fc-last';
13671                 ret.push(row);
13672                 
13673             }
13674             ret[0].cls += ' fc-first';
13675             ret[4].cls += ' fc-prev-last';
13676             ret[5].cls += ' fc-last';
13677             return ret;
13678             
13679         };
13680         
13681         var cal_table = {
13682             tag: 'table',
13683             cls: 'fc-border-separate',
13684             style : 'width:100%',
13685             cellspacing  : 0,
13686             cn : [
13687                 { 
13688                     tag: 'thead',
13689                     cn : [
13690                         { 
13691                             tag: 'tr',
13692                             cls : 'fc-first fc-last',
13693                             cn : cal_heads()
13694                         }
13695                     ]
13696                 },
13697                 { 
13698                     tag: 'tbody',
13699                     cn : cal_rows()
13700                 }
13701                   
13702             ]
13703         };
13704          
13705          var cfg = {
13706             cls : 'fc fc-ltr',
13707             cn : [
13708                 header,
13709                 {
13710                     cls : 'fc-content',
13711                     style : "position: relative;",
13712                     cn : [
13713                         {
13714                             cls : 'fc-view fc-view-month fc-grid',
13715                             style : 'position: relative',
13716                             unselectable : 'on',
13717                             cn : [
13718                                 {
13719                                     cls : 'fc-event-container',
13720                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13721                                 },
13722                                 cal_table
13723                             ]
13724                         }
13725                     ]
13726     
13727                 }
13728            ] 
13729             
13730         };
13731         
13732          
13733         
13734         return cfg;
13735     },
13736     
13737     
13738     initEvents : function()
13739     {
13740         if(!this.store){
13741             throw "can not find store for calendar";
13742         }
13743         
13744         var mark = {
13745             tag: "div",
13746             cls:"x-dlg-mask",
13747             style: "text-align:center",
13748             cn: [
13749                 {
13750                     tag: "div",
13751                     style: "background-color:white;width:50%;margin:250 auto",
13752                     cn: [
13753                         {
13754                             tag: "img",
13755                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13756                         },
13757                         {
13758                             tag: "span",
13759                             html: "Loading"
13760                         }
13761                         
13762                     ]
13763                 }
13764             ]
13765         }
13766         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13767         
13768         var size = this.el.select('.fc-content', true).first().getSize();
13769         this.maskEl.setSize(size.width, size.height);
13770         this.maskEl.enableDisplayMode("block");
13771         if(!this.loadMask){
13772             this.maskEl.hide();
13773         }
13774         
13775         this.store = Roo.factory(this.store, Roo.data);
13776         this.store.on('load', this.onLoad, this);
13777         this.store.on('beforeload', this.onBeforeLoad, this);
13778         
13779         this.resize();
13780         
13781         this.cells = this.el.select('.fc-day',true);
13782         //Roo.log(this.cells);
13783         this.textNodes = this.el.query('.fc-day-number');
13784         this.cells.addClassOnOver('fc-state-hover');
13785         
13786         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13787         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13788         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13789         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13790         
13791         this.on('monthchange', this.onMonthChange, this);
13792         
13793         this.update(new Date().clearTime());
13794     },
13795     
13796     resize : function() {
13797         var sz  = this.el.getSize();
13798         
13799         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13800         this.el.select('.fc-day-content div',true).setHeight(34);
13801     },
13802     
13803     
13804     // private
13805     showPrevMonth : function(e){
13806         this.update(this.activeDate.add("mo", -1));
13807     },
13808     showToday : function(e){
13809         this.update(new Date().clearTime());
13810     },
13811     // private
13812     showNextMonth : function(e){
13813         this.update(this.activeDate.add("mo", 1));
13814     },
13815
13816     // private
13817     showPrevYear : function(){
13818         this.update(this.activeDate.add("y", -1));
13819     },
13820
13821     // private
13822     showNextYear : function(){
13823         this.update(this.activeDate.add("y", 1));
13824     },
13825
13826     
13827    // private
13828     update : function(date)
13829     {
13830         var vd = this.activeDate;
13831         this.activeDate = date;
13832 //        if(vd && this.el){
13833 //            var t = date.getTime();
13834 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13835 //                Roo.log('using add remove');
13836 //                
13837 //                this.fireEvent('monthchange', this, date);
13838 //                
13839 //                this.cells.removeClass("fc-state-highlight");
13840 //                this.cells.each(function(c){
13841 //                   if(c.dateValue == t){
13842 //                       c.addClass("fc-state-highlight");
13843 //                       setTimeout(function(){
13844 //                            try{c.dom.firstChild.focus();}catch(e){}
13845 //                       }, 50);
13846 //                       return false;
13847 //                   }
13848 //                   return true;
13849 //                });
13850 //                return;
13851 //            }
13852 //        }
13853         
13854         var days = date.getDaysInMonth();
13855         
13856         var firstOfMonth = date.getFirstDateOfMonth();
13857         var startingPos = firstOfMonth.getDay()-this.startDay;
13858         
13859         if(startingPos < this.startDay){
13860             startingPos += 7;
13861         }
13862         
13863         var pm = date.add(Date.MONTH, -1);
13864         var prevStart = pm.getDaysInMonth()-startingPos;
13865 //        
13866         this.cells = this.el.select('.fc-day',true);
13867         this.textNodes = this.el.query('.fc-day-number');
13868         this.cells.addClassOnOver('fc-state-hover');
13869         
13870         var cells = this.cells.elements;
13871         var textEls = this.textNodes;
13872         
13873         Roo.each(cells, function(cell){
13874             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13875         });
13876         
13877         days += startingPos;
13878
13879         // convert everything to numbers so it's fast
13880         var day = 86400000;
13881         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13882         //Roo.log(d);
13883         //Roo.log(pm);
13884         //Roo.log(prevStart);
13885         
13886         var today = new Date().clearTime().getTime();
13887         var sel = date.clearTime().getTime();
13888         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13889         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13890         var ddMatch = this.disabledDatesRE;
13891         var ddText = this.disabledDatesText;
13892         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13893         var ddaysText = this.disabledDaysText;
13894         var format = this.format;
13895         
13896         var setCellClass = function(cal, cell){
13897             cell.row = 0;
13898             cell.events = [];
13899             cell.more = [];
13900             //Roo.log('set Cell Class');
13901             cell.title = "";
13902             var t = d.getTime();
13903             
13904             //Roo.log(d);
13905             
13906             cell.dateValue = t;
13907             if(t == today){
13908                 cell.className += " fc-today";
13909                 cell.className += " fc-state-highlight";
13910                 cell.title = cal.todayText;
13911             }
13912             if(t == sel){
13913                 // disable highlight in other month..
13914                 //cell.className += " fc-state-highlight";
13915                 
13916             }
13917             // disabling
13918             if(t < min) {
13919                 cell.className = " fc-state-disabled";
13920                 cell.title = cal.minText;
13921                 return;
13922             }
13923             if(t > max) {
13924                 cell.className = " fc-state-disabled";
13925                 cell.title = cal.maxText;
13926                 return;
13927             }
13928             if(ddays){
13929                 if(ddays.indexOf(d.getDay()) != -1){
13930                     cell.title = ddaysText;
13931                     cell.className = " fc-state-disabled";
13932                 }
13933             }
13934             if(ddMatch && format){
13935                 var fvalue = d.dateFormat(format);
13936                 if(ddMatch.test(fvalue)){
13937                     cell.title = ddText.replace("%0", fvalue);
13938                     cell.className = " fc-state-disabled";
13939                 }
13940             }
13941             
13942             if (!cell.initialClassName) {
13943                 cell.initialClassName = cell.dom.className;
13944             }
13945             
13946             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13947         };
13948
13949         var i = 0;
13950         
13951         for(; i < startingPos; i++) {
13952             textEls[i].innerHTML = (++prevStart);
13953             d.setDate(d.getDate()+1);
13954             
13955             cells[i].className = "fc-past fc-other-month";
13956             setCellClass(this, cells[i]);
13957         }
13958         
13959         var intDay = 0;
13960         
13961         for(; i < days; i++){
13962             intDay = i - startingPos + 1;
13963             textEls[i].innerHTML = (intDay);
13964             d.setDate(d.getDate()+1);
13965             
13966             cells[i].className = ''; // "x-date-active";
13967             setCellClass(this, cells[i]);
13968         }
13969         var extraDays = 0;
13970         
13971         for(; i < 42; i++) {
13972             textEls[i].innerHTML = (++extraDays);
13973             d.setDate(d.getDate()+1);
13974             
13975             cells[i].className = "fc-future fc-other-month";
13976             setCellClass(this, cells[i]);
13977         }
13978         
13979         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13980         
13981         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13982         
13983         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13984         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13985         
13986         if(totalRows != 6){
13987             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13988             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13989         }
13990         
13991         this.fireEvent('monthchange', this, date);
13992         
13993         
13994         /*
13995         if(!this.internalRender){
13996             var main = this.el.dom.firstChild;
13997             var w = main.offsetWidth;
13998             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13999             Roo.fly(main).setWidth(w);
14000             this.internalRender = true;
14001             // opera does not respect the auto grow header center column
14002             // then, after it gets a width opera refuses to recalculate
14003             // without a second pass
14004             if(Roo.isOpera && !this.secondPass){
14005                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14006                 this.secondPass = true;
14007                 this.update.defer(10, this, [date]);
14008             }
14009         }
14010         */
14011         
14012     },
14013     
14014     findCell : function(dt) {
14015         dt = dt.clearTime().getTime();
14016         var ret = false;
14017         this.cells.each(function(c){
14018             //Roo.log("check " +c.dateValue + '?=' + dt);
14019             if(c.dateValue == dt){
14020                 ret = c;
14021                 return false;
14022             }
14023             return true;
14024         });
14025         
14026         return ret;
14027     },
14028     
14029     findCells : function(ev) {
14030         var s = ev.start.clone().clearTime().getTime();
14031        // Roo.log(s);
14032         var e= ev.end.clone().clearTime().getTime();
14033        // Roo.log(e);
14034         var ret = [];
14035         this.cells.each(function(c){
14036              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14037             
14038             if(c.dateValue > e){
14039                 return ;
14040             }
14041             if(c.dateValue < s){
14042                 return ;
14043             }
14044             ret.push(c);
14045         });
14046         
14047         return ret;    
14048     },
14049     
14050 //    findBestRow: function(cells)
14051 //    {
14052 //        var ret = 0;
14053 //        
14054 //        for (var i =0 ; i < cells.length;i++) {
14055 //            ret  = Math.max(cells[i].rows || 0,ret);
14056 //        }
14057 //        return ret;
14058 //        
14059 //    },
14060     
14061     
14062     addItem : function(ev)
14063     {
14064         // look for vertical location slot in
14065         var cells = this.findCells(ev);
14066         
14067 //        ev.row = this.findBestRow(cells);
14068         
14069         // work out the location.
14070         
14071         var crow = false;
14072         var rows = [];
14073         for(var i =0; i < cells.length; i++) {
14074             
14075             cells[i].row = cells[0].row;
14076             
14077             if(i == 0){
14078                 cells[i].row = cells[i].row + 1;
14079             }
14080             
14081             if (!crow) {
14082                 crow = {
14083                     start : cells[i],
14084                     end :  cells[i]
14085                 };
14086                 continue;
14087             }
14088             if (crow.start.getY() == cells[i].getY()) {
14089                 // on same row.
14090                 crow.end = cells[i];
14091                 continue;
14092             }
14093             // different row.
14094             rows.push(crow);
14095             crow = {
14096                 start: cells[i],
14097                 end : cells[i]
14098             };
14099             
14100         }
14101         
14102         rows.push(crow);
14103         ev.els = [];
14104         ev.rows = rows;
14105         ev.cells = cells;
14106         
14107         cells[0].events.push(ev);
14108         
14109         this.calevents.push(ev);
14110     },
14111     
14112     clearEvents: function() {
14113         
14114         if(!this.calevents){
14115             return;
14116         }
14117         
14118         Roo.each(this.cells.elements, function(c){
14119             c.row = 0;
14120             c.events = [];
14121             c.more = [];
14122         });
14123         
14124         Roo.each(this.calevents, function(e) {
14125             Roo.each(e.els, function(el) {
14126                 el.un('mouseenter' ,this.onEventEnter, this);
14127                 el.un('mouseleave' ,this.onEventLeave, this);
14128                 el.remove();
14129             },this);
14130         },this);
14131         
14132         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14133             e.remove();
14134         });
14135         
14136     },
14137     
14138     renderEvents: function()
14139     {   
14140         var _this = this;
14141         
14142         this.cells.each(function(c) {
14143             
14144             if(c.row < 5){
14145                 return;
14146             }
14147             
14148             var ev = c.events;
14149             
14150             var r = 4;
14151             if(c.row != c.events.length){
14152                 r = 4 - (4 - (c.row - c.events.length));
14153             }
14154             
14155             c.events = ev.slice(0, r);
14156             c.more = ev.slice(r);
14157             
14158             if(c.more.length && c.more.length == 1){
14159                 c.events.push(c.more.pop());
14160             }
14161             
14162             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14163             
14164         });
14165             
14166         this.cells.each(function(c) {
14167             
14168             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14169             
14170             
14171             for (var e = 0; e < c.events.length; e++){
14172                 var ev = c.events[e];
14173                 var rows = ev.rows;
14174                 
14175                 for(var i = 0; i < rows.length; i++) {
14176                 
14177                     // how many rows should it span..
14178
14179                     var  cfg = {
14180                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14181                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14182
14183                         unselectable : "on",
14184                         cn : [
14185                             {
14186                                 cls: 'fc-event-inner',
14187                                 cn : [
14188     //                                {
14189     //                                  tag:'span',
14190     //                                  cls: 'fc-event-time',
14191     //                                  html : cells.length > 1 ? '' : ev.time
14192     //                                },
14193                                     {
14194                                       tag:'span',
14195                                       cls: 'fc-event-title',
14196                                       html : String.format('{0}', ev.title)
14197                                     }
14198
14199
14200                                 ]
14201                             },
14202                             {
14203                                 cls: 'ui-resizable-handle ui-resizable-e',
14204                                 html : '&nbsp;&nbsp;&nbsp'
14205                             }
14206
14207                         ]
14208                     };
14209
14210                     if (i == 0) {
14211                         cfg.cls += ' fc-event-start';
14212                     }
14213                     if ((i+1) == rows.length) {
14214                         cfg.cls += ' fc-event-end';
14215                     }
14216
14217                     var ctr = _this.el.select('.fc-event-container',true).first();
14218                     var cg = ctr.createChild(cfg);
14219
14220                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14221                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14222
14223                     var r = (c.more.length) ? 1 : 0;
14224                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14225                     cg.setWidth(ebox.right - sbox.x -2);
14226
14227                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14228                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14229                     cg.on('click', _this.onEventClick, _this, ev);
14230
14231                     ev.els.push(cg);
14232                     
14233                 }
14234                 
14235             }
14236             
14237             
14238             if(c.more.length){
14239                 var  cfg = {
14240                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14241                     style : 'position: absolute',
14242                     unselectable : "on",
14243                     cn : [
14244                         {
14245                             cls: 'fc-event-inner',
14246                             cn : [
14247                                 {
14248                                   tag:'span',
14249                                   cls: 'fc-event-title',
14250                                   html : 'More'
14251                                 }
14252
14253
14254                             ]
14255                         },
14256                         {
14257                             cls: 'ui-resizable-handle ui-resizable-e',
14258                             html : '&nbsp;&nbsp;&nbsp'
14259                         }
14260
14261                     ]
14262                 };
14263
14264                 var ctr = _this.el.select('.fc-event-container',true).first();
14265                 var cg = ctr.createChild(cfg);
14266
14267                 var sbox = c.select('.fc-day-content',true).first().getBox();
14268                 var ebox = c.select('.fc-day-content',true).first().getBox();
14269                 //Roo.log(cg);
14270                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14271                 cg.setWidth(ebox.right - sbox.x -2);
14272
14273                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14274                 
14275             }
14276             
14277         });
14278         
14279         
14280         
14281     },
14282     
14283     onEventEnter: function (e, el,event,d) {
14284         this.fireEvent('evententer', this, el, event);
14285     },
14286     
14287     onEventLeave: function (e, el,event,d) {
14288         this.fireEvent('eventleave', this, el, event);
14289     },
14290     
14291     onEventClick: function (e, el,event,d) {
14292         this.fireEvent('eventclick', this, el, event);
14293     },
14294     
14295     onMonthChange: function () {
14296         this.store.load();
14297     },
14298     
14299     onMoreEventClick: function(e, el, more)
14300     {
14301         var _this = this;
14302         
14303         this.calpopover.placement = 'right';
14304         this.calpopover.setTitle('More');
14305         
14306         this.calpopover.setContent('');
14307         
14308         var ctr = this.calpopover.el.select('.popover-content', true).first();
14309         
14310         Roo.each(more, function(m){
14311             var cfg = {
14312                 cls : 'fc-event-hori fc-event-draggable',
14313                 html : m.title
14314             }
14315             var cg = ctr.createChild(cfg);
14316             
14317             cg.on('click', _this.onEventClick, _this, m);
14318         });
14319         
14320         this.calpopover.show(el);
14321         
14322         
14323     },
14324     
14325     onLoad: function () 
14326     {   
14327         this.calevents = [];
14328         var cal = this;
14329         
14330         if(this.store.getCount() > 0){
14331             this.store.data.each(function(d){
14332                cal.addItem({
14333                     id : d.data.id,
14334                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14335                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14336                     time : d.data.start_time,
14337                     title : d.data.title,
14338                     description : d.data.description,
14339                     venue : d.data.venue
14340                 });
14341             });
14342         }
14343         
14344         this.renderEvents();
14345         
14346         if(this.calevents.length && this.loadMask){
14347             this.maskEl.hide();
14348         }
14349     },
14350     
14351     onBeforeLoad: function()
14352     {
14353         this.clearEvents();
14354         if(this.loadMask){
14355             this.maskEl.show();
14356         }
14357     }
14358 });
14359
14360  
14361  /*
14362  * - LGPL
14363  *
14364  * element
14365  * 
14366  */
14367
14368 /**
14369  * @class Roo.bootstrap.Popover
14370  * @extends Roo.bootstrap.Component
14371  * Bootstrap Popover class
14372  * @cfg {String} html contents of the popover   (or false to use children..)
14373  * @cfg {String} title of popover (or false to hide)
14374  * @cfg {String} placement how it is placed
14375  * @cfg {String} trigger click || hover (or false to trigger manually)
14376  * @cfg {String} over what (parent or false to trigger manually.)
14377  * @cfg {Number} delay - delay before showing
14378  
14379  * @constructor
14380  * Create a new Popover
14381  * @param {Object} config The config object
14382  */
14383
14384 Roo.bootstrap.Popover = function(config){
14385     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14386 };
14387
14388 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14389     
14390     title: 'Fill in a title',
14391     html: false,
14392     
14393     placement : 'right',
14394     trigger : 'hover', // hover
14395     
14396     delay : 0,
14397     
14398     over: 'parent',
14399     
14400     can_build_overlaid : false,
14401     
14402     getChildContainer : function()
14403     {
14404         return this.el.select('.popover-content',true).first();
14405     },
14406     
14407     getAutoCreate : function(){
14408          Roo.log('make popover?');
14409         var cfg = {
14410            cls : 'popover roo-dynamic',
14411            style: 'display:block',
14412            cn : [
14413                 {
14414                     cls : 'arrow'
14415                 },
14416                 {
14417                     cls : 'popover-inner',
14418                     cn : [
14419                         {
14420                             tag: 'h3',
14421                             cls: 'popover-title',
14422                             html : this.title
14423                         },
14424                         {
14425                             cls : 'popover-content',
14426                             html : this.html
14427                         }
14428                     ]
14429                     
14430                 }
14431            ]
14432         };
14433         
14434         return cfg;
14435     },
14436     setTitle: function(str)
14437     {
14438         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14439     },
14440     setContent: function(str)
14441     {
14442         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14443     },
14444     // as it get's added to the bottom of the page.
14445     onRender : function(ct, position)
14446     {
14447         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14448         if(!this.el){
14449             var cfg = Roo.apply({},  this.getAutoCreate());
14450             cfg.id = Roo.id();
14451             
14452             if (this.cls) {
14453                 cfg.cls += ' ' + this.cls;
14454             }
14455             if (this.style) {
14456                 cfg.style = this.style;
14457             }
14458             Roo.log("adding to ")
14459             this.el = Roo.get(document.body).createChild(cfg, position);
14460             Roo.log(this.el);
14461         }
14462         this.initEvents();
14463     },
14464     
14465     initEvents : function()
14466     {
14467         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14468         this.el.enableDisplayMode('block');
14469         this.el.hide();
14470         if (this.over === false) {
14471             return; 
14472         }
14473         if (this.triggers === false) {
14474             return;
14475         }
14476         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14477         var triggers = this.trigger ? this.trigger.split(' ') : [];
14478         Roo.each(triggers, function(trigger) {
14479         
14480             if (trigger == 'click') {
14481                 on_el.on('click', this.toggle, this);
14482             } else if (trigger != 'manual') {
14483                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14484                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14485       
14486                 on_el.on(eventIn  ,this.enter, this);
14487                 on_el.on(eventOut, this.leave, this);
14488             }
14489         }, this);
14490         
14491     },
14492     
14493     
14494     // private
14495     timeout : null,
14496     hoverState : null,
14497     
14498     toggle : function () {
14499         this.hoverState == 'in' ? this.leave() : this.enter();
14500     },
14501     
14502     enter : function () {
14503        
14504     
14505         clearTimeout(this.timeout);
14506     
14507         this.hoverState = 'in';
14508     
14509         if (!this.delay || !this.delay.show) {
14510             this.show();
14511             return;
14512         }
14513         var _t = this;
14514         this.timeout = setTimeout(function () {
14515             if (_t.hoverState == 'in') {
14516                 _t.show();
14517             }
14518         }, this.delay.show)
14519     },
14520     leave : function() {
14521         clearTimeout(this.timeout);
14522     
14523         this.hoverState = 'out';
14524     
14525         if (!this.delay || !this.delay.hide) {
14526             this.hide();
14527             return;
14528         }
14529         var _t = this;
14530         this.timeout = setTimeout(function () {
14531             if (_t.hoverState == 'out') {
14532                 _t.hide();
14533             }
14534         }, this.delay.hide)
14535     },
14536     
14537     show : function (on_el)
14538     {
14539         if (!on_el) {
14540             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14541         }
14542         // set content.
14543         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14544         if (this.html !== false) {
14545             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14546         }
14547         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14548         if (!this.title.length) {
14549             this.el.select('.popover-title',true).hide();
14550         }
14551         
14552         var placement = typeof this.placement == 'function' ?
14553             this.placement.call(this, this.el, on_el) :
14554             this.placement;
14555             
14556         var autoToken = /\s?auto?\s?/i;
14557         var autoPlace = autoToken.test(placement);
14558         if (autoPlace) {
14559             placement = placement.replace(autoToken, '') || 'top';
14560         }
14561         
14562         //this.el.detach()
14563         //this.el.setXY([0,0]);
14564         this.el.show();
14565         this.el.dom.style.display='block';
14566         this.el.addClass(placement);
14567         
14568         //this.el.appendTo(on_el);
14569         
14570         var p = this.getPosition();
14571         var box = this.el.getBox();
14572         
14573         if (autoPlace) {
14574             // fixme..
14575         }
14576         var align = Roo.bootstrap.Popover.alignment[placement];
14577         this.el.alignTo(on_el, align[0],align[1]);
14578         //var arrow = this.el.select('.arrow',true).first();
14579         //arrow.set(align[2], 
14580         
14581         this.el.addClass('in');
14582         this.hoverState = null;
14583         
14584         if (this.el.hasClass('fade')) {
14585             // fade it?
14586         }
14587         
14588     },
14589     hide : function()
14590     {
14591         this.el.setXY([0,0]);
14592         this.el.removeClass('in');
14593         this.el.hide();
14594         
14595     }
14596     
14597 });
14598
14599 Roo.bootstrap.Popover.alignment = {
14600     'left' : ['r-l', [-10,0], 'right'],
14601     'right' : ['l-r', [10,0], 'left'],
14602     'bottom' : ['t-b', [0,10], 'top'],
14603     'top' : [ 'b-t', [0,-10], 'bottom']
14604 };
14605
14606  /*
14607  * - LGPL
14608  *
14609  * Progress
14610  * 
14611  */
14612
14613 /**
14614  * @class Roo.bootstrap.Progress
14615  * @extends Roo.bootstrap.Component
14616  * Bootstrap Progress class
14617  * @cfg {Boolean} striped striped of the progress bar
14618  * @cfg {Boolean} active animated of the progress bar
14619  * 
14620  * 
14621  * @constructor
14622  * Create a new Progress
14623  * @param {Object} config The config object
14624  */
14625
14626 Roo.bootstrap.Progress = function(config){
14627     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14628 };
14629
14630 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14631     
14632     striped : false,
14633     active: false,
14634     
14635     getAutoCreate : function(){
14636         var cfg = {
14637             tag: 'div',
14638             cls: 'progress'
14639         };
14640         
14641         
14642         if(this.striped){
14643             cfg.cls += ' progress-striped';
14644         }
14645       
14646         if(this.active){
14647             cfg.cls += ' active';
14648         }
14649         
14650         
14651         return cfg;
14652     }
14653    
14654 });
14655
14656  
14657
14658  /*
14659  * - LGPL
14660  *
14661  * ProgressBar
14662  * 
14663  */
14664
14665 /**
14666  * @class Roo.bootstrap.ProgressBar
14667  * @extends Roo.bootstrap.Component
14668  * Bootstrap ProgressBar class
14669  * @cfg {Number} aria_valuenow aria-value now
14670  * @cfg {Number} aria_valuemin aria-value min
14671  * @cfg {Number} aria_valuemax aria-value max
14672  * @cfg {String} label label for the progress bar
14673  * @cfg {String} panel (success | info | warning | danger )
14674  * @cfg {String} role role of the progress bar
14675  * @cfg {String} sr_only text
14676  * 
14677  * 
14678  * @constructor
14679  * Create a new ProgressBar
14680  * @param {Object} config The config object
14681  */
14682
14683 Roo.bootstrap.ProgressBar = function(config){
14684     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14685 };
14686
14687 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14688     
14689     aria_valuenow : 0,
14690     aria_valuemin : 0,
14691     aria_valuemax : 100,
14692     label : false,
14693     panel : false,
14694     role : false,
14695     sr_only: false,
14696     
14697     getAutoCreate : function()
14698     {
14699         
14700         var cfg = {
14701             tag: 'div',
14702             cls: 'progress-bar',
14703             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14704         };
14705         
14706         if(this.sr_only){
14707             cfg.cn = {
14708                 tag: 'span',
14709                 cls: 'sr-only',
14710                 html: this.sr_only
14711             }
14712         }
14713         
14714         if(this.role){
14715             cfg.role = this.role;
14716         }
14717         
14718         if(this.aria_valuenow){
14719             cfg['aria-valuenow'] = this.aria_valuenow;
14720         }
14721         
14722         if(this.aria_valuemin){
14723             cfg['aria-valuemin'] = this.aria_valuemin;
14724         }
14725         
14726         if(this.aria_valuemax){
14727             cfg['aria-valuemax'] = this.aria_valuemax;
14728         }
14729         
14730         if(this.label && !this.sr_only){
14731             cfg.html = this.label;
14732         }
14733         
14734         if(this.panel){
14735             cfg.cls += ' progress-bar-' + this.panel;
14736         }
14737         
14738         return cfg;
14739     },
14740     
14741     update : function(aria_valuenow)
14742     {
14743         this.aria_valuenow = aria_valuenow;
14744         
14745         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14746     }
14747    
14748 });
14749
14750  
14751
14752  /*
14753  * - LGPL
14754  *
14755  * column
14756  * 
14757  */
14758
14759 /**
14760  * @class Roo.bootstrap.TabGroup
14761  * @extends Roo.bootstrap.Column
14762  * Bootstrap Column class
14763  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14764  * @cfg {Boolean} carousel true to make the group behave like a carousel
14765  * @cfg {Number} bullets show the panel pointer.. default 0
14766  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14767  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14768  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14769  * 
14770  * @constructor
14771  * Create a new TabGroup
14772  * @param {Object} config The config object
14773  */
14774
14775 Roo.bootstrap.TabGroup = function(config){
14776     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14777     if (!this.navId) {
14778         this.navId = Roo.id();
14779     }
14780     this.tabs = [];
14781     Roo.bootstrap.TabGroup.register(this);
14782     
14783 };
14784
14785 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14786     
14787     carousel : false,
14788     transition : false,
14789     bullets : 0,
14790     timer : 0,
14791     autoslide : false,
14792     slideFn : false,
14793     slideOnTouch : false,
14794     
14795     getAutoCreate : function()
14796     {
14797         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14798         
14799         cfg.cls += ' tab-content';
14800         
14801         Roo.log('get auto create...............');
14802         
14803         if (this.carousel) {
14804             cfg.cls += ' carousel slide';
14805             
14806             cfg.cn = [{
14807                cls : 'carousel-inner'
14808             }];
14809         
14810             if(this.bullets > 0 && !Roo.isTouch){
14811                 
14812                 var bullets = {
14813                     cls : 'carousel-bullets',
14814                     cn : []
14815                 };
14816                 
14817                 if(this.bullets_cls){
14818                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14819                 }
14820                 
14821                 for (var i = 0; i < this.bullets; i++){
14822                     bullets.cn.push({
14823                         cls : 'bullet bullet-' + i
14824                     });
14825                 }
14826                 
14827                 bullets.cn.push({
14828                     cls : 'clear'
14829                 });
14830                 
14831                 cfg.cn[0].cn = bullets;
14832             }
14833         }
14834         
14835         return cfg;
14836     },
14837     
14838     initEvents:  function()
14839     {
14840         Roo.log('-------- init events on tab group ---------');
14841         
14842         if(this.bullets > 0 && !Roo.isTouch){
14843             this.initBullet();
14844         }
14845         
14846         Roo.log(this);
14847         
14848         if(Roo.isTouch && this.slideOnTouch){
14849             this.el.on("touchstart", this.onTouchStart, this);
14850         }
14851         
14852         if(this.autoslide){
14853             var _this = this;
14854             
14855             this.slideFn = window.setInterval(function() {
14856                 _this.showPanelNext();
14857             }, this.timer);
14858         }
14859         
14860     },
14861     
14862     onTouchStart : function(e, el, o)
14863     {
14864         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14865             return;
14866         }
14867         
14868         this.showPanelNext();
14869     },
14870     
14871     getChildContainer : function()
14872     {
14873         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14874     },
14875     
14876     /**
14877     * register a Navigation item
14878     * @param {Roo.bootstrap.NavItem} the navitem to add
14879     */
14880     register : function(item)
14881     {
14882         this.tabs.push( item);
14883         item.navId = this.navId; // not really needed..
14884     
14885     },
14886     
14887     getActivePanel : function()
14888     {
14889         var r = false;
14890         Roo.each(this.tabs, function(t) {
14891             if (t.active) {
14892                 r = t;
14893                 return false;
14894             }
14895             return null;
14896         });
14897         return r;
14898         
14899     },
14900     getPanelByName : function(n)
14901     {
14902         var r = false;
14903         Roo.each(this.tabs, function(t) {
14904             if (t.tabId == n) {
14905                 r = t;
14906                 return false;
14907             }
14908             return null;
14909         });
14910         return r;
14911     },
14912     indexOfPanel : function(p)
14913     {
14914         var r = false;
14915         Roo.each(this.tabs, function(t,i) {
14916             if (t.tabId == p.tabId) {
14917                 r = i;
14918                 return false;
14919             }
14920             return null;
14921         });
14922         return r;
14923     },
14924     /**
14925      * show a specific panel
14926      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14927      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14928      */
14929     showPanel : function (pan)
14930     {
14931         if(this.transition){
14932             Roo.log("waiting for the transitionend");
14933             return;
14934         }
14935         
14936         if (typeof(pan) == 'number') {
14937             pan = this.tabs[pan];
14938         }
14939         if (typeof(pan) == 'string') {
14940             pan = this.getPanelByName(pan);
14941         }
14942         if (pan.tabId == this.getActivePanel().tabId) {
14943             return true;
14944         }
14945         var cur = this.getActivePanel();
14946         
14947         if (false === cur.fireEvent('beforedeactivate')) {
14948             return false;
14949         }
14950         
14951         if(this.bullets > 0 && !Roo.isTouch){
14952             this.setActiveBullet(this.indexOfPanel(pan));
14953         }
14954         
14955         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14956             
14957             this.transition = true;
14958             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14959             var lr = dir == 'next' ? 'left' : 'right';
14960             pan.el.addClass(dir); // or prev
14961             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14962             cur.el.addClass(lr); // or right
14963             pan.el.addClass(lr);
14964             
14965             var _this = this;
14966             cur.el.on('transitionend', function() {
14967                 Roo.log("trans end?");
14968                 
14969                 pan.el.removeClass([lr,dir]);
14970                 pan.setActive(true);
14971                 
14972                 cur.el.removeClass([lr]);
14973                 cur.setActive(false);
14974                 
14975                 _this.transition = false;
14976                 
14977             }, this, { single:  true } );
14978             
14979             return true;
14980         }
14981         
14982         cur.setActive(false);
14983         pan.setActive(true);
14984         
14985         return true;
14986         
14987     },
14988     showPanelNext : function()
14989     {
14990         var i = this.indexOfPanel(this.getActivePanel());
14991         
14992         if (i >= this.tabs.length - 1 && !this.autoslide) {
14993             return;
14994         }
14995         
14996         if (i >= this.tabs.length - 1 && this.autoslide) {
14997             i = -1;
14998         }
14999         
15000         this.showPanel(this.tabs[i+1]);
15001     },
15002     
15003     showPanelPrev : function()
15004     {
15005         var i = this.indexOfPanel(this.getActivePanel());
15006         
15007         if (i  < 1 && !this.autoslide) {
15008             return;
15009         }
15010         
15011         if (i < 1 && this.autoslide) {
15012             i = this.tabs.length;
15013         }
15014         
15015         this.showPanel(this.tabs[i-1]);
15016     },
15017     
15018     initBullet : function()
15019     {
15020         if(Roo.isTouch){
15021             return;
15022         }
15023         
15024         var _this = this;
15025         
15026         for (var i = 0; i < this.bullets; i++){
15027             var bullet = this.el.select('.bullet-' + i, true).first();
15028
15029             if(!bullet){
15030                 continue;
15031             }
15032
15033             bullet.on('click', (function(e, el, o, ii, t){
15034
15035                 e.preventDefault();
15036
15037                 _this.showPanel(ii);
15038
15039                 if(_this.autoslide && _this.slideFn){
15040                     clearInterval(_this.slideFn);
15041                     _this.slideFn = window.setInterval(function() {
15042                         _this.showPanelNext();
15043                     }, _this.timer);
15044                 }
15045
15046             }).createDelegate(this, [i, bullet], true));
15047         }
15048     },
15049     
15050     setActiveBullet : function(i)
15051     {
15052         if(Roo.isTouch){
15053             return;
15054         }
15055         
15056         Roo.each(this.el.select('.bullet', true).elements, function(el){
15057             el.removeClass('selected');
15058         });
15059
15060         var bullet = this.el.select('.bullet-' + i, true).first();
15061         
15062         if(!bullet){
15063             return;
15064         }
15065         
15066         bullet.addClass('selected');
15067     }
15068     
15069     
15070   
15071 });
15072
15073  
15074
15075  
15076  
15077 Roo.apply(Roo.bootstrap.TabGroup, {
15078     
15079     groups: {},
15080      /**
15081     * register a Navigation Group
15082     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15083     */
15084     register : function(navgrp)
15085     {
15086         this.groups[navgrp.navId] = navgrp;
15087         
15088     },
15089     /**
15090     * fetch a Navigation Group based on the navigation ID
15091     * if one does not exist , it will get created.
15092     * @param {string} the navgroup to add
15093     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15094     */
15095     get: function(navId) {
15096         if (typeof(this.groups[navId]) == 'undefined') {
15097             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15098         }
15099         return this.groups[navId] ;
15100     }
15101     
15102     
15103     
15104 });
15105
15106  /*
15107  * - LGPL
15108  *
15109  * TabPanel
15110  * 
15111  */
15112
15113 /**
15114  * @class Roo.bootstrap.TabPanel
15115  * @extends Roo.bootstrap.Component
15116  * Bootstrap TabPanel class
15117  * @cfg {Boolean} active panel active
15118  * @cfg {String} html panel content
15119  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15120  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15121  * 
15122  * 
15123  * @constructor
15124  * Create a new TabPanel
15125  * @param {Object} config The config object
15126  */
15127
15128 Roo.bootstrap.TabPanel = function(config){
15129     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15130     this.addEvents({
15131         /**
15132              * @event changed
15133              * Fires when the active status changes
15134              * @param {Roo.bootstrap.TabPanel} this
15135              * @param {Boolean} state the new state
15136             
15137          */
15138         'changed': true,
15139         /**
15140              * @event beforedeactivate
15141              * Fires before a tab is de-activated - can be used to do validation on a form.
15142              * @param {Roo.bootstrap.TabPanel} this
15143              * @return {Boolean} false if there is an error
15144             
15145          */
15146         'beforedeactivate': true
15147      });
15148     
15149     this.tabId = this.tabId || Roo.id();
15150   
15151 };
15152
15153 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15154     
15155     active: false,
15156     html: false,
15157     tabId: false,
15158     navId : false,
15159     
15160     getAutoCreate : function(){
15161         var cfg = {
15162             tag: 'div',
15163             // item is needed for carousel - not sure if it has any effect otherwise
15164             cls: 'tab-pane item',
15165             html: this.html || ''
15166         };
15167         
15168         if(this.active){
15169             cfg.cls += ' active';
15170         }
15171         
15172         if(this.tabId){
15173             cfg.tabId = this.tabId;
15174         }
15175         
15176         
15177         return cfg;
15178     },
15179     
15180     initEvents:  function()
15181     {
15182         Roo.log('-------- init events on tab panel ---------');
15183         
15184         var p = this.parent();
15185         this.navId = this.navId || p.navId;
15186         
15187         if (typeof(this.navId) != 'undefined') {
15188             // not really needed.. but just in case.. parent should be a NavGroup.
15189             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15190             Roo.log(['register', tg, this]);
15191             tg.register(this);
15192             
15193             var i = tg.tabs.length - 1;
15194             
15195             if(this.active && tg.bullets > 0 && i < tg.bullets){
15196                 tg.setActiveBullet(i);
15197             }
15198         }
15199         
15200     },
15201     
15202     
15203     onRender : function(ct, position)
15204     {
15205        // Roo.log("Call onRender: " + this.xtype);
15206         
15207         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15208         
15209         
15210         
15211         
15212         
15213     },
15214     
15215     setActive: function(state)
15216     {
15217         Roo.log("panel - set active " + this.tabId + "=" + state);
15218         
15219         this.active = state;
15220         if (!state) {
15221             this.el.removeClass('active');
15222             
15223         } else  if (!this.el.hasClass('active')) {
15224             this.el.addClass('active');
15225         }
15226         
15227         this.fireEvent('changed', this, state);
15228     }
15229     
15230     
15231 });
15232  
15233
15234  
15235
15236  /*
15237  * - LGPL
15238  *
15239  * DateField
15240  * 
15241  */
15242
15243 /**
15244  * @class Roo.bootstrap.DateField
15245  * @extends Roo.bootstrap.Input
15246  * Bootstrap DateField class
15247  * @cfg {Number} weekStart default 0
15248  * @cfg {String} viewMode default empty, (months|years)
15249  * @cfg {String} minViewMode default empty, (months|years)
15250  * @cfg {Number} startDate default -Infinity
15251  * @cfg {Number} endDate default Infinity
15252  * @cfg {Boolean} todayHighlight default false
15253  * @cfg {Boolean} todayBtn default false
15254  * @cfg {Boolean} calendarWeeks default false
15255  * @cfg {Object} daysOfWeekDisabled default empty
15256  * @cfg {Boolean} singleMode default false (true | false)
15257  * 
15258  * @cfg {Boolean} keyboardNavigation default true
15259  * @cfg {String} language default en
15260  * 
15261  * @constructor
15262  * Create a new DateField
15263  * @param {Object} config The config object
15264  */
15265
15266 Roo.bootstrap.DateField = function(config){
15267     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15268      this.addEvents({
15269             /**
15270              * @event show
15271              * Fires when this field show.
15272              * @param {Roo.bootstrap.DateField} this
15273              * @param {Mixed} date The date value
15274              */
15275             show : true,
15276             /**
15277              * @event show
15278              * Fires when this field hide.
15279              * @param {Roo.bootstrap.DateField} this
15280              * @param {Mixed} date The date value
15281              */
15282             hide : true,
15283             /**
15284              * @event select
15285              * Fires when select a date.
15286              * @param {Roo.bootstrap.DateField} this
15287              * @param {Mixed} date The date value
15288              */
15289             select : true
15290         });
15291 };
15292
15293 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15294     
15295     /**
15296      * @cfg {String} format
15297      * The default date format string which can be overriden for localization support.  The format must be
15298      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15299      */
15300     format : "m/d/y",
15301     /**
15302      * @cfg {String} altFormats
15303      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15304      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15305      */
15306     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15307     
15308     weekStart : 0,
15309     
15310     viewMode : '',
15311     
15312     minViewMode : '',
15313     
15314     todayHighlight : false,
15315     
15316     todayBtn: false,
15317     
15318     language: 'en',
15319     
15320     keyboardNavigation: true,
15321     
15322     calendarWeeks: false,
15323     
15324     startDate: -Infinity,
15325     
15326     endDate: Infinity,
15327     
15328     daysOfWeekDisabled: [],
15329     
15330     _events: [],
15331     
15332     singleMode : false,
15333     
15334     UTCDate: function()
15335     {
15336         return new Date(Date.UTC.apply(Date, arguments));
15337     },
15338     
15339     UTCToday: function()
15340     {
15341         var today = new Date();
15342         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15343     },
15344     
15345     getDate: function() {
15346             var d = this.getUTCDate();
15347             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15348     },
15349     
15350     getUTCDate: function() {
15351             return this.date;
15352     },
15353     
15354     setDate: function(d) {
15355             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15356     },
15357     
15358     setUTCDate: function(d) {
15359             this.date = d;
15360             this.setValue(this.formatDate(this.date));
15361     },
15362         
15363     onRender: function(ct, position)
15364     {
15365         
15366         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15367         
15368         this.language = this.language || 'en';
15369         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15370         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15371         
15372         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15373         this.format = this.format || 'm/d/y';
15374         this.isInline = false;
15375         this.isInput = true;
15376         this.component = this.el.select('.add-on', true).first() || false;
15377         this.component = (this.component && this.component.length === 0) ? false : this.component;
15378         this.hasInput = this.component && this.inputEL().length;
15379         
15380         if (typeof(this.minViewMode === 'string')) {
15381             switch (this.minViewMode) {
15382                 case 'months':
15383                     this.minViewMode = 1;
15384                     break;
15385                 case 'years':
15386                     this.minViewMode = 2;
15387                     break;
15388                 default:
15389                     this.minViewMode = 0;
15390                     break;
15391             }
15392         }
15393         
15394         if (typeof(this.viewMode === 'string')) {
15395             switch (this.viewMode) {
15396                 case 'months':
15397                     this.viewMode = 1;
15398                     break;
15399                 case 'years':
15400                     this.viewMode = 2;
15401                     break;
15402                 default:
15403                     this.viewMode = 0;
15404                     break;
15405             }
15406         }
15407                 
15408         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15409         
15410 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15411         
15412         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15413         
15414         this.picker().on('mousedown', this.onMousedown, this);
15415         this.picker().on('click', this.onClick, this);
15416         
15417         this.picker().addClass('datepicker-dropdown');
15418         
15419         this.startViewMode = this.viewMode;
15420         
15421         if(this.singleMode){
15422             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15423                 v.setVisibilityMode(Roo.Element.DISPLAY)
15424                 v.hide();
15425             });
15426             
15427             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15428                 v.setStyle('width', '189px');
15429             });
15430         }
15431         
15432         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15433             if(!this.calendarWeeks){
15434                 v.remove();
15435                 return;
15436             }
15437             
15438             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15439             v.attr('colspan', function(i, val){
15440                 return parseInt(val) + 1;
15441             });
15442         })
15443                         
15444         
15445         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15446         
15447         this.setStartDate(this.startDate);
15448         this.setEndDate(this.endDate);
15449         
15450         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15451         
15452         this.fillDow();
15453         this.fillMonths();
15454         this.update();
15455         this.showMode();
15456         
15457         if(this.isInline) {
15458             this.show();
15459         }
15460     },
15461     
15462     picker : function()
15463     {
15464         return this.pickerEl;
15465 //        return this.el.select('.datepicker', true).first();
15466     },
15467     
15468     fillDow: function()
15469     {
15470         var dowCnt = this.weekStart;
15471         
15472         var dow = {
15473             tag: 'tr',
15474             cn: [
15475                 
15476             ]
15477         };
15478         
15479         if(this.calendarWeeks){
15480             dow.cn.push({
15481                 tag: 'th',
15482                 cls: 'cw',
15483                 html: '&nbsp;'
15484             })
15485         }
15486         
15487         while (dowCnt < this.weekStart + 7) {
15488             dow.cn.push({
15489                 tag: 'th',
15490                 cls: 'dow',
15491                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15492             });
15493         }
15494         
15495         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15496     },
15497     
15498     fillMonths: function()
15499     {    
15500         var i = 0;
15501         var months = this.picker().select('>.datepicker-months td', true).first();
15502         
15503         months.dom.innerHTML = '';
15504         
15505         while (i < 12) {
15506             var month = {
15507                 tag: 'span',
15508                 cls: 'month',
15509                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15510             }
15511             
15512             months.createChild(month);
15513         }
15514         
15515     },
15516     
15517     update: function()
15518     {
15519         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;
15520         
15521         if (this.date < this.startDate) {
15522             this.viewDate = new Date(this.startDate);
15523         } else if (this.date > this.endDate) {
15524             this.viewDate = new Date(this.endDate);
15525         } else {
15526             this.viewDate = new Date(this.date);
15527         }
15528         
15529         this.fill();
15530     },
15531     
15532     fill: function() 
15533     {
15534         var d = new Date(this.viewDate),
15535                 year = d.getUTCFullYear(),
15536                 month = d.getUTCMonth(),
15537                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15538                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15539                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15540                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15541                 currentDate = this.date && this.date.valueOf(),
15542                 today = this.UTCToday();
15543         
15544         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15545         
15546 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15547         
15548 //        this.picker.select('>tfoot th.today').
15549 //                                              .text(dates[this.language].today)
15550 //                                              .toggle(this.todayBtn !== false);
15551     
15552         this.updateNavArrows();
15553         this.fillMonths();
15554                                                 
15555         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15556         
15557         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15558          
15559         prevMonth.setUTCDate(day);
15560         
15561         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15562         
15563         var nextMonth = new Date(prevMonth);
15564         
15565         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15566         
15567         nextMonth = nextMonth.valueOf();
15568         
15569         var fillMonths = false;
15570         
15571         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15572         
15573         while(prevMonth.valueOf() < nextMonth) {
15574             var clsName = '';
15575             
15576             if (prevMonth.getUTCDay() === this.weekStart) {
15577                 if(fillMonths){
15578                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15579                 }
15580                     
15581                 fillMonths = {
15582                     tag: 'tr',
15583                     cn: []
15584                 };
15585                 
15586                 if(this.calendarWeeks){
15587                     // ISO 8601: First week contains first thursday.
15588                     // ISO also states week starts on Monday, but we can be more abstract here.
15589                     var
15590                     // Start of current week: based on weekstart/current date
15591                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15592                     // Thursday of this week
15593                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15594                     // First Thursday of year, year from thursday
15595                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15596                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15597                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15598                     
15599                     fillMonths.cn.push({
15600                         tag: 'td',
15601                         cls: 'cw',
15602                         html: calWeek
15603                     });
15604                 }
15605             }
15606             
15607             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15608                 clsName += ' old';
15609             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15610                 clsName += ' new';
15611             }
15612             if (this.todayHighlight &&
15613                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15614                 prevMonth.getUTCMonth() == today.getMonth() &&
15615                 prevMonth.getUTCDate() == today.getDate()) {
15616                 clsName += ' today';
15617             }
15618             
15619             if (currentDate && prevMonth.valueOf() === currentDate) {
15620                 clsName += ' active';
15621             }
15622             
15623             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15624                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15625                     clsName += ' disabled';
15626             }
15627             
15628             fillMonths.cn.push({
15629                 tag: 'td',
15630                 cls: 'day ' + clsName,
15631                 html: prevMonth.getDate()
15632             })
15633             
15634             prevMonth.setDate(prevMonth.getDate()+1);
15635         }
15636           
15637         var currentYear = this.date && this.date.getUTCFullYear();
15638         var currentMonth = this.date && this.date.getUTCMonth();
15639         
15640         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15641         
15642         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15643             v.removeClass('active');
15644             
15645             if(currentYear === year && k === currentMonth){
15646                 v.addClass('active');
15647             }
15648             
15649             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15650                 v.addClass('disabled');
15651             }
15652             
15653         });
15654         
15655         
15656         year = parseInt(year/10, 10) * 10;
15657         
15658         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15659         
15660         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15661         
15662         year -= 1;
15663         for (var i = -1; i < 11; i++) {
15664             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15665                 tag: 'span',
15666                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15667                 html: year
15668             })
15669             
15670             year += 1;
15671         }
15672     },
15673     
15674     showMode: function(dir) 
15675     {
15676         if (dir) {
15677             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15678         }
15679         
15680         Roo.each(this.picker().select('>div',true).elements, function(v){
15681             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15682             v.hide();
15683         });
15684         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15685     },
15686     
15687     place: function()
15688     {
15689         if(this.isInline) return;
15690         
15691         this.picker().removeClass(['bottom', 'top']);
15692         
15693         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15694             /*
15695              * place to the top of element!
15696              *
15697              */
15698             
15699             this.picker().addClass('top');
15700             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15701             
15702             return;
15703         }
15704         
15705         this.picker().addClass('bottom');
15706         
15707         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15708     },
15709     
15710     parseDate : function(value)
15711     {
15712         if(!value || value instanceof Date){
15713             return value;
15714         }
15715         var v = Date.parseDate(value, this.format);
15716         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15717             v = Date.parseDate(value, 'Y-m-d');
15718         }
15719         if(!v && this.altFormats){
15720             if(!this.altFormatsArray){
15721                 this.altFormatsArray = this.altFormats.split("|");
15722             }
15723             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15724                 v = Date.parseDate(value, this.altFormatsArray[i]);
15725             }
15726         }
15727         return v;
15728     },
15729     
15730     formatDate : function(date, fmt)
15731     {   
15732         return (!date || !(date instanceof Date)) ?
15733         date : date.dateFormat(fmt || this.format);
15734     },
15735     
15736     onFocus : function()
15737     {
15738         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15739         this.show();
15740     },
15741     
15742     onBlur : function()
15743     {
15744         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15745         
15746         var d = this.inputEl().getValue();
15747         
15748         this.setValue(d);
15749                 
15750         this.hide();
15751     },
15752     
15753     show : function()
15754     {
15755         this.picker().show();
15756         this.update();
15757         this.place();
15758         
15759         this.fireEvent('show', this, this.date);
15760     },
15761     
15762     hide : function()
15763     {
15764         if(this.isInline) return;
15765         this.picker().hide();
15766         this.viewMode = this.startViewMode;
15767         this.showMode();
15768         
15769         this.fireEvent('hide', this, this.date);
15770         
15771     },
15772     
15773     onMousedown: function(e)
15774     {
15775         e.stopPropagation();
15776         e.preventDefault();
15777     },
15778     
15779     keyup: function(e)
15780     {
15781         Roo.bootstrap.DateField.superclass.keyup.call(this);
15782         this.update();
15783     },
15784
15785     setValue: function(v)
15786     {
15787         
15788         // v can be a string or a date..
15789         
15790         
15791         var d = new Date(this.parseDate(v) ).clearTime();
15792         
15793         if(isNaN(d.getTime())){
15794             this.date = this.viewDate = '';
15795             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15796             return;
15797         }
15798         
15799         v = this.formatDate(d);
15800         
15801         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15802         
15803         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15804      
15805         this.update();
15806
15807         this.fireEvent('select', this, this.date);
15808         
15809     },
15810     
15811     getValue: function()
15812     {
15813         return this.formatDate(this.date);
15814     },
15815     
15816     fireKey: function(e)
15817     {
15818         if (!this.picker().isVisible()){
15819             if (e.keyCode == 27) // allow escape to hide and re-show picker
15820                 this.show();
15821             return;
15822         }
15823         
15824         var dateChanged = false,
15825         dir, day, month,
15826         newDate, newViewDate;
15827         
15828         switch(e.keyCode){
15829             case 27: // escape
15830                 this.hide();
15831                 e.preventDefault();
15832                 break;
15833             case 37: // left
15834             case 39: // right
15835                 if (!this.keyboardNavigation) break;
15836                 dir = e.keyCode == 37 ? -1 : 1;
15837                 
15838                 if (e.ctrlKey){
15839                     newDate = this.moveYear(this.date, dir);
15840                     newViewDate = this.moveYear(this.viewDate, dir);
15841                 } else if (e.shiftKey){
15842                     newDate = this.moveMonth(this.date, dir);
15843                     newViewDate = this.moveMonth(this.viewDate, dir);
15844                 } else {
15845                     newDate = new Date(this.date);
15846                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15847                     newViewDate = new Date(this.viewDate);
15848                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15849                 }
15850                 if (this.dateWithinRange(newDate)){
15851                     this.date = newDate;
15852                     this.viewDate = newViewDate;
15853                     this.setValue(this.formatDate(this.date));
15854 //                    this.update();
15855                     e.preventDefault();
15856                     dateChanged = true;
15857                 }
15858                 break;
15859             case 38: // up
15860             case 40: // down
15861                 if (!this.keyboardNavigation) break;
15862                 dir = e.keyCode == 38 ? -1 : 1;
15863                 if (e.ctrlKey){
15864                     newDate = this.moveYear(this.date, dir);
15865                     newViewDate = this.moveYear(this.viewDate, dir);
15866                 } else if (e.shiftKey){
15867                     newDate = this.moveMonth(this.date, dir);
15868                     newViewDate = this.moveMonth(this.viewDate, dir);
15869                 } else {
15870                     newDate = new Date(this.date);
15871                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15872                     newViewDate = new Date(this.viewDate);
15873                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15874                 }
15875                 if (this.dateWithinRange(newDate)){
15876                     this.date = newDate;
15877                     this.viewDate = newViewDate;
15878                     this.setValue(this.formatDate(this.date));
15879 //                    this.update();
15880                     e.preventDefault();
15881                     dateChanged = true;
15882                 }
15883                 break;
15884             case 13: // enter
15885                 this.setValue(this.formatDate(this.date));
15886                 this.hide();
15887                 e.preventDefault();
15888                 break;
15889             case 9: // tab
15890                 this.setValue(this.formatDate(this.date));
15891                 this.hide();
15892                 break;
15893             case 16: // shift
15894             case 17: // ctrl
15895             case 18: // alt
15896                 break;
15897             default :
15898                 this.hide();
15899                 
15900         }
15901     },
15902     
15903     
15904     onClick: function(e) 
15905     {
15906         e.stopPropagation();
15907         e.preventDefault();
15908         
15909         var target = e.getTarget();
15910         
15911         if(target.nodeName.toLowerCase() === 'i'){
15912             target = Roo.get(target).dom.parentNode;
15913         }
15914         
15915         var nodeName = target.nodeName;
15916         var className = target.className;
15917         var html = target.innerHTML;
15918         //Roo.log(nodeName);
15919         
15920         switch(nodeName.toLowerCase()) {
15921             case 'th':
15922                 switch(className) {
15923                     case 'switch':
15924                         this.showMode(1);
15925                         break;
15926                     case 'prev':
15927                     case 'next':
15928                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15929                         switch(this.viewMode){
15930                                 case 0:
15931                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15932                                         break;
15933                                 case 1:
15934                                 case 2:
15935                                         this.viewDate = this.moveYear(this.viewDate, dir);
15936                                         break;
15937                         }
15938                         this.fill();
15939                         break;
15940                     case 'today':
15941                         var date = new Date();
15942                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15943 //                        this.fill()
15944                         this.setValue(this.formatDate(this.date));
15945                         
15946                         this.hide();
15947                         break;
15948                 }
15949                 break;
15950             case 'span':
15951                 if (className.indexOf('disabled') < 0) {
15952                     this.viewDate.setUTCDate(1);
15953                     if (className.indexOf('month') > -1) {
15954                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15955                     } else {
15956                         var year = parseInt(html, 10) || 0;
15957                         this.viewDate.setUTCFullYear(year);
15958                         
15959                     }
15960                     
15961                     if(this.singleMode){
15962                         this.setValue(this.formatDate(this.viewDate));
15963                         this.hide();
15964                         return;
15965                     }
15966                     
15967                     this.showMode(-1);
15968                     this.fill();
15969                 }
15970                 break;
15971                 
15972             case 'td':
15973                 //Roo.log(className);
15974                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15975                     var day = parseInt(html, 10) || 1;
15976                     var year = this.viewDate.getUTCFullYear(),
15977                         month = this.viewDate.getUTCMonth();
15978
15979                     if (className.indexOf('old') > -1) {
15980                         if(month === 0 ){
15981                             month = 11;
15982                             year -= 1;
15983                         }else{
15984                             month -= 1;
15985                         }
15986                     } else if (className.indexOf('new') > -1) {
15987                         if (month == 11) {
15988                             month = 0;
15989                             year += 1;
15990                         } else {
15991                             month += 1;
15992                         }
15993                     }
15994                     //Roo.log([year,month,day]);
15995                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15996                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15997 //                    this.fill();
15998                     //Roo.log(this.formatDate(this.date));
15999                     this.setValue(this.formatDate(this.date));
16000                     this.hide();
16001                 }
16002                 break;
16003         }
16004     },
16005     
16006     setStartDate: function(startDate)
16007     {
16008         this.startDate = startDate || -Infinity;
16009         if (this.startDate !== -Infinity) {
16010             this.startDate = this.parseDate(this.startDate);
16011         }
16012         this.update();
16013         this.updateNavArrows();
16014     },
16015
16016     setEndDate: function(endDate)
16017     {
16018         this.endDate = endDate || Infinity;
16019         if (this.endDate !== Infinity) {
16020             this.endDate = this.parseDate(this.endDate);
16021         }
16022         this.update();
16023         this.updateNavArrows();
16024     },
16025     
16026     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16027     {
16028         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16029         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16030             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16031         }
16032         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16033             return parseInt(d, 10);
16034         });
16035         this.update();
16036         this.updateNavArrows();
16037     },
16038     
16039     updateNavArrows: function() 
16040     {
16041         if(this.singleMode){
16042             return;
16043         }
16044         
16045         var d = new Date(this.viewDate),
16046         year = d.getUTCFullYear(),
16047         month = d.getUTCMonth();
16048         
16049         Roo.each(this.picker().select('.prev', true).elements, function(v){
16050             v.show();
16051             switch (this.viewMode) {
16052                 case 0:
16053
16054                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16055                         v.hide();
16056                     }
16057                     break;
16058                 case 1:
16059                 case 2:
16060                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16061                         v.hide();
16062                     }
16063                     break;
16064             }
16065         });
16066         
16067         Roo.each(this.picker().select('.next', true).elements, function(v){
16068             v.show();
16069             switch (this.viewMode) {
16070                 case 0:
16071
16072                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16073                         v.hide();
16074                     }
16075                     break;
16076                 case 1:
16077                 case 2:
16078                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16079                         v.hide();
16080                     }
16081                     break;
16082             }
16083         })
16084     },
16085     
16086     moveMonth: function(date, dir)
16087     {
16088         if (!dir) return date;
16089         var new_date = new Date(date.valueOf()),
16090         day = new_date.getUTCDate(),
16091         month = new_date.getUTCMonth(),
16092         mag = Math.abs(dir),
16093         new_month, test;
16094         dir = dir > 0 ? 1 : -1;
16095         if (mag == 1){
16096             test = dir == -1
16097             // If going back one month, make sure month is not current month
16098             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16099             ? function(){
16100                 return new_date.getUTCMonth() == month;
16101             }
16102             // If going forward one month, make sure month is as expected
16103             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16104             : function(){
16105                 return new_date.getUTCMonth() != new_month;
16106             };
16107             new_month = month + dir;
16108             new_date.setUTCMonth(new_month);
16109             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16110             if (new_month < 0 || new_month > 11)
16111                 new_month = (new_month + 12) % 12;
16112         } else {
16113             // For magnitudes >1, move one month at a time...
16114             for (var i=0; i<mag; i++)
16115                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16116                 new_date = this.moveMonth(new_date, dir);
16117             // ...then reset the day, keeping it in the new month
16118             new_month = new_date.getUTCMonth();
16119             new_date.setUTCDate(day);
16120             test = function(){
16121                 return new_month != new_date.getUTCMonth();
16122             };
16123         }
16124         // Common date-resetting loop -- if date is beyond end of month, make it
16125         // end of month
16126         while (test()){
16127             new_date.setUTCDate(--day);
16128             new_date.setUTCMonth(new_month);
16129         }
16130         return new_date;
16131     },
16132
16133     moveYear: function(date, dir)
16134     {
16135         return this.moveMonth(date, dir*12);
16136     },
16137
16138     dateWithinRange: function(date)
16139     {
16140         return date >= this.startDate && date <= this.endDate;
16141     },
16142
16143     
16144     remove: function() 
16145     {
16146         this.picker().remove();
16147     }
16148    
16149 });
16150
16151 Roo.apply(Roo.bootstrap.DateField,  {
16152     
16153     head : {
16154         tag: 'thead',
16155         cn: [
16156         {
16157             tag: 'tr',
16158             cn: [
16159             {
16160                 tag: 'th',
16161                 cls: 'prev',
16162                 html: '<i class="fa fa-arrow-left"/>'
16163             },
16164             {
16165                 tag: 'th',
16166                 cls: 'switch',
16167                 colspan: '5'
16168             },
16169             {
16170                 tag: 'th',
16171                 cls: 'next',
16172                 html: '<i class="fa fa-arrow-right"/>'
16173             }
16174
16175             ]
16176         }
16177         ]
16178     },
16179     
16180     content : {
16181         tag: 'tbody',
16182         cn: [
16183         {
16184             tag: 'tr',
16185             cn: [
16186             {
16187                 tag: 'td',
16188                 colspan: '7'
16189             }
16190             ]
16191         }
16192         ]
16193     },
16194     
16195     footer : {
16196         tag: 'tfoot',
16197         cn: [
16198         {
16199             tag: 'tr',
16200             cn: [
16201             {
16202                 tag: 'th',
16203                 colspan: '7',
16204                 cls: 'today'
16205             }
16206                     
16207             ]
16208         }
16209         ]
16210     },
16211     
16212     dates:{
16213         en: {
16214             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16215             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16216             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16217             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16218             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16219             today: "Today"
16220         }
16221     },
16222     
16223     modes: [
16224     {
16225         clsName: 'days',
16226         navFnc: 'Month',
16227         navStep: 1
16228     },
16229     {
16230         clsName: 'months',
16231         navFnc: 'FullYear',
16232         navStep: 1
16233     },
16234     {
16235         clsName: 'years',
16236         navFnc: 'FullYear',
16237         navStep: 10
16238     }]
16239 });
16240
16241 Roo.apply(Roo.bootstrap.DateField,  {
16242   
16243     template : {
16244         tag: 'div',
16245         cls: 'datepicker dropdown-menu roo-dynamic',
16246         cn: [
16247         {
16248             tag: 'div',
16249             cls: 'datepicker-days',
16250             cn: [
16251             {
16252                 tag: 'table',
16253                 cls: 'table-condensed',
16254                 cn:[
16255                 Roo.bootstrap.DateField.head,
16256                 {
16257                     tag: 'tbody'
16258                 },
16259                 Roo.bootstrap.DateField.footer
16260                 ]
16261             }
16262             ]
16263         },
16264         {
16265             tag: 'div',
16266             cls: 'datepicker-months',
16267             cn: [
16268             {
16269                 tag: 'table',
16270                 cls: 'table-condensed',
16271                 cn:[
16272                 Roo.bootstrap.DateField.head,
16273                 Roo.bootstrap.DateField.content,
16274                 Roo.bootstrap.DateField.footer
16275                 ]
16276             }
16277             ]
16278         },
16279         {
16280             tag: 'div',
16281             cls: 'datepicker-years',
16282             cn: [
16283             {
16284                 tag: 'table',
16285                 cls: 'table-condensed',
16286                 cn:[
16287                 Roo.bootstrap.DateField.head,
16288                 Roo.bootstrap.DateField.content,
16289                 Roo.bootstrap.DateField.footer
16290                 ]
16291             }
16292             ]
16293         }
16294         ]
16295     }
16296 });
16297
16298  
16299
16300  /*
16301  * - LGPL
16302  *
16303  * TimeField
16304  * 
16305  */
16306
16307 /**
16308  * @class Roo.bootstrap.TimeField
16309  * @extends Roo.bootstrap.Input
16310  * Bootstrap DateField class
16311  * 
16312  * 
16313  * @constructor
16314  * Create a new TimeField
16315  * @param {Object} config The config object
16316  */
16317
16318 Roo.bootstrap.TimeField = function(config){
16319     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16320     this.addEvents({
16321             /**
16322              * @event show
16323              * Fires when this field show.
16324              * @param {Roo.bootstrap.DateField} thisthis
16325              * @param {Mixed} date The date value
16326              */
16327             show : true,
16328             /**
16329              * @event show
16330              * Fires when this field hide.
16331              * @param {Roo.bootstrap.DateField} this
16332              * @param {Mixed} date The date value
16333              */
16334             hide : true,
16335             /**
16336              * @event select
16337              * Fires when select a date.
16338              * @param {Roo.bootstrap.DateField} this
16339              * @param {Mixed} date The date value
16340              */
16341             select : true
16342         });
16343 };
16344
16345 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16346     
16347     /**
16348      * @cfg {String} format
16349      * The default time format string which can be overriden for localization support.  The format must be
16350      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16351      */
16352     format : "H:i",
16353        
16354     onRender: function(ct, position)
16355     {
16356         
16357         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16358                 
16359         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16360         
16361         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16362         
16363         this.pop = this.picker().select('>.datepicker-time',true).first();
16364         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16365         
16366         this.picker().on('mousedown', this.onMousedown, this);
16367         this.picker().on('click', this.onClick, this);
16368         
16369         this.picker().addClass('datepicker-dropdown');
16370     
16371         this.fillTime();
16372         this.update();
16373             
16374         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16375         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16376         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16377         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16378         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16379         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16380
16381     },
16382     
16383     fireKey: function(e){
16384         if (!this.picker().isVisible()){
16385             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16386                 this.show();
16387             }
16388             return;
16389         }
16390
16391         e.preventDefault();
16392         
16393         switch(e.keyCode){
16394             case 27: // escape
16395                 this.hide();
16396                 break;
16397             case 37: // left
16398             case 39: // right
16399                 this.onTogglePeriod();
16400                 break;
16401             case 38: // up
16402                 this.onIncrementMinutes();
16403                 break;
16404             case 40: // down
16405                 this.onDecrementMinutes();
16406                 break;
16407             case 13: // enter
16408             case 9: // tab
16409                 this.setTime();
16410                 break;
16411         }
16412     },
16413     
16414     onClick: function(e) {
16415         e.stopPropagation();
16416         e.preventDefault();
16417     },
16418     
16419     picker : function()
16420     {
16421         return this.el.select('.datepicker', true).first();
16422     },
16423     
16424     fillTime: function()
16425     {    
16426         var time = this.pop.select('tbody', true).first();
16427         
16428         time.dom.innerHTML = '';
16429         
16430         time.createChild({
16431             tag: 'tr',
16432             cn: [
16433                 {
16434                     tag: 'td',
16435                     cn: [
16436                         {
16437                             tag: 'a',
16438                             href: '#',
16439                             cls: 'btn',
16440                             cn: [
16441                                 {
16442                                     tag: 'span',
16443                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16444                                 }
16445                             ]
16446                         } 
16447                     ]
16448                 },
16449                 {
16450                     tag: 'td',
16451                     cls: 'separator'
16452                 },
16453                 {
16454                     tag: 'td',
16455                     cn: [
16456                         {
16457                             tag: 'a',
16458                             href: '#',
16459                             cls: 'btn',
16460                             cn: [
16461                                 {
16462                                     tag: 'span',
16463                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16464                                 }
16465                             ]
16466                         }
16467                     ]
16468                 },
16469                 {
16470                     tag: 'td',
16471                     cls: 'separator'
16472                 }
16473             ]
16474         });
16475         
16476         time.createChild({
16477             tag: 'tr',
16478             cn: [
16479                 {
16480                     tag: 'td',
16481                     cn: [
16482                         {
16483                             tag: 'span',
16484                             cls: 'timepicker-hour',
16485                             html: '00'
16486                         }  
16487                     ]
16488                 },
16489                 {
16490                     tag: 'td',
16491                     cls: 'separator',
16492                     html: ':'
16493                 },
16494                 {
16495                     tag: 'td',
16496                     cn: [
16497                         {
16498                             tag: 'span',
16499                             cls: 'timepicker-minute',
16500                             html: '00'
16501                         }  
16502                     ]
16503                 },
16504                 {
16505                     tag: 'td',
16506                     cls: 'separator'
16507                 },
16508                 {
16509                     tag: 'td',
16510                     cn: [
16511                         {
16512                             tag: 'button',
16513                             type: 'button',
16514                             cls: 'btn btn-primary period',
16515                             html: 'AM'
16516                             
16517                         }
16518                     ]
16519                 }
16520             ]
16521         });
16522         
16523         time.createChild({
16524             tag: 'tr',
16525             cn: [
16526                 {
16527                     tag: 'td',
16528                     cn: [
16529                         {
16530                             tag: 'a',
16531                             href: '#',
16532                             cls: 'btn',
16533                             cn: [
16534                                 {
16535                                     tag: 'span',
16536                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16537                                 }
16538                             ]
16539                         }
16540                     ]
16541                 },
16542                 {
16543                     tag: 'td',
16544                     cls: 'separator'
16545                 },
16546                 {
16547                     tag: 'td',
16548                     cn: [
16549                         {
16550                             tag: 'a',
16551                             href: '#',
16552                             cls: 'btn',
16553                             cn: [
16554                                 {
16555                                     tag: 'span',
16556                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16557                                 }
16558                             ]
16559                         }
16560                     ]
16561                 },
16562                 {
16563                     tag: 'td',
16564                     cls: 'separator'
16565                 }
16566             ]
16567         });
16568         
16569     },
16570     
16571     update: function()
16572     {
16573         
16574         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16575         
16576         this.fill();
16577     },
16578     
16579     fill: function() 
16580     {
16581         var hours = this.time.getHours();
16582         var minutes = this.time.getMinutes();
16583         var period = 'AM';
16584         
16585         if(hours > 11){
16586             period = 'PM';
16587         }
16588         
16589         if(hours == 0){
16590             hours = 12;
16591         }
16592         
16593         
16594         if(hours > 12){
16595             hours = hours - 12;
16596         }
16597         
16598         if(hours < 10){
16599             hours = '0' + hours;
16600         }
16601         
16602         if(minutes < 10){
16603             minutes = '0' + minutes;
16604         }
16605         
16606         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16607         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16608         this.pop.select('button', true).first().dom.innerHTML = period;
16609         
16610     },
16611     
16612     place: function()
16613     {   
16614         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16615         
16616         var cls = ['bottom'];
16617         
16618         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16619             cls.pop();
16620             cls.push('top');
16621         }
16622         
16623         cls.push('right');
16624         
16625         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16626             cls.pop();
16627             cls.push('left');
16628         }
16629         
16630         this.picker().addClass(cls.join('-'));
16631         
16632         var _this = this;
16633         
16634         Roo.each(cls, function(c){
16635             if(c == 'bottom'){
16636                 _this.picker().setTop(_this.inputEl().getHeight());
16637                 return;
16638             }
16639             if(c == 'top'){
16640                 _this.picker().setTop(0 - _this.picker().getHeight());
16641                 return;
16642             }
16643             
16644             if(c == 'left'){
16645                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16646                 return;
16647             }
16648             if(c == 'right'){
16649                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16650                 return;
16651             }
16652         });
16653         
16654     },
16655   
16656     onFocus : function()
16657     {
16658         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16659         this.show();
16660     },
16661     
16662     onBlur : function()
16663     {
16664         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16665         this.hide();
16666     },
16667     
16668     show : function()
16669     {
16670         this.picker().show();
16671         this.pop.show();
16672         this.update();
16673         this.place();
16674         
16675         this.fireEvent('show', this, this.date);
16676     },
16677     
16678     hide : function()
16679     {
16680         this.picker().hide();
16681         this.pop.hide();
16682         
16683         this.fireEvent('hide', this, this.date);
16684     },
16685     
16686     setTime : function()
16687     {
16688         this.hide();
16689         this.setValue(this.time.format(this.format));
16690         
16691         this.fireEvent('select', this, this.date);
16692         
16693         
16694     },
16695     
16696     onMousedown: function(e){
16697         e.stopPropagation();
16698         e.preventDefault();
16699     },
16700     
16701     onIncrementHours: function()
16702     {
16703         Roo.log('onIncrementHours');
16704         this.time = this.time.add(Date.HOUR, 1);
16705         this.update();
16706         
16707     },
16708     
16709     onDecrementHours: function()
16710     {
16711         Roo.log('onDecrementHours');
16712         this.time = this.time.add(Date.HOUR, -1);
16713         this.update();
16714     },
16715     
16716     onIncrementMinutes: function()
16717     {
16718         Roo.log('onIncrementMinutes');
16719         this.time = this.time.add(Date.MINUTE, 1);
16720         this.update();
16721     },
16722     
16723     onDecrementMinutes: function()
16724     {
16725         Roo.log('onDecrementMinutes');
16726         this.time = this.time.add(Date.MINUTE, -1);
16727         this.update();
16728     },
16729     
16730     onTogglePeriod: function()
16731     {
16732         Roo.log('onTogglePeriod');
16733         this.time = this.time.add(Date.HOUR, 12);
16734         this.update();
16735     }
16736     
16737    
16738 });
16739
16740 Roo.apply(Roo.bootstrap.TimeField,  {
16741     
16742     content : {
16743         tag: 'tbody',
16744         cn: [
16745             {
16746                 tag: 'tr',
16747                 cn: [
16748                 {
16749                     tag: 'td',
16750                     colspan: '7'
16751                 }
16752                 ]
16753             }
16754         ]
16755     },
16756     
16757     footer : {
16758         tag: 'tfoot',
16759         cn: [
16760             {
16761                 tag: 'tr',
16762                 cn: [
16763                 {
16764                     tag: 'th',
16765                     colspan: '7',
16766                     cls: '',
16767                     cn: [
16768                         {
16769                             tag: 'button',
16770                             cls: 'btn btn-info ok',
16771                             html: 'OK'
16772                         }
16773                     ]
16774                 }
16775
16776                 ]
16777             }
16778         ]
16779     }
16780 });
16781
16782 Roo.apply(Roo.bootstrap.TimeField,  {
16783   
16784     template : {
16785         tag: 'div',
16786         cls: 'datepicker dropdown-menu',
16787         cn: [
16788             {
16789                 tag: 'div',
16790                 cls: 'datepicker-time',
16791                 cn: [
16792                 {
16793                     tag: 'table',
16794                     cls: 'table-condensed',
16795                     cn:[
16796                     Roo.bootstrap.TimeField.content,
16797                     Roo.bootstrap.TimeField.footer
16798                     ]
16799                 }
16800                 ]
16801             }
16802         ]
16803     }
16804 });
16805
16806  
16807
16808  /*
16809  * - LGPL
16810  *
16811  * MonthField
16812  * 
16813  */
16814
16815 /**
16816  * @class Roo.bootstrap.MonthField
16817  * @extends Roo.bootstrap.Input
16818  * Bootstrap MonthField class
16819  * 
16820  * @cfg {String} language default en
16821  * 
16822  * @constructor
16823  * Create a new MonthField
16824  * @param {Object} config The config object
16825  */
16826
16827 Roo.bootstrap.MonthField = function(config){
16828     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16829     
16830     this.addEvents({
16831         /**
16832          * @event show
16833          * Fires when this field show.
16834          * @param {Roo.bootstrap.MonthField} this
16835          * @param {Mixed} date The date value
16836          */
16837         show : true,
16838         /**
16839          * @event show
16840          * Fires when this field hide.
16841          * @param {Roo.bootstrap.MonthField} this
16842          * @param {Mixed} date The date value
16843          */
16844         hide : true,
16845         /**
16846          * @event select
16847          * Fires when select a date.
16848          * @param {Roo.bootstrap.MonthField} this
16849          * @param {String} oldvalue The old value
16850          * @param {String} newvalue The new value
16851          */
16852         select : true
16853     });
16854 };
16855
16856 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16857     
16858     onRender: function(ct, position)
16859     {
16860         
16861         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16862         
16863         this.language = this.language || 'en';
16864         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16865         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16866         
16867         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16868         this.isInline = false;
16869         this.isInput = true;
16870         this.component = this.el.select('.add-on', true).first() || false;
16871         this.component = (this.component && this.component.length === 0) ? false : this.component;
16872         this.hasInput = this.component && this.inputEL().length;
16873         
16874         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16875         
16876         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16877         
16878         this.picker().on('mousedown', this.onMousedown, this);
16879         this.picker().on('click', this.onClick, this);
16880         
16881         this.picker().addClass('datepicker-dropdown');
16882         
16883         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16884             v.setStyle('width', '189px');
16885         });
16886         
16887         this.fillMonths();
16888         
16889         this.update();
16890         
16891         if(this.isInline) {
16892             this.show();
16893         }
16894         
16895     },
16896     
16897     setValue: function(v, suppressEvent)
16898     {   
16899         var o = this.getValue();
16900         
16901         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16902         
16903         this.update();
16904
16905         if(suppressEvent !== true){
16906             this.fireEvent('select', this, o, v);
16907         }
16908         
16909     },
16910     
16911     getValue: function()
16912     {
16913         return this.value;
16914     },
16915     
16916     onClick: function(e) 
16917     {
16918         e.stopPropagation();
16919         e.preventDefault();
16920         
16921         var target = e.getTarget();
16922         
16923         if(target.nodeName.toLowerCase() === 'i'){
16924             target = Roo.get(target).dom.parentNode;
16925         }
16926         
16927         var nodeName = target.nodeName;
16928         var className = target.className;
16929         var html = target.innerHTML;
16930         
16931         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16932             return;
16933         }
16934         
16935         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16936         
16937         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16938         
16939         this.hide();
16940                         
16941     },
16942     
16943     picker : function()
16944     {
16945         return this.pickerEl;
16946     },
16947     
16948     fillMonths: function()
16949     {    
16950         var i = 0;
16951         var months = this.picker().select('>.datepicker-months td', true).first();
16952         
16953         months.dom.innerHTML = '';
16954         
16955         while (i < 12) {
16956             var month = {
16957                 tag: 'span',
16958                 cls: 'month',
16959                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16960             }
16961             
16962             months.createChild(month);
16963         }
16964         
16965     },
16966     
16967     update: function()
16968     {
16969         var _this = this;
16970         
16971         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16972             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16973         }
16974         
16975         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16976             e.removeClass('active');
16977             
16978             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16979                 e.addClass('active');
16980             }
16981         })
16982     },
16983     
16984     place: function()
16985     {
16986         if(this.isInline) return;
16987         
16988         this.picker().removeClass(['bottom', 'top']);
16989         
16990         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16991             /*
16992              * place to the top of element!
16993              *
16994              */
16995             
16996             this.picker().addClass('top');
16997             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16998             
16999             return;
17000         }
17001         
17002         this.picker().addClass('bottom');
17003         
17004         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17005     },
17006     
17007     onFocus : function()
17008     {
17009         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17010         this.show();
17011     },
17012     
17013     onBlur : function()
17014     {
17015         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17016         
17017         var d = this.inputEl().getValue();
17018         
17019         this.setValue(d);
17020                 
17021         this.hide();
17022     },
17023     
17024     show : function()
17025     {
17026         this.picker().show();
17027         this.picker().select('>.datepicker-months', true).first().show();
17028         this.update();
17029         this.place();
17030         
17031         this.fireEvent('show', this, this.date);
17032     },
17033     
17034     hide : function()
17035     {
17036         if(this.isInline) return;
17037         this.picker().hide();
17038         this.fireEvent('hide', this, this.date);
17039         
17040     },
17041     
17042     onMousedown: function(e)
17043     {
17044         e.stopPropagation();
17045         e.preventDefault();
17046     },
17047     
17048     keyup: function(e)
17049     {
17050         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17051         this.update();
17052     },
17053
17054     fireKey: function(e)
17055     {
17056         if (!this.picker().isVisible()){
17057             if (e.keyCode == 27) // allow escape to hide and re-show picker
17058                 this.show();
17059             return;
17060         }
17061         
17062         var dir;
17063         
17064         switch(e.keyCode){
17065             case 27: // escape
17066                 this.hide();
17067                 e.preventDefault();
17068                 break;
17069             case 37: // left
17070             case 39: // right
17071                 dir = e.keyCode == 37 ? -1 : 1;
17072                 
17073                 this.vIndex = this.vIndex + dir;
17074                 
17075                 if(this.vIndex < 0){
17076                     this.vIndex = 0;
17077                 }
17078                 
17079                 if(this.vIndex > 11){
17080                     this.vIndex = 11;
17081                 }
17082                 
17083                 if(isNaN(this.vIndex)){
17084                     this.vIndex = 0;
17085                 }
17086                 
17087                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17088                 
17089                 break;
17090             case 38: // up
17091             case 40: // down
17092                 
17093                 dir = e.keyCode == 38 ? -1 : 1;
17094                 
17095                 this.vIndex = this.vIndex + dir * 4;
17096                 
17097                 if(this.vIndex < 0){
17098                     this.vIndex = 0;
17099                 }
17100                 
17101                 if(this.vIndex > 11){
17102                     this.vIndex = 11;
17103                 }
17104                 
17105                 if(isNaN(this.vIndex)){
17106                     this.vIndex = 0;
17107                 }
17108                 
17109                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17110                 break;
17111                 
17112             case 13: // enter
17113                 
17114                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17115                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17116                 }
17117                 
17118                 this.hide();
17119                 e.preventDefault();
17120                 break;
17121             case 9: // tab
17122                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17123                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17124                 }
17125                 this.hide();
17126                 break;
17127             case 16: // shift
17128             case 17: // ctrl
17129             case 18: // alt
17130                 break;
17131             default :
17132                 this.hide();
17133                 
17134         }
17135     },
17136     
17137     remove: function() 
17138     {
17139         this.picker().remove();
17140     }
17141    
17142 });
17143
17144 Roo.apply(Roo.bootstrap.MonthField,  {
17145     
17146     content : {
17147         tag: 'tbody',
17148         cn: [
17149         {
17150             tag: 'tr',
17151             cn: [
17152             {
17153                 tag: 'td',
17154                 colspan: '7'
17155             }
17156             ]
17157         }
17158         ]
17159     },
17160     
17161     dates:{
17162         en: {
17163             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17164             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17165         }
17166     }
17167 });
17168
17169 Roo.apply(Roo.bootstrap.MonthField,  {
17170   
17171     template : {
17172         tag: 'div',
17173         cls: 'datepicker dropdown-menu roo-dynamic',
17174         cn: [
17175             {
17176                 tag: 'div',
17177                 cls: 'datepicker-months',
17178                 cn: [
17179                 {
17180                     tag: 'table',
17181                     cls: 'table-condensed',
17182                     cn:[
17183                         Roo.bootstrap.DateField.content
17184                     ]
17185                 }
17186                 ]
17187             }
17188         ]
17189     }
17190 });
17191
17192  
17193
17194  
17195  /*
17196  * - LGPL
17197  *
17198  * CheckBox
17199  * 
17200  */
17201
17202 /**
17203  * @class Roo.bootstrap.CheckBox
17204  * @extends Roo.bootstrap.Input
17205  * Bootstrap CheckBox class
17206  * 
17207  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17208  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17209  * @cfg {String} boxLabel The text that appears beside the checkbox
17210  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17211  * @cfg {Boolean} checked initnal the element
17212  * @cfg {Boolean} inline inline the element (default false)
17213  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17214  * 
17215  * @constructor
17216  * Create a new CheckBox
17217  * @param {Object} config The config object
17218  */
17219
17220 Roo.bootstrap.CheckBox = function(config){
17221     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17222    
17223     this.addEvents({
17224         /**
17225         * @event check
17226         * Fires when the element is checked or unchecked.
17227         * @param {Roo.bootstrap.CheckBox} this This input
17228         * @param {Boolean} checked The new checked value
17229         */
17230        check : true
17231     });
17232     
17233 };
17234
17235 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17236   
17237     inputType: 'checkbox',
17238     inputValue: 1,
17239     valueOff: 0,
17240     boxLabel: false,
17241     checked: false,
17242     weight : false,
17243     inline: false,
17244     
17245     getAutoCreate : function()
17246     {
17247         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17248         
17249         var id = Roo.id();
17250         
17251         var cfg = {};
17252         
17253         cfg.cls = 'form-group ' + this.inputType; //input-group
17254         
17255         if(this.inline){
17256             cfg.cls += ' ' + this.inputType + '-inline';
17257         }
17258         
17259         var input =  {
17260             tag: 'input',
17261             id : id,
17262             type : this.inputType,
17263             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17264             cls : 'roo-' + this.inputType, //'form-box',
17265             placeholder : this.placeholder || ''
17266             
17267         };
17268         
17269         if (this.weight) { // Validity check?
17270             cfg.cls += " " + this.inputType + "-" + this.weight;
17271         }
17272         
17273         if (this.disabled) {
17274             input.disabled=true;
17275         }
17276         
17277         if(this.checked){
17278             input.checked = this.checked;
17279         }
17280         
17281         if (this.name) {
17282             input.name = this.name;
17283         }
17284         
17285         if (this.size) {
17286             input.cls += ' input-' + this.size;
17287         }
17288         
17289         var settings=this;
17290         
17291         ['xs','sm','md','lg'].map(function(size){
17292             if (settings[size]) {
17293                 cfg.cls += ' col-' + size + '-' + settings[size];
17294             }
17295         });
17296         
17297         var inputblock = input;
17298          
17299         if (this.before || this.after) {
17300             
17301             inputblock = {
17302                 cls : 'input-group',
17303                 cn :  [] 
17304             };
17305             
17306             if (this.before) {
17307                 inputblock.cn.push({
17308                     tag :'span',
17309                     cls : 'input-group-addon',
17310                     html : this.before
17311                 });
17312             }
17313             
17314             inputblock.cn.push(input);
17315             
17316             if (this.after) {
17317                 inputblock.cn.push({
17318                     tag :'span',
17319                     cls : 'input-group-addon',
17320                     html : this.after
17321                 });
17322             }
17323             
17324         }
17325         
17326         if (align ==='left' && this.fieldLabel.length) {
17327                 Roo.log("left and has label");
17328                 cfg.cn = [
17329                     
17330                     {
17331                         tag: 'label',
17332                         'for' :  id,
17333                         cls : 'control-label col-md-' + this.labelWidth,
17334                         html : this.fieldLabel
17335                         
17336                     },
17337                     {
17338                         cls : "col-md-" + (12 - this.labelWidth), 
17339                         cn: [
17340                             inputblock
17341                         ]
17342                     }
17343                     
17344                 ];
17345         } else if ( this.fieldLabel.length) {
17346                 Roo.log(" label");
17347                 cfg.cn = [
17348                    
17349                     {
17350                         tag: this.boxLabel ? 'span' : 'label',
17351                         'for': id,
17352                         cls: 'control-label box-input-label',
17353                         //cls : 'input-group-addon',
17354                         html : this.fieldLabel
17355                         
17356                     },
17357                     
17358                     inputblock
17359                     
17360                 ];
17361
17362         } else {
17363             
17364                 Roo.log(" no label && no align");
17365                 cfg.cn = [  inputblock ] ;
17366                 
17367                 
17368         }
17369         if(this.boxLabel){
17370              var boxLabelCfg = {
17371                 tag: 'label',
17372                 //'for': id, // box label is handled by onclick - so no for...
17373                 cls: 'box-label',
17374                 html: this.boxLabel
17375             }
17376             
17377             if(this.tooltip){
17378                 boxLabelCfg.tooltip = this.tooltip;
17379             }
17380              
17381             cfg.cn.push(boxLabelCfg);
17382         }
17383         
17384         
17385        
17386         return cfg;
17387         
17388     },
17389     
17390     /**
17391      * return the real input element.
17392      */
17393     inputEl: function ()
17394     {
17395         return this.el.select('input.roo-' + this.inputType,true).first();
17396     },
17397     
17398     labelEl: function()
17399     {
17400         return this.el.select('label.control-label',true).first();
17401     },
17402     /* depricated... */
17403     
17404     label: function()
17405     {
17406         return this.labelEl();
17407     },
17408     
17409     initEvents : function()
17410     {
17411 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17412         
17413         this.inputEl().on('click', this.onClick,  this);
17414         
17415         if (this.boxLabel) { 
17416             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17417         }
17418         
17419         this.startValue = this.getValue();
17420         
17421         if(this.groupId){
17422             Roo.bootstrap.CheckBox.register(this);
17423         }
17424     },
17425     
17426     onClick : function()
17427     {   
17428         this.setChecked(!this.checked);
17429     },
17430     
17431     setChecked : function(state,suppressEvent)
17432     {
17433         this.startValue = this.getValue();
17434         
17435         if(this.inputType == 'radio'){
17436             
17437             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17438                 e.dom.checked = false;
17439             });
17440             
17441             this.inputEl().dom.checked = true;
17442             
17443             this.inputEl().dom.value = this.inputValue;
17444             
17445             if(suppressEvent !== true){
17446                 this.fireEvent('check', this, true);
17447             }
17448             
17449             this.validate();
17450             
17451             return;
17452         }
17453         
17454         this.checked = state;
17455         
17456         this.inputEl().dom.checked = state;
17457         
17458         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17459         
17460         if(suppressEvent !== true){
17461             this.fireEvent('check', this, state);
17462         }
17463         
17464         this.validate();
17465     },
17466     
17467     getValue : function()
17468     {
17469         if(this.inputType == 'radio'){
17470             return this.getGroupValue();
17471         }
17472         
17473         return this.inputEl().getValue();
17474         
17475     },
17476     
17477     getGroupValue : function()
17478     {
17479         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17480             return '';
17481         }
17482         
17483         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17484     },
17485     
17486     setValue : function(v,suppressEvent)
17487     {
17488         if(this.inputType == 'radio'){
17489             this.setGroupValue(v, suppressEvent);
17490             return;
17491         }
17492         
17493         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17494         
17495         this.validate();
17496     },
17497     
17498     setGroupValue : function(v, suppressEvent)
17499     {
17500         this.startValue = this.getValue();
17501         
17502         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17503             e.dom.checked = false;
17504             
17505             if(e.dom.value == v){
17506                 e.dom.checked = true;
17507             }
17508         });
17509         
17510         if(suppressEvent !== true){
17511             this.fireEvent('check', this, true);
17512         }
17513
17514         this.validate();
17515         
17516         return;
17517     },
17518     
17519     validate : function()
17520     {
17521         if(
17522                 this.disabled || 
17523                 (this.inputType == 'radio' && this.validateRadio()) ||
17524                 (this.inputType == 'checkbox' && this.validateCheckbox())
17525         ){
17526             this.markValid();
17527             return true;
17528         }
17529         
17530         this.markInvalid();
17531         return false;
17532     },
17533     
17534     validateRadio : function()
17535     {
17536         var valid = false;
17537         
17538         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17539             if(!e.dom.checked){
17540                 return;
17541             }
17542             
17543             valid = true;
17544             
17545             return false;
17546         });
17547         
17548         return valid;
17549     },
17550     
17551     validateCheckbox : function()
17552     {
17553         if(!this.groupId){
17554             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17555         }
17556         
17557         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17558         
17559         if(!group){
17560             return false;
17561         }
17562         
17563         var r = false;
17564         
17565         for(var i in group){
17566             if(r){
17567                 break;
17568             }
17569             
17570             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17571         }
17572         
17573         return r;
17574     },
17575     
17576     /**
17577      * Mark this field as valid
17578      */
17579     markValid : function()
17580     {
17581         if(this.allowBlank){
17582             return;
17583         }
17584         
17585         var _this = this;
17586         
17587         this.fireEvent('valid', this);
17588         
17589         if(this.inputType == 'radio'){
17590             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17591                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17592                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17593             });
17594             
17595             return;
17596         }
17597         
17598         if(!this.groupId){
17599             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17600             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17601             return;
17602         }
17603         
17604         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17605             
17606         if(!group){
17607             return;
17608         }
17609         
17610         for(var i in group){
17611             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17612             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17613         }
17614     },
17615     
17616      /**
17617      * Mark this field as invalid
17618      * @param {String} msg The validation message
17619      */
17620     markInvalid : function(msg)
17621     {
17622         if(this.allowBlank){
17623             return;
17624         }
17625         
17626         var _this = this;
17627         
17628         this.fireEvent('invalid', this, msg);
17629         
17630         if(this.inputType == 'radio'){
17631             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17632                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17633                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17634             });
17635             
17636             return;
17637         }
17638         
17639         if(!this.groupId){
17640             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17641             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17642             return;
17643         }
17644         
17645         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17646             
17647         if(!group){
17648             return;
17649         }
17650         
17651         for(var i in group){
17652             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17653             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17654         }
17655         
17656     }
17657     
17658 });
17659
17660 Roo.apply(Roo.bootstrap.CheckBox, {
17661     
17662     groups: {},
17663     
17664      /**
17665     * register a CheckBox Group
17666     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17667     */
17668     register : function(checkbox)
17669     {
17670         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17671             this.groups[checkbox.groupId] = {};
17672         }
17673         
17674         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17675             return;
17676         }
17677         
17678         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17679         
17680     },
17681     /**
17682     * fetch a CheckBox Group based on the group ID
17683     * @param {string} the group ID
17684     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17685     */
17686     get: function(groupId) {
17687         if (typeof(this.groups[groupId]) == 'undefined') {
17688             return false;
17689         }
17690         
17691         return this.groups[groupId] ;
17692     }
17693     
17694     
17695 });
17696 /*
17697  * - LGPL
17698  *
17699  * Radio
17700  *
17701  *
17702  * not inline
17703  *<div class="radio">
17704   <label>
17705     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17706     Option one is this and that&mdash;be sure to include why it's great
17707   </label>
17708 </div>
17709  *
17710  *
17711  *inline
17712  *<span>
17713  *<label class="radio-inline">fieldLabel</label>
17714  *<label class="radio-inline">
17715   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17716 </label>
17717 <span>
17718  * 
17719  * 
17720  */
17721
17722 /**
17723  * @class Roo.bootstrap.Radio
17724  * @extends Roo.bootstrap.CheckBox
17725  * Bootstrap Radio class
17726
17727  * @constructor
17728  * Create a new Radio
17729  * @param {Object} config The config object
17730  */
17731
17732 Roo.bootstrap.Radio = function(config){
17733     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17734    
17735 };
17736
17737 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17738     
17739     inputType: 'radio',
17740     inputValue: '',
17741     valueOff: '',
17742     
17743     getAutoCreate : function()
17744     {
17745         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17746         align = align || 'left'; // default...
17747         
17748         
17749         
17750         var id = Roo.id();
17751         
17752         var cfg = {
17753                 tag : this.inline ? 'span' : 'div',
17754                 cls : '',
17755                 cn : []
17756         };
17757         
17758         var inline = this.inline ? ' radio-inline' : '';
17759         
17760         var lbl = {
17761                 tag: 'label' ,
17762                 // does not need for, as we wrap the input with it..
17763                 'for' : id,
17764                 cls : 'control-label box-label' + inline,
17765                 cn : []
17766         };
17767         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17768         
17769         var fieldLabel = {
17770             tag: 'label' ,
17771             //cls : 'control-label' + inline,
17772             html : this.fieldLabel,
17773             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17774         };
17775         
17776  
17777         
17778         
17779         var input =  {
17780             tag: 'input',
17781             id : id,
17782             type : this.inputType,
17783             //value : (!this.checked) ? this.valueOff : this.inputValue,
17784             value : this.inputValue,
17785             cls : 'roo-radio',
17786             placeholder : this.placeholder || '' // ?? needed????
17787             
17788         };
17789         if (this.weight) { // Validity check?
17790             input.cls += " radio-" + this.weight;
17791         }
17792         if (this.disabled) {
17793             input.disabled=true;
17794         }
17795         
17796         if(this.checked){
17797             input.checked = this.checked;
17798         }
17799         
17800         if (this.name) {
17801             input.name = this.name;
17802         }
17803         
17804         if (this.size) {
17805             input.cls += ' input-' + this.size;
17806         }
17807         
17808         //?? can span's inline have a width??
17809         
17810         var settings=this;
17811         ['xs','sm','md','lg'].map(function(size){
17812             if (settings[size]) {
17813                 cfg.cls += ' col-' + size + '-' + settings[size];
17814             }
17815         });
17816         
17817         var inputblock = input;
17818         
17819         if (this.before || this.after) {
17820             
17821             inputblock = {
17822                 cls : 'input-group',
17823                 tag : 'span',
17824                 cn :  [] 
17825             };
17826             if (this.before) {
17827                 inputblock.cn.push({
17828                     tag :'span',
17829                     cls : 'input-group-addon',
17830                     html : this.before
17831                 });
17832             }
17833             inputblock.cn.push(input);
17834             if (this.after) {
17835                 inputblock.cn.push({
17836                     tag :'span',
17837                     cls : 'input-group-addon',
17838                     html : this.after
17839                 });
17840             }
17841             
17842         };
17843         
17844         
17845         if (this.fieldLabel && this.fieldLabel.length) {
17846             cfg.cn.push(fieldLabel);
17847         }
17848        
17849         // normal bootstrap puts the input inside the label.
17850         // however with our styled version - it has to go after the input.
17851        
17852         //lbl.cn.push(inputblock);
17853         
17854         var lblwrap =  {
17855             tag: 'span',
17856             cls: 'radio' + inline,
17857             cn: [
17858                 inputblock,
17859                 lbl
17860             ]
17861         };
17862         
17863         cfg.cn.push( lblwrap);
17864         
17865         if(this.boxLabel){
17866             lbl.cn.push({
17867                 tag: 'span',
17868                 html: this.boxLabel
17869             })
17870         }
17871          
17872         
17873         return cfg;
17874         
17875     },
17876     
17877     initEvents : function()
17878     {
17879 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17880         
17881         this.inputEl().on('click', this.onClick,  this);
17882         if (this.boxLabel) {
17883             Roo.log('find label')
17884             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17885         }
17886         
17887     },
17888     
17889     inputEl: function ()
17890     {
17891         return this.el.select('input.roo-radio',true).first();
17892     },
17893     onClick : function()
17894     {   
17895         Roo.log("click");
17896         this.setChecked(true);
17897     },
17898     
17899     setChecked : function(state,suppressEvent)
17900     {
17901         if(state){
17902             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17903                 v.dom.checked = false;
17904             });
17905         }
17906         Roo.log(this.inputEl().dom);
17907         this.checked = state;
17908         this.inputEl().dom.checked = state;
17909         
17910         if(suppressEvent !== true){
17911             this.fireEvent('check', this, state);
17912         }
17913         
17914         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17915         
17916     },
17917     
17918     getGroupValue : function()
17919     {
17920         var value = '';
17921         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17922             if(v.dom.checked == true){
17923                 value = v.dom.value;
17924             }
17925         });
17926         
17927         return value;
17928     },
17929     
17930     /**
17931      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17932      * @return {Mixed} value The field value
17933      */
17934     getValue : function(){
17935         return this.getGroupValue();
17936     }
17937     
17938 });
17939
17940  
17941 //<script type="text/javascript">
17942
17943 /*
17944  * Based  Ext JS Library 1.1.1
17945  * Copyright(c) 2006-2007, Ext JS, LLC.
17946  * LGPL
17947  *
17948  */
17949  
17950 /**
17951  * @class Roo.HtmlEditorCore
17952  * @extends Roo.Component
17953  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17954  *
17955  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17956  */
17957
17958 Roo.HtmlEditorCore = function(config){
17959     
17960     
17961     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17962     
17963     
17964     this.addEvents({
17965         /**
17966          * @event initialize
17967          * Fires when the editor is fully initialized (including the iframe)
17968          * @param {Roo.HtmlEditorCore} this
17969          */
17970         initialize: true,
17971         /**
17972          * @event activate
17973          * Fires when the editor is first receives the focus. Any insertion must wait
17974          * until after this event.
17975          * @param {Roo.HtmlEditorCore} this
17976          */
17977         activate: true,
17978          /**
17979          * @event beforesync
17980          * Fires before the textarea is updated with content from the editor iframe. Return false
17981          * to cancel the sync.
17982          * @param {Roo.HtmlEditorCore} this
17983          * @param {String} html
17984          */
17985         beforesync: true,
17986          /**
17987          * @event beforepush
17988          * Fires before the iframe editor is updated with content from the textarea. Return false
17989          * to cancel the push.
17990          * @param {Roo.HtmlEditorCore} this
17991          * @param {String} html
17992          */
17993         beforepush: true,
17994          /**
17995          * @event sync
17996          * Fires when the textarea is updated with content from the editor iframe.
17997          * @param {Roo.HtmlEditorCore} this
17998          * @param {String} html
17999          */
18000         sync: true,
18001          /**
18002          * @event push
18003          * Fires when the iframe editor is updated with content from the textarea.
18004          * @param {Roo.HtmlEditorCore} this
18005          * @param {String} html
18006          */
18007         push: true,
18008         
18009         /**
18010          * @event editorevent
18011          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18012          * @param {Roo.HtmlEditorCore} this
18013          */
18014         editorevent: true
18015         
18016     });
18017     
18018     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18019     
18020     // defaults : white / black...
18021     this.applyBlacklists();
18022     
18023     
18024     
18025 };
18026
18027
18028 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18029
18030
18031      /**
18032      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18033      */
18034     
18035     owner : false,
18036     
18037      /**
18038      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18039      *                        Roo.resizable.
18040      */
18041     resizable : false,
18042      /**
18043      * @cfg {Number} height (in pixels)
18044      */   
18045     height: 300,
18046    /**
18047      * @cfg {Number} width (in pixels)
18048      */   
18049     width: 500,
18050     
18051     /**
18052      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18053      * 
18054      */
18055     stylesheets: false,
18056     
18057     // id of frame..
18058     frameId: false,
18059     
18060     // private properties
18061     validationEvent : false,
18062     deferHeight: true,
18063     initialized : false,
18064     activated : false,
18065     sourceEditMode : false,
18066     onFocus : Roo.emptyFn,
18067     iframePad:3,
18068     hideMode:'offsets',
18069     
18070     clearUp: true,
18071     
18072     // blacklist + whitelisted elements..
18073     black: false,
18074     white: false,
18075      
18076     
18077
18078     /**
18079      * Protected method that will not generally be called directly. It
18080      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18081      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18082      */
18083     getDocMarkup : function(){
18084         // body styles..
18085         var st = '';
18086         
18087         // inherit styels from page...?? 
18088         if (this.stylesheets === false) {
18089             
18090             Roo.get(document.head).select('style').each(function(node) {
18091                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18092             });
18093             
18094             Roo.get(document.head).select('link').each(function(node) { 
18095                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18096             });
18097             
18098         } else if (!this.stylesheets.length) {
18099                 // simple..
18100                 st = '<style type="text/css">' +
18101                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18102                    '</style>';
18103         } else { 
18104             
18105         }
18106         
18107         st +=  '<style type="text/css">' +
18108             'IMG { cursor: pointer } ' +
18109         '</style>';
18110
18111         
18112         return '<html><head>' + st  +
18113             //<style type="text/css">' +
18114             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18115             //'</style>' +
18116             ' </head><body class="roo-htmleditor-body"></body></html>';
18117     },
18118
18119     // private
18120     onRender : function(ct, position)
18121     {
18122         var _t = this;
18123         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18124         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18125         
18126         
18127         this.el.dom.style.border = '0 none';
18128         this.el.dom.setAttribute('tabIndex', -1);
18129         this.el.addClass('x-hidden hide');
18130         
18131         
18132         
18133         if(Roo.isIE){ // fix IE 1px bogus margin
18134             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18135         }
18136        
18137         
18138         this.frameId = Roo.id();
18139         
18140          
18141         
18142         var iframe = this.owner.wrap.createChild({
18143             tag: 'iframe',
18144             cls: 'form-control', // bootstrap..
18145             id: this.frameId,
18146             name: this.frameId,
18147             frameBorder : 'no',
18148             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18149         }, this.el
18150         );
18151         
18152         
18153         this.iframe = iframe.dom;
18154
18155          this.assignDocWin();
18156         
18157         this.doc.designMode = 'on';
18158        
18159         this.doc.open();
18160         this.doc.write(this.getDocMarkup());
18161         this.doc.close();
18162
18163         
18164         var task = { // must defer to wait for browser to be ready
18165             run : function(){
18166                 //console.log("run task?" + this.doc.readyState);
18167                 this.assignDocWin();
18168                 if(this.doc.body || this.doc.readyState == 'complete'){
18169                     try {
18170                         this.doc.designMode="on";
18171                     } catch (e) {
18172                         return;
18173                     }
18174                     Roo.TaskMgr.stop(task);
18175                     this.initEditor.defer(10, this);
18176                 }
18177             },
18178             interval : 10,
18179             duration: 10000,
18180             scope: this
18181         };
18182         Roo.TaskMgr.start(task);
18183
18184     },
18185
18186     // private
18187     onResize : function(w, h)
18188     {
18189          Roo.log('resize: ' +w + ',' + h );
18190         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18191         if(!this.iframe){
18192             return;
18193         }
18194         if(typeof w == 'number'){
18195             
18196             this.iframe.style.width = w + 'px';
18197         }
18198         if(typeof h == 'number'){
18199             
18200             this.iframe.style.height = h + 'px';
18201             if(this.doc){
18202                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18203             }
18204         }
18205         
18206     },
18207
18208     /**
18209      * Toggles the editor between standard and source edit mode.
18210      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18211      */
18212     toggleSourceEdit : function(sourceEditMode){
18213         
18214         this.sourceEditMode = sourceEditMode === true;
18215         
18216         if(this.sourceEditMode){
18217  
18218             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18219             
18220         }else{
18221             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18222             //this.iframe.className = '';
18223             this.deferFocus();
18224         }
18225         //this.setSize(this.owner.wrap.getSize());
18226         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18227     },
18228
18229     
18230   
18231
18232     /**
18233      * Protected method that will not generally be called directly. If you need/want
18234      * custom HTML cleanup, this is the method you should override.
18235      * @param {String} html The HTML to be cleaned
18236      * return {String} The cleaned HTML
18237      */
18238     cleanHtml : function(html){
18239         html = String(html);
18240         if(html.length > 5){
18241             if(Roo.isSafari){ // strip safari nonsense
18242                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18243             }
18244         }
18245         if(html == '&nbsp;'){
18246             html = '';
18247         }
18248         return html;
18249     },
18250
18251     /**
18252      * HTML Editor -> Textarea
18253      * Protected method that will not generally be called directly. Syncs the contents
18254      * of the editor iframe with the textarea.
18255      */
18256     syncValue : function(){
18257         if(this.initialized){
18258             var bd = (this.doc.body || this.doc.documentElement);
18259             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18260             var html = bd.innerHTML;
18261             if(Roo.isSafari){
18262                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18263                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18264                 if(m && m[1]){
18265                     html = '<div style="'+m[0]+'">' + html + '</div>';
18266                 }
18267             }
18268             html = this.cleanHtml(html);
18269             // fix up the special chars.. normaly like back quotes in word...
18270             // however we do not want to do this with chinese..
18271             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18272                 var cc = b.charCodeAt();
18273                 if (
18274                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18275                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18276                     (cc >= 0xf900 && cc < 0xfb00 )
18277                 ) {
18278                         return b;
18279                 }
18280                 return "&#"+cc+";" 
18281             });
18282             if(this.owner.fireEvent('beforesync', this, html) !== false){
18283                 this.el.dom.value = html;
18284                 this.owner.fireEvent('sync', this, html);
18285             }
18286         }
18287     },
18288
18289     /**
18290      * Protected method that will not generally be called directly. Pushes the value of the textarea
18291      * into the iframe editor.
18292      */
18293     pushValue : function(){
18294         if(this.initialized){
18295             var v = this.el.dom.value.trim();
18296             
18297 //            if(v.length < 1){
18298 //                v = '&#160;';
18299 //            }
18300             
18301             if(this.owner.fireEvent('beforepush', this, v) !== false){
18302                 var d = (this.doc.body || this.doc.documentElement);
18303                 d.innerHTML = v;
18304                 this.cleanUpPaste();
18305                 this.el.dom.value = d.innerHTML;
18306                 this.owner.fireEvent('push', this, v);
18307             }
18308         }
18309     },
18310
18311     // private
18312     deferFocus : function(){
18313         this.focus.defer(10, this);
18314     },
18315
18316     // doc'ed in Field
18317     focus : function(){
18318         if(this.win && !this.sourceEditMode){
18319             this.win.focus();
18320         }else{
18321             this.el.focus();
18322         }
18323     },
18324     
18325     assignDocWin: function()
18326     {
18327         var iframe = this.iframe;
18328         
18329          if(Roo.isIE){
18330             this.doc = iframe.contentWindow.document;
18331             this.win = iframe.contentWindow;
18332         } else {
18333 //            if (!Roo.get(this.frameId)) {
18334 //                return;
18335 //            }
18336 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18337 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18338             
18339             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18340                 return;
18341             }
18342             
18343             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18344             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18345         }
18346     },
18347     
18348     // private
18349     initEditor : function(){
18350         //console.log("INIT EDITOR");
18351         this.assignDocWin();
18352         
18353         
18354         
18355         this.doc.designMode="on";
18356         this.doc.open();
18357         this.doc.write(this.getDocMarkup());
18358         this.doc.close();
18359         
18360         var dbody = (this.doc.body || this.doc.documentElement);
18361         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18362         // this copies styles from the containing element into thsi one..
18363         // not sure why we need all of this..
18364         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18365         
18366         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18367         //ss['background-attachment'] = 'fixed'; // w3c
18368         dbody.bgProperties = 'fixed'; // ie
18369         //Roo.DomHelper.applyStyles(dbody, ss);
18370         Roo.EventManager.on(this.doc, {
18371             //'mousedown': this.onEditorEvent,
18372             'mouseup': this.onEditorEvent,
18373             'dblclick': this.onEditorEvent,
18374             'click': this.onEditorEvent,
18375             'keyup': this.onEditorEvent,
18376             buffer:100,
18377             scope: this
18378         });
18379         if(Roo.isGecko){
18380             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18381         }
18382         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18383             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18384         }
18385         this.initialized = true;
18386
18387         this.owner.fireEvent('initialize', this);
18388         this.pushValue();
18389     },
18390
18391     // private
18392     onDestroy : function(){
18393         
18394         
18395         
18396         if(this.rendered){
18397             
18398             //for (var i =0; i < this.toolbars.length;i++) {
18399             //    // fixme - ask toolbars for heights?
18400             //    this.toolbars[i].onDestroy();
18401            // }
18402             
18403             //this.wrap.dom.innerHTML = '';
18404             //this.wrap.remove();
18405         }
18406     },
18407
18408     // private
18409     onFirstFocus : function(){
18410         
18411         this.assignDocWin();
18412         
18413         
18414         this.activated = true;
18415          
18416     
18417         if(Roo.isGecko){ // prevent silly gecko errors
18418             this.win.focus();
18419             var s = this.win.getSelection();
18420             if(!s.focusNode || s.focusNode.nodeType != 3){
18421                 var r = s.getRangeAt(0);
18422                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18423                 r.collapse(true);
18424                 this.deferFocus();
18425             }
18426             try{
18427                 this.execCmd('useCSS', true);
18428                 this.execCmd('styleWithCSS', false);
18429             }catch(e){}
18430         }
18431         this.owner.fireEvent('activate', this);
18432     },
18433
18434     // private
18435     adjustFont: function(btn){
18436         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18437         //if(Roo.isSafari){ // safari
18438         //    adjust *= 2;
18439        // }
18440         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18441         if(Roo.isSafari){ // safari
18442             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18443             v =  (v < 10) ? 10 : v;
18444             v =  (v > 48) ? 48 : v;
18445             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18446             
18447         }
18448         
18449         
18450         v = Math.max(1, v+adjust);
18451         
18452         this.execCmd('FontSize', v  );
18453     },
18454
18455     onEditorEvent : function(e){
18456         this.owner.fireEvent('editorevent', this, e);
18457       //  this.updateToolbar();
18458         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18459     },
18460
18461     insertTag : function(tg)
18462     {
18463         // could be a bit smarter... -> wrap the current selected tRoo..
18464         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18465             
18466             range = this.createRange(this.getSelection());
18467             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18468             wrappingNode.appendChild(range.extractContents());
18469             range.insertNode(wrappingNode);
18470
18471             return;
18472             
18473             
18474             
18475         }
18476         this.execCmd("formatblock",   tg);
18477         
18478     },
18479     
18480     insertText : function(txt)
18481     {
18482         
18483         
18484         var range = this.createRange();
18485         range.deleteContents();
18486                //alert(Sender.getAttribute('label'));
18487                
18488         range.insertNode(this.doc.createTextNode(txt));
18489     } ,
18490     
18491      
18492
18493     /**
18494      * Executes a Midas editor command on the editor document and performs necessary focus and
18495      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18496      * @param {String} cmd The Midas command
18497      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18498      */
18499     relayCmd : function(cmd, value){
18500         this.win.focus();
18501         this.execCmd(cmd, value);
18502         this.owner.fireEvent('editorevent', this);
18503         //this.updateToolbar();
18504         this.owner.deferFocus();
18505     },
18506
18507     /**
18508      * Executes a Midas editor command directly on the editor document.
18509      * For visual commands, you should use {@link #relayCmd} instead.
18510      * <b>This should only be called after the editor is initialized.</b>
18511      * @param {String} cmd The Midas command
18512      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18513      */
18514     execCmd : function(cmd, value){
18515         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18516         this.syncValue();
18517     },
18518  
18519  
18520    
18521     /**
18522      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18523      * to insert tRoo.
18524      * @param {String} text | dom node.. 
18525      */
18526     insertAtCursor : function(text)
18527     {
18528         
18529         
18530         
18531         if(!this.activated){
18532             return;
18533         }
18534         /*
18535         if(Roo.isIE){
18536             this.win.focus();
18537             var r = this.doc.selection.createRange();
18538             if(r){
18539                 r.collapse(true);
18540                 r.pasteHTML(text);
18541                 this.syncValue();
18542                 this.deferFocus();
18543             
18544             }
18545             return;
18546         }
18547         */
18548         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18549             this.win.focus();
18550             
18551             
18552             // from jquery ui (MIT licenced)
18553             var range, node;
18554             var win = this.win;
18555             
18556             if (win.getSelection && win.getSelection().getRangeAt) {
18557                 range = win.getSelection().getRangeAt(0);
18558                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18559                 range.insertNode(node);
18560             } else if (win.document.selection && win.document.selection.createRange) {
18561                 // no firefox support
18562                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18563                 win.document.selection.createRange().pasteHTML(txt);
18564             } else {
18565                 // no firefox support
18566                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18567                 this.execCmd('InsertHTML', txt);
18568             } 
18569             
18570             this.syncValue();
18571             
18572             this.deferFocus();
18573         }
18574     },
18575  // private
18576     mozKeyPress : function(e){
18577         if(e.ctrlKey){
18578             var c = e.getCharCode(), cmd;
18579           
18580             if(c > 0){
18581                 c = String.fromCharCode(c).toLowerCase();
18582                 switch(c){
18583                     case 'b':
18584                         cmd = 'bold';
18585                         break;
18586                     case 'i':
18587                         cmd = 'italic';
18588                         break;
18589                     
18590                     case 'u':
18591                         cmd = 'underline';
18592                         break;
18593                     
18594                     case 'v':
18595                         this.cleanUpPaste.defer(100, this);
18596                         return;
18597                         
18598                 }
18599                 if(cmd){
18600                     this.win.focus();
18601                     this.execCmd(cmd);
18602                     this.deferFocus();
18603                     e.preventDefault();
18604                 }
18605                 
18606             }
18607         }
18608     },
18609
18610     // private
18611     fixKeys : function(){ // load time branching for fastest keydown performance
18612         if(Roo.isIE){
18613             return function(e){
18614                 var k = e.getKey(), r;
18615                 if(k == e.TAB){
18616                     e.stopEvent();
18617                     r = this.doc.selection.createRange();
18618                     if(r){
18619                         r.collapse(true);
18620                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18621                         this.deferFocus();
18622                     }
18623                     return;
18624                 }
18625                 
18626                 if(k == e.ENTER){
18627                     r = this.doc.selection.createRange();
18628                     if(r){
18629                         var target = r.parentElement();
18630                         if(!target || target.tagName.toLowerCase() != 'li'){
18631                             e.stopEvent();
18632                             r.pasteHTML('<br />');
18633                             r.collapse(false);
18634                             r.select();
18635                         }
18636                     }
18637                 }
18638                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18639                     this.cleanUpPaste.defer(100, this);
18640                     return;
18641                 }
18642                 
18643                 
18644             };
18645         }else if(Roo.isOpera){
18646             return function(e){
18647                 var k = e.getKey();
18648                 if(k == e.TAB){
18649                     e.stopEvent();
18650                     this.win.focus();
18651                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18652                     this.deferFocus();
18653                 }
18654                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18655                     this.cleanUpPaste.defer(100, this);
18656                     return;
18657                 }
18658                 
18659             };
18660         }else if(Roo.isSafari){
18661             return function(e){
18662                 var k = e.getKey();
18663                 
18664                 if(k == e.TAB){
18665                     e.stopEvent();
18666                     this.execCmd('InsertText','\t');
18667                     this.deferFocus();
18668                     return;
18669                 }
18670                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18671                     this.cleanUpPaste.defer(100, this);
18672                     return;
18673                 }
18674                 
18675              };
18676         }
18677     }(),
18678     
18679     getAllAncestors: function()
18680     {
18681         var p = this.getSelectedNode();
18682         var a = [];
18683         if (!p) {
18684             a.push(p); // push blank onto stack..
18685             p = this.getParentElement();
18686         }
18687         
18688         
18689         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18690             a.push(p);
18691             p = p.parentNode;
18692         }
18693         a.push(this.doc.body);
18694         return a;
18695     },
18696     lastSel : false,
18697     lastSelNode : false,
18698     
18699     
18700     getSelection : function() 
18701     {
18702         this.assignDocWin();
18703         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18704     },
18705     
18706     getSelectedNode: function() 
18707     {
18708         // this may only work on Gecko!!!
18709         
18710         // should we cache this!!!!
18711         
18712         
18713         
18714          
18715         var range = this.createRange(this.getSelection()).cloneRange();
18716         
18717         if (Roo.isIE) {
18718             var parent = range.parentElement();
18719             while (true) {
18720                 var testRange = range.duplicate();
18721                 testRange.moveToElementText(parent);
18722                 if (testRange.inRange(range)) {
18723                     break;
18724                 }
18725                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18726                     break;
18727                 }
18728                 parent = parent.parentElement;
18729             }
18730             return parent;
18731         }
18732         
18733         // is ancestor a text element.
18734         var ac =  range.commonAncestorContainer;
18735         if (ac.nodeType == 3) {
18736             ac = ac.parentNode;
18737         }
18738         
18739         var ar = ac.childNodes;
18740          
18741         var nodes = [];
18742         var other_nodes = [];
18743         var has_other_nodes = false;
18744         for (var i=0;i<ar.length;i++) {
18745             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18746                 continue;
18747             }
18748             // fullly contained node.
18749             
18750             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18751                 nodes.push(ar[i]);
18752                 continue;
18753             }
18754             
18755             // probably selected..
18756             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18757                 other_nodes.push(ar[i]);
18758                 continue;
18759             }
18760             // outer..
18761             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18762                 continue;
18763             }
18764             
18765             
18766             has_other_nodes = true;
18767         }
18768         if (!nodes.length && other_nodes.length) {
18769             nodes= other_nodes;
18770         }
18771         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18772             return false;
18773         }
18774         
18775         return nodes[0];
18776     },
18777     createRange: function(sel)
18778     {
18779         // this has strange effects when using with 
18780         // top toolbar - not sure if it's a great idea.
18781         //this.editor.contentWindow.focus();
18782         if (typeof sel != "undefined") {
18783             try {
18784                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18785             } catch(e) {
18786                 return this.doc.createRange();
18787             }
18788         } else {
18789             return this.doc.createRange();
18790         }
18791     },
18792     getParentElement: function()
18793     {
18794         
18795         this.assignDocWin();
18796         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18797         
18798         var range = this.createRange(sel);
18799          
18800         try {
18801             var p = range.commonAncestorContainer;
18802             while (p.nodeType == 3) { // text node
18803                 p = p.parentNode;
18804             }
18805             return p;
18806         } catch (e) {
18807             return null;
18808         }
18809     
18810     },
18811     /***
18812      *
18813      * Range intersection.. the hard stuff...
18814      *  '-1' = before
18815      *  '0' = hits..
18816      *  '1' = after.
18817      *         [ -- selected range --- ]
18818      *   [fail]                        [fail]
18819      *
18820      *    basically..
18821      *      if end is before start or  hits it. fail.
18822      *      if start is after end or hits it fail.
18823      *
18824      *   if either hits (but other is outside. - then it's not 
18825      *   
18826      *    
18827      **/
18828     
18829     
18830     // @see http://www.thismuchiknow.co.uk/?p=64.
18831     rangeIntersectsNode : function(range, node)
18832     {
18833         var nodeRange = node.ownerDocument.createRange();
18834         try {
18835             nodeRange.selectNode(node);
18836         } catch (e) {
18837             nodeRange.selectNodeContents(node);
18838         }
18839     
18840         var rangeStartRange = range.cloneRange();
18841         rangeStartRange.collapse(true);
18842     
18843         var rangeEndRange = range.cloneRange();
18844         rangeEndRange.collapse(false);
18845     
18846         var nodeStartRange = nodeRange.cloneRange();
18847         nodeStartRange.collapse(true);
18848     
18849         var nodeEndRange = nodeRange.cloneRange();
18850         nodeEndRange.collapse(false);
18851     
18852         return rangeStartRange.compareBoundaryPoints(
18853                  Range.START_TO_START, nodeEndRange) == -1 &&
18854                rangeEndRange.compareBoundaryPoints(
18855                  Range.START_TO_START, nodeStartRange) == 1;
18856         
18857          
18858     },
18859     rangeCompareNode : function(range, node)
18860     {
18861         var nodeRange = node.ownerDocument.createRange();
18862         try {
18863             nodeRange.selectNode(node);
18864         } catch (e) {
18865             nodeRange.selectNodeContents(node);
18866         }
18867         
18868         
18869         range.collapse(true);
18870     
18871         nodeRange.collapse(true);
18872      
18873         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18874         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18875          
18876         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18877         
18878         var nodeIsBefore   =  ss == 1;
18879         var nodeIsAfter    = ee == -1;
18880         
18881         if (nodeIsBefore && nodeIsAfter)
18882             return 0; // outer
18883         if (!nodeIsBefore && nodeIsAfter)
18884             return 1; //right trailed.
18885         
18886         if (nodeIsBefore && !nodeIsAfter)
18887             return 2;  // left trailed.
18888         // fully contined.
18889         return 3;
18890     },
18891
18892     // private? - in a new class?
18893     cleanUpPaste :  function()
18894     {
18895         // cleans up the whole document..
18896         Roo.log('cleanuppaste');
18897         
18898         this.cleanUpChildren(this.doc.body);
18899         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18900         if (clean != this.doc.body.innerHTML) {
18901             this.doc.body.innerHTML = clean;
18902         }
18903         
18904     },
18905     
18906     cleanWordChars : function(input) {// change the chars to hex code
18907         var he = Roo.HtmlEditorCore;
18908         
18909         var output = input;
18910         Roo.each(he.swapCodes, function(sw) { 
18911             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18912             
18913             output = output.replace(swapper, sw[1]);
18914         });
18915         
18916         return output;
18917     },
18918     
18919     
18920     cleanUpChildren : function (n)
18921     {
18922         if (!n.childNodes.length) {
18923             return;
18924         }
18925         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18926            this.cleanUpChild(n.childNodes[i]);
18927         }
18928     },
18929     
18930     
18931         
18932     
18933     cleanUpChild : function (node)
18934     {
18935         var ed = this;
18936         //console.log(node);
18937         if (node.nodeName == "#text") {
18938             // clean up silly Windows -- stuff?
18939             return; 
18940         }
18941         if (node.nodeName == "#comment") {
18942             node.parentNode.removeChild(node);
18943             // clean up silly Windows -- stuff?
18944             return; 
18945         }
18946         var lcname = node.tagName.toLowerCase();
18947         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18948         // whitelist of tags..
18949         
18950         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18951             // remove node.
18952             node.parentNode.removeChild(node);
18953             return;
18954             
18955         }
18956         
18957         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18958         
18959         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18960         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18961         
18962         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18963         //    remove_keep_children = true;
18964         //}
18965         
18966         if (remove_keep_children) {
18967             this.cleanUpChildren(node);
18968             // inserts everything just before this node...
18969             while (node.childNodes.length) {
18970                 var cn = node.childNodes[0];
18971                 node.removeChild(cn);
18972                 node.parentNode.insertBefore(cn, node);
18973             }
18974             node.parentNode.removeChild(node);
18975             return;
18976         }
18977         
18978         if (!node.attributes || !node.attributes.length) {
18979             this.cleanUpChildren(node);
18980             return;
18981         }
18982         
18983         function cleanAttr(n,v)
18984         {
18985             
18986             if (v.match(/^\./) || v.match(/^\//)) {
18987                 return;
18988             }
18989             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18990                 return;
18991             }
18992             if (v.match(/^#/)) {
18993                 return;
18994             }
18995 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18996             node.removeAttribute(n);
18997             
18998         }
18999         
19000         var cwhite = this.cwhite;
19001         var cblack = this.cblack;
19002             
19003         function cleanStyle(n,v)
19004         {
19005             if (v.match(/expression/)) { //XSS?? should we even bother..
19006                 node.removeAttribute(n);
19007                 return;
19008             }
19009             
19010             var parts = v.split(/;/);
19011             var clean = [];
19012             
19013             Roo.each(parts, function(p) {
19014                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19015                 if (!p.length) {
19016                     return true;
19017                 }
19018                 var l = p.split(':').shift().replace(/\s+/g,'');
19019                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19020                 
19021                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19022 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19023                     //node.removeAttribute(n);
19024                     return true;
19025                 }
19026                 //Roo.log()
19027                 // only allow 'c whitelisted system attributes'
19028                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19029 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19030                     //node.removeAttribute(n);
19031                     return true;
19032                 }
19033                 
19034                 
19035                  
19036                 
19037                 clean.push(p);
19038                 return true;
19039             });
19040             if (clean.length) { 
19041                 node.setAttribute(n, clean.join(';'));
19042             } else {
19043                 node.removeAttribute(n);
19044             }
19045             
19046         }
19047         
19048         
19049         for (var i = node.attributes.length-1; i > -1 ; i--) {
19050             var a = node.attributes[i];
19051             //console.log(a);
19052             
19053             if (a.name.toLowerCase().substr(0,2)=='on')  {
19054                 node.removeAttribute(a.name);
19055                 continue;
19056             }
19057             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19058                 node.removeAttribute(a.name);
19059                 continue;
19060             }
19061             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19062                 cleanAttr(a.name,a.value); // fixme..
19063                 continue;
19064             }
19065             if (a.name == 'style') {
19066                 cleanStyle(a.name,a.value);
19067                 continue;
19068             }
19069             /// clean up MS crap..
19070             // tecnically this should be a list of valid class'es..
19071             
19072             
19073             if (a.name == 'class') {
19074                 if (a.value.match(/^Mso/)) {
19075                     node.className = '';
19076                 }
19077                 
19078                 if (a.value.match(/body/)) {
19079                     node.className = '';
19080                 }
19081                 continue;
19082             }
19083             
19084             // style cleanup!?
19085             // class cleanup?
19086             
19087         }
19088         
19089         
19090         this.cleanUpChildren(node);
19091         
19092         
19093     },
19094     /**
19095      * Clean up MS wordisms...
19096      */
19097     cleanWord : function(node)
19098     {
19099         var _t = this;
19100         var cleanWordChildren = function()
19101         {
19102             if (!node.childNodes.length) {
19103                 return;
19104             }
19105             for (var i = node.childNodes.length-1; i > -1 ; i--) {
19106                _t.cleanWord(node.childNodes[i]);
19107             }
19108         }
19109         
19110         
19111         if (!node) {
19112             this.cleanWord(this.doc.body);
19113             return;
19114         }
19115         if (node.nodeName == "#text") {
19116             // clean up silly Windows -- stuff?
19117             return; 
19118         }
19119         if (node.nodeName == "#comment") {
19120             node.parentNode.removeChild(node);
19121             // clean up silly Windows -- stuff?
19122             return; 
19123         }
19124         
19125         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19126             node.parentNode.removeChild(node);
19127             return;
19128         }
19129         
19130         // remove - but keep children..
19131         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19132             while (node.childNodes.length) {
19133                 var cn = node.childNodes[0];
19134                 node.removeChild(cn);
19135                 node.parentNode.insertBefore(cn, node);
19136             }
19137             node.parentNode.removeChild(node);
19138             cleanWordChildren();
19139             return;
19140         }
19141         // clean styles
19142         if (node.className.length) {
19143             
19144             var cn = node.className.split(/\W+/);
19145             var cna = [];
19146             Roo.each(cn, function(cls) {
19147                 if (cls.match(/Mso[a-zA-Z]+/)) {
19148                     return;
19149                 }
19150                 cna.push(cls);
19151             });
19152             node.className = cna.length ? cna.join(' ') : '';
19153             if (!cna.length) {
19154                 node.removeAttribute("class");
19155             }
19156         }
19157         
19158         if (node.hasAttribute("lang")) {
19159             node.removeAttribute("lang");
19160         }
19161         
19162         if (node.hasAttribute("style")) {
19163             
19164             var styles = node.getAttribute("style").split(";");
19165             var nstyle = [];
19166             Roo.each(styles, function(s) {
19167                 if (!s.match(/:/)) {
19168                     return;
19169                 }
19170                 var kv = s.split(":");
19171                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19172                     return;
19173                 }
19174                 // what ever is left... we allow.
19175                 nstyle.push(s);
19176             });
19177             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19178             if (!nstyle.length) {
19179                 node.removeAttribute('style');
19180             }
19181         }
19182         
19183         cleanWordChildren();
19184         
19185         
19186     },
19187     domToHTML : function(currentElement, depth, nopadtext) {
19188         
19189         depth = depth || 0;
19190         nopadtext = nopadtext || false;
19191     
19192         if (!currentElement) {
19193             return this.domToHTML(this.doc.body);
19194         }
19195         
19196         //Roo.log(currentElement);
19197         var j;
19198         var allText = false;
19199         var nodeName = currentElement.nodeName;
19200         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19201         
19202         if  (nodeName == '#text') {
19203             
19204             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19205         }
19206         
19207         
19208         var ret = '';
19209         if (nodeName != 'BODY') {
19210              
19211             var i = 0;
19212             // Prints the node tagName, such as <A>, <IMG>, etc
19213             if (tagName) {
19214                 var attr = [];
19215                 for(i = 0; i < currentElement.attributes.length;i++) {
19216                     // quoting?
19217                     var aname = currentElement.attributes.item(i).name;
19218                     if (!currentElement.attributes.item(i).value.length) {
19219                         continue;
19220                     }
19221                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19222                 }
19223                 
19224                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19225             } 
19226             else {
19227                 
19228                 // eack
19229             }
19230         } else {
19231             tagName = false;
19232         }
19233         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19234             return ret;
19235         }
19236         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19237             nopadtext = true;
19238         }
19239         
19240         
19241         // Traverse the tree
19242         i = 0;
19243         var currentElementChild = currentElement.childNodes.item(i);
19244         var allText = true;
19245         var innerHTML  = '';
19246         lastnode = '';
19247         while (currentElementChild) {
19248             // Formatting code (indent the tree so it looks nice on the screen)
19249             var nopad = nopadtext;
19250             if (lastnode == 'SPAN') {
19251                 nopad  = true;
19252             }
19253             // text
19254             if  (currentElementChild.nodeName == '#text') {
19255                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19256                 toadd = nopadtext ? toadd : toadd.trim();
19257                 if (!nopad && toadd.length > 80) {
19258                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19259                 }
19260                 innerHTML  += toadd;
19261                 
19262                 i++;
19263                 currentElementChild = currentElement.childNodes.item(i);
19264                 lastNode = '';
19265                 continue;
19266             }
19267             allText = false;
19268             
19269             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19270                 
19271             // Recursively traverse the tree structure of the child node
19272             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19273             lastnode = currentElementChild.nodeName;
19274             i++;
19275             currentElementChild=currentElement.childNodes.item(i);
19276         }
19277         
19278         ret += innerHTML;
19279         
19280         if (!allText) {
19281                 // The remaining code is mostly for formatting the tree
19282             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19283         }
19284         
19285         
19286         if (tagName) {
19287             ret+= "</"+tagName+">";
19288         }
19289         return ret;
19290         
19291     },
19292         
19293     applyBlacklists : function()
19294     {
19295         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19296         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19297         
19298         this.white = [];
19299         this.black = [];
19300         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19301             if (b.indexOf(tag) > -1) {
19302                 return;
19303             }
19304             this.white.push(tag);
19305             
19306         }, this);
19307         
19308         Roo.each(w, function(tag) {
19309             if (b.indexOf(tag) > -1) {
19310                 return;
19311             }
19312             if (this.white.indexOf(tag) > -1) {
19313                 return;
19314             }
19315             this.white.push(tag);
19316             
19317         }, this);
19318         
19319         
19320         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19321             if (w.indexOf(tag) > -1) {
19322                 return;
19323             }
19324             this.black.push(tag);
19325             
19326         }, this);
19327         
19328         Roo.each(b, function(tag) {
19329             if (w.indexOf(tag) > -1) {
19330                 return;
19331             }
19332             if (this.black.indexOf(tag) > -1) {
19333                 return;
19334             }
19335             this.black.push(tag);
19336             
19337         }, this);
19338         
19339         
19340         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19341         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19342         
19343         this.cwhite = [];
19344         this.cblack = [];
19345         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19346             if (b.indexOf(tag) > -1) {
19347                 return;
19348             }
19349             this.cwhite.push(tag);
19350             
19351         }, this);
19352         
19353         Roo.each(w, function(tag) {
19354             if (b.indexOf(tag) > -1) {
19355                 return;
19356             }
19357             if (this.cwhite.indexOf(tag) > -1) {
19358                 return;
19359             }
19360             this.cwhite.push(tag);
19361             
19362         }, this);
19363         
19364         
19365         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19366             if (w.indexOf(tag) > -1) {
19367                 return;
19368             }
19369             this.cblack.push(tag);
19370             
19371         }, this);
19372         
19373         Roo.each(b, function(tag) {
19374             if (w.indexOf(tag) > -1) {
19375                 return;
19376             }
19377             if (this.cblack.indexOf(tag) > -1) {
19378                 return;
19379             }
19380             this.cblack.push(tag);
19381             
19382         }, this);
19383     },
19384     
19385     setStylesheets : function(stylesheets)
19386     {
19387         if(typeof(stylesheets) == 'string'){
19388             Roo.get(this.iframe.contentDocument.head).createChild({
19389                 tag : 'link',
19390                 rel : 'stylesheet',
19391                 type : 'text/css',
19392                 href : stylesheets
19393             });
19394             
19395             return;
19396         }
19397         var _this = this;
19398      
19399         Roo.each(stylesheets, function(s) {
19400             if(!s.length){
19401                 return;
19402             }
19403             
19404             Roo.get(_this.iframe.contentDocument.head).createChild({
19405                 tag : 'link',
19406                 rel : 'stylesheet',
19407                 type : 'text/css',
19408                 href : s
19409             });
19410         });
19411
19412         
19413     },
19414     
19415     removeStylesheets : function()
19416     {
19417         var _this = this;
19418         
19419         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19420             s.remove();
19421         });
19422     }
19423     
19424     // hide stuff that is not compatible
19425     /**
19426      * @event blur
19427      * @hide
19428      */
19429     /**
19430      * @event change
19431      * @hide
19432      */
19433     /**
19434      * @event focus
19435      * @hide
19436      */
19437     /**
19438      * @event specialkey
19439      * @hide
19440      */
19441     /**
19442      * @cfg {String} fieldClass @hide
19443      */
19444     /**
19445      * @cfg {String} focusClass @hide
19446      */
19447     /**
19448      * @cfg {String} autoCreate @hide
19449      */
19450     /**
19451      * @cfg {String} inputType @hide
19452      */
19453     /**
19454      * @cfg {String} invalidClass @hide
19455      */
19456     /**
19457      * @cfg {String} invalidText @hide
19458      */
19459     /**
19460      * @cfg {String} msgFx @hide
19461      */
19462     /**
19463      * @cfg {String} validateOnBlur @hide
19464      */
19465 });
19466
19467 Roo.HtmlEditorCore.white = [
19468         'area', 'br', 'img', 'input', 'hr', 'wbr',
19469         
19470        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19471        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19472        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19473        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19474        'table',   'ul',         'xmp', 
19475        
19476        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19477       'thead',   'tr', 
19478      
19479       'dir', 'menu', 'ol', 'ul', 'dl',
19480        
19481       'embed',  'object'
19482 ];
19483
19484
19485 Roo.HtmlEditorCore.black = [
19486     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19487         'applet', // 
19488         'base',   'basefont', 'bgsound', 'blink',  'body', 
19489         'frame',  'frameset', 'head',    'html',   'ilayer', 
19490         'iframe', 'layer',  'link',     'meta',    'object',   
19491         'script', 'style' ,'title',  'xml' // clean later..
19492 ];
19493 Roo.HtmlEditorCore.clean = [
19494     'script', 'style', 'title', 'xml'
19495 ];
19496 Roo.HtmlEditorCore.remove = [
19497     'font'
19498 ];
19499 // attributes..
19500
19501 Roo.HtmlEditorCore.ablack = [
19502     'on'
19503 ];
19504     
19505 Roo.HtmlEditorCore.aclean = [ 
19506     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19507 ];
19508
19509 // protocols..
19510 Roo.HtmlEditorCore.pwhite= [
19511         'http',  'https',  'mailto'
19512 ];
19513
19514 // white listed style attributes.
19515 Roo.HtmlEditorCore.cwhite= [
19516       //  'text-align', /// default is to allow most things..
19517       
19518          
19519 //        'font-size'//??
19520 ];
19521
19522 // black listed style attributes.
19523 Roo.HtmlEditorCore.cblack= [
19524       //  'font-size' -- this can be set by the project 
19525 ];
19526
19527
19528 Roo.HtmlEditorCore.swapCodes   =[ 
19529     [    8211, "--" ], 
19530     [    8212, "--" ], 
19531     [    8216,  "'" ],  
19532     [    8217, "'" ],  
19533     [    8220, '"' ],  
19534     [    8221, '"' ],  
19535     [    8226, "*" ],  
19536     [    8230, "..." ]
19537 ]; 
19538
19539     /*
19540  * - LGPL
19541  *
19542  * HtmlEditor
19543  * 
19544  */
19545
19546 /**
19547  * @class Roo.bootstrap.HtmlEditor
19548  * @extends Roo.bootstrap.TextArea
19549  * Bootstrap HtmlEditor class
19550
19551  * @constructor
19552  * Create a new HtmlEditor
19553  * @param {Object} config The config object
19554  */
19555
19556 Roo.bootstrap.HtmlEditor = function(config){
19557     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19558     if (!this.toolbars) {
19559         this.toolbars = [];
19560     }
19561     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19562     this.addEvents({
19563             /**
19564              * @event initialize
19565              * Fires when the editor is fully initialized (including the iframe)
19566              * @param {HtmlEditor} this
19567              */
19568             initialize: true,
19569             /**
19570              * @event activate
19571              * Fires when the editor is first receives the focus. Any insertion must wait
19572              * until after this event.
19573              * @param {HtmlEditor} this
19574              */
19575             activate: true,
19576              /**
19577              * @event beforesync
19578              * Fires before the textarea is updated with content from the editor iframe. Return false
19579              * to cancel the sync.
19580              * @param {HtmlEditor} this
19581              * @param {String} html
19582              */
19583             beforesync: true,
19584              /**
19585              * @event beforepush
19586              * Fires before the iframe editor is updated with content from the textarea. Return false
19587              * to cancel the push.
19588              * @param {HtmlEditor} this
19589              * @param {String} html
19590              */
19591             beforepush: true,
19592              /**
19593              * @event sync
19594              * Fires when the textarea is updated with content from the editor iframe.
19595              * @param {HtmlEditor} this
19596              * @param {String} html
19597              */
19598             sync: true,
19599              /**
19600              * @event push
19601              * Fires when the iframe editor is updated with content from the textarea.
19602              * @param {HtmlEditor} this
19603              * @param {String} html
19604              */
19605             push: true,
19606              /**
19607              * @event editmodechange
19608              * Fires when the editor switches edit modes
19609              * @param {HtmlEditor} this
19610              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19611              */
19612             editmodechange: true,
19613             /**
19614              * @event editorevent
19615              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19616              * @param {HtmlEditor} this
19617              */
19618             editorevent: true,
19619             /**
19620              * @event firstfocus
19621              * Fires when on first focus - needed by toolbars..
19622              * @param {HtmlEditor} this
19623              */
19624             firstfocus: true,
19625             /**
19626              * @event autosave
19627              * Auto save the htmlEditor value as a file into Events
19628              * @param {HtmlEditor} this
19629              */
19630             autosave: true,
19631             /**
19632              * @event savedpreview
19633              * preview the saved version of htmlEditor
19634              * @param {HtmlEditor} this
19635              */
19636             savedpreview: true
19637         });
19638 };
19639
19640
19641 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19642     
19643     
19644       /**
19645      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19646      */
19647     toolbars : false,
19648    
19649      /**
19650      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19651      *                        Roo.resizable.
19652      */
19653     resizable : false,
19654      /**
19655      * @cfg {Number} height (in pixels)
19656      */   
19657     height: 300,
19658    /**
19659      * @cfg {Number} width (in pixels)
19660      */   
19661     width: false,
19662     
19663     /**
19664      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19665      * 
19666      */
19667     stylesheets: false,
19668     
19669     // id of frame..
19670     frameId: false,
19671     
19672     // private properties
19673     validationEvent : false,
19674     deferHeight: true,
19675     initialized : false,
19676     activated : false,
19677     
19678     onFocus : Roo.emptyFn,
19679     iframePad:3,
19680     hideMode:'offsets',
19681     
19682     
19683     tbContainer : false,
19684     
19685     toolbarContainer :function() {
19686         return this.wrap.select('.x-html-editor-tb',true).first();
19687     },
19688
19689     /**
19690      * Protected method that will not generally be called directly. It
19691      * is called when the editor creates its toolbar. Override this method if you need to
19692      * add custom toolbar buttons.
19693      * @param {HtmlEditor} editor
19694      */
19695     createToolbar : function(){
19696         
19697         Roo.log("create toolbars");
19698         
19699         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19700         this.toolbars[0].render(this.toolbarContainer());
19701         
19702         return;
19703         
19704 //        if (!editor.toolbars || !editor.toolbars.length) {
19705 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19706 //        }
19707 //        
19708 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19709 //            editor.toolbars[i] = Roo.factory(
19710 //                    typeof(editor.toolbars[i]) == 'string' ?
19711 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19712 //                Roo.bootstrap.HtmlEditor);
19713 //            editor.toolbars[i].init(editor);
19714 //        }
19715     },
19716
19717      
19718     // private
19719     onRender : function(ct, position)
19720     {
19721        // Roo.log("Call onRender: " + this.xtype);
19722         var _t = this;
19723         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19724       
19725         this.wrap = this.inputEl().wrap({
19726             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19727         });
19728         
19729         this.editorcore.onRender(ct, position);
19730          
19731         if (this.resizable) {
19732             this.resizeEl = new Roo.Resizable(this.wrap, {
19733                 pinned : true,
19734                 wrap: true,
19735                 dynamic : true,
19736                 minHeight : this.height,
19737                 height: this.height,
19738                 handles : this.resizable,
19739                 width: this.width,
19740                 listeners : {
19741                     resize : function(r, w, h) {
19742                         _t.onResize(w,h); // -something
19743                     }
19744                 }
19745             });
19746             
19747         }
19748         this.createToolbar(this);
19749        
19750         
19751         if(!this.width && this.resizable){
19752             this.setSize(this.wrap.getSize());
19753         }
19754         if (this.resizeEl) {
19755             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19756             // should trigger onReize..
19757         }
19758         
19759     },
19760
19761     // private
19762     onResize : function(w, h)
19763     {
19764         Roo.log('resize: ' +w + ',' + h );
19765         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19766         var ew = false;
19767         var eh = false;
19768         
19769         if(this.inputEl() ){
19770             if(typeof w == 'number'){
19771                 var aw = w - this.wrap.getFrameWidth('lr');
19772                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19773                 ew = aw;
19774             }
19775             if(typeof h == 'number'){
19776                  var tbh = -11;  // fixme it needs to tool bar size!
19777                 for (var i =0; i < this.toolbars.length;i++) {
19778                     // fixme - ask toolbars for heights?
19779                     tbh += this.toolbars[i].el.getHeight();
19780                     //if (this.toolbars[i].footer) {
19781                     //    tbh += this.toolbars[i].footer.el.getHeight();
19782                     //}
19783                 }
19784               
19785                 
19786                 
19787                 
19788                 
19789                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19790                 ah -= 5; // knock a few pixes off for look..
19791                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19792                 var eh = ah;
19793             }
19794         }
19795         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19796         this.editorcore.onResize(ew,eh);
19797         
19798     },
19799
19800     /**
19801      * Toggles the editor between standard and source edit mode.
19802      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19803      */
19804     toggleSourceEdit : function(sourceEditMode)
19805     {
19806         this.editorcore.toggleSourceEdit(sourceEditMode);
19807         
19808         if(this.editorcore.sourceEditMode){
19809             Roo.log('editor - showing textarea');
19810             
19811 //            Roo.log('in');
19812 //            Roo.log(this.syncValue());
19813             this.syncValue();
19814             this.inputEl().removeClass(['hide', 'x-hidden']);
19815             this.inputEl().dom.removeAttribute('tabIndex');
19816             this.inputEl().focus();
19817         }else{
19818             Roo.log('editor - hiding textarea');
19819 //            Roo.log('out')
19820 //            Roo.log(this.pushValue()); 
19821             this.pushValue();
19822             
19823             this.inputEl().addClass(['hide', 'x-hidden']);
19824             this.inputEl().dom.setAttribute('tabIndex', -1);
19825             //this.deferFocus();
19826         }
19827          
19828         if(this.resizable){
19829             this.setSize(this.wrap.getSize());
19830         }
19831         
19832         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19833     },
19834  
19835     // private (for BoxComponent)
19836     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19837
19838     // private (for BoxComponent)
19839     getResizeEl : function(){
19840         return this.wrap;
19841     },
19842
19843     // private (for BoxComponent)
19844     getPositionEl : function(){
19845         return this.wrap;
19846     },
19847
19848     // private
19849     initEvents : function(){
19850         this.originalValue = this.getValue();
19851     },
19852
19853 //    /**
19854 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19855 //     * @method
19856 //     */
19857 //    markInvalid : Roo.emptyFn,
19858 //    /**
19859 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19860 //     * @method
19861 //     */
19862 //    clearInvalid : Roo.emptyFn,
19863
19864     setValue : function(v){
19865         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19866         this.editorcore.pushValue();
19867     },
19868
19869      
19870     // private
19871     deferFocus : function(){
19872         this.focus.defer(10, this);
19873     },
19874
19875     // doc'ed in Field
19876     focus : function(){
19877         this.editorcore.focus();
19878         
19879     },
19880       
19881
19882     // private
19883     onDestroy : function(){
19884         
19885         
19886         
19887         if(this.rendered){
19888             
19889             for (var i =0; i < this.toolbars.length;i++) {
19890                 // fixme - ask toolbars for heights?
19891                 this.toolbars[i].onDestroy();
19892             }
19893             
19894             this.wrap.dom.innerHTML = '';
19895             this.wrap.remove();
19896         }
19897     },
19898
19899     // private
19900     onFirstFocus : function(){
19901         //Roo.log("onFirstFocus");
19902         this.editorcore.onFirstFocus();
19903          for (var i =0; i < this.toolbars.length;i++) {
19904             this.toolbars[i].onFirstFocus();
19905         }
19906         
19907     },
19908     
19909     // private
19910     syncValue : function()
19911     {   
19912         this.editorcore.syncValue();
19913     },
19914     
19915     pushValue : function()
19916     {   
19917         this.editorcore.pushValue();
19918     }
19919      
19920     
19921     // hide stuff that is not compatible
19922     /**
19923      * @event blur
19924      * @hide
19925      */
19926     /**
19927      * @event change
19928      * @hide
19929      */
19930     /**
19931      * @event focus
19932      * @hide
19933      */
19934     /**
19935      * @event specialkey
19936      * @hide
19937      */
19938     /**
19939      * @cfg {String} fieldClass @hide
19940      */
19941     /**
19942      * @cfg {String} focusClass @hide
19943      */
19944     /**
19945      * @cfg {String} autoCreate @hide
19946      */
19947     /**
19948      * @cfg {String} inputType @hide
19949      */
19950     /**
19951      * @cfg {String} invalidClass @hide
19952      */
19953     /**
19954      * @cfg {String} invalidText @hide
19955      */
19956     /**
19957      * @cfg {String} msgFx @hide
19958      */
19959     /**
19960      * @cfg {String} validateOnBlur @hide
19961      */
19962 });
19963  
19964     
19965    
19966    
19967    
19968       
19969 Roo.namespace('Roo.bootstrap.htmleditor');
19970 /**
19971  * @class Roo.bootstrap.HtmlEditorToolbar1
19972  * Basic Toolbar
19973  * 
19974  * Usage:
19975  *
19976  new Roo.bootstrap.HtmlEditor({
19977     ....
19978     toolbars : [
19979         new Roo.bootstrap.HtmlEditorToolbar1({
19980             disable : { fonts: 1 , format: 1, ..., ... , ...],
19981             btns : [ .... ]
19982         })
19983     }
19984      
19985  * 
19986  * @cfg {Object} disable List of elements to disable..
19987  * @cfg {Array} btns List of additional buttons.
19988  * 
19989  * 
19990  * NEEDS Extra CSS? 
19991  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19992  */
19993  
19994 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19995 {
19996     
19997     Roo.apply(this, config);
19998     
19999     // default disabled, based on 'good practice'..
20000     this.disable = this.disable || {};
20001     Roo.applyIf(this.disable, {
20002         fontSize : true,
20003         colors : true,
20004         specialElements : true
20005     });
20006     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20007     
20008     this.editor = config.editor;
20009     this.editorcore = config.editor.editorcore;
20010     
20011     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20012     
20013     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20014     // dont call parent... till later.
20015 }
20016 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20017      
20018     bar : true,
20019     
20020     editor : false,
20021     editorcore : false,
20022     
20023     
20024     formats : [
20025         "p" ,  
20026         "h1","h2","h3","h4","h5","h6", 
20027         "pre", "code", 
20028         "abbr", "acronym", "address", "cite", "samp", "var",
20029         'div','span'
20030     ],
20031     
20032     onRender : function(ct, position)
20033     {
20034        // Roo.log("Call onRender: " + this.xtype);
20035         
20036        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20037        Roo.log(this.el);
20038        this.el.dom.style.marginBottom = '0';
20039        var _this = this;
20040        var editorcore = this.editorcore;
20041        var editor= this.editor;
20042        
20043        var children = [];
20044        var btn = function(id,cmd , toggle, handler){
20045        
20046             var  event = toggle ? 'toggle' : 'click';
20047        
20048             var a = {
20049                 size : 'sm',
20050                 xtype: 'Button',
20051                 xns: Roo.bootstrap,
20052                 glyphicon : id,
20053                 cmd : id || cmd,
20054                 enableToggle:toggle !== false,
20055                 //html : 'submit'
20056                 pressed : toggle ? false : null,
20057                 listeners : {}
20058             }
20059             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20060                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20061             }
20062             children.push(a);
20063             return a;
20064        }
20065         
20066         var style = {
20067                 xtype: 'Button',
20068                 size : 'sm',
20069                 xns: Roo.bootstrap,
20070                 glyphicon : 'font',
20071                 //html : 'submit'
20072                 menu : {
20073                     xtype: 'Menu',
20074                     xns: Roo.bootstrap,
20075                     items:  []
20076                 }
20077         };
20078         Roo.each(this.formats, function(f) {
20079             style.menu.items.push({
20080                 xtype :'MenuItem',
20081                 xns: Roo.bootstrap,
20082                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20083                 tagname : f,
20084                 listeners : {
20085                     click : function()
20086                     {
20087                         editorcore.insertTag(this.tagname);
20088                         editor.focus();
20089                     }
20090                 }
20091                 
20092             });
20093         });
20094          children.push(style);   
20095             
20096             
20097         btn('bold',false,true);
20098         btn('italic',false,true);
20099         btn('align-left', 'justifyleft',true);
20100         btn('align-center', 'justifycenter',true);
20101         btn('align-right' , 'justifyright',true);
20102         btn('link', false, false, function(btn) {
20103             //Roo.log("create link?");
20104             var url = prompt(this.createLinkText, this.defaultLinkValue);
20105             if(url && url != 'http:/'+'/'){
20106                 this.editorcore.relayCmd('createlink', url);
20107             }
20108         }),
20109         btn('list','insertunorderedlist',true);
20110         btn('pencil', false,true, function(btn){
20111                 Roo.log(this);
20112                 
20113                 this.toggleSourceEdit(btn.pressed);
20114         });
20115         /*
20116         var cog = {
20117                 xtype: 'Button',
20118                 size : 'sm',
20119                 xns: Roo.bootstrap,
20120                 glyphicon : 'cog',
20121                 //html : 'submit'
20122                 menu : {
20123                     xtype: 'Menu',
20124                     xns: Roo.bootstrap,
20125                     items:  []
20126                 }
20127         };
20128         
20129         cog.menu.items.push({
20130             xtype :'MenuItem',
20131             xns: Roo.bootstrap,
20132             html : Clean styles,
20133             tagname : f,
20134             listeners : {
20135                 click : function()
20136                 {
20137                     editorcore.insertTag(this.tagname);
20138                     editor.focus();
20139                 }
20140             }
20141             
20142         });
20143        */
20144         
20145          
20146        this.xtype = 'NavSimplebar';
20147         
20148         for(var i=0;i< children.length;i++) {
20149             
20150             this.buttons.add(this.addxtypeChild(children[i]));
20151             
20152         }
20153         
20154         editor.on('editorevent', this.updateToolbar, this);
20155     },
20156     onBtnClick : function(id)
20157     {
20158        this.editorcore.relayCmd(id);
20159        this.editorcore.focus();
20160     },
20161     
20162     /**
20163      * Protected method that will not generally be called directly. It triggers
20164      * a toolbar update by reading the markup state of the current selection in the editor.
20165      */
20166     updateToolbar: function(){
20167
20168         if(!this.editorcore.activated){
20169             this.editor.onFirstFocus(); // is this neeed?
20170             return;
20171         }
20172
20173         var btns = this.buttons; 
20174         var doc = this.editorcore.doc;
20175         btns.get('bold').setActive(doc.queryCommandState('bold'));
20176         btns.get('italic').setActive(doc.queryCommandState('italic'));
20177         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20178         
20179         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20180         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20181         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20182         
20183         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20184         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20185          /*
20186         
20187         var ans = this.editorcore.getAllAncestors();
20188         if (this.formatCombo) {
20189             
20190             
20191             var store = this.formatCombo.store;
20192             this.formatCombo.setValue("");
20193             for (var i =0; i < ans.length;i++) {
20194                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20195                     // select it..
20196                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20197                     break;
20198                 }
20199             }
20200         }
20201         
20202         
20203         
20204         // hides menus... - so this cant be on a menu...
20205         Roo.bootstrap.MenuMgr.hideAll();
20206         */
20207         Roo.bootstrap.MenuMgr.hideAll();
20208         //this.editorsyncValue();
20209     },
20210     onFirstFocus: function() {
20211         this.buttons.each(function(item){
20212            item.enable();
20213         });
20214     },
20215     toggleSourceEdit : function(sourceEditMode){
20216         
20217           
20218         if(sourceEditMode){
20219             Roo.log("disabling buttons");
20220            this.buttons.each( function(item){
20221                 if(item.cmd != 'pencil'){
20222                     item.disable();
20223                 }
20224             });
20225           
20226         }else{
20227             Roo.log("enabling buttons");
20228             if(this.editorcore.initialized){
20229                 this.buttons.each( function(item){
20230                     item.enable();
20231                 });
20232             }
20233             
20234         }
20235         Roo.log("calling toggole on editor");
20236         // tell the editor that it's been pressed..
20237         this.editor.toggleSourceEdit(sourceEditMode);
20238        
20239     }
20240 });
20241
20242
20243
20244
20245
20246 /**
20247  * @class Roo.bootstrap.Table.AbstractSelectionModel
20248  * @extends Roo.util.Observable
20249  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20250  * implemented by descendant classes.  This class should not be directly instantiated.
20251  * @constructor
20252  */
20253 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20254     this.locked = false;
20255     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20256 };
20257
20258
20259 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20260     /** @ignore Called by the grid automatically. Do not call directly. */
20261     init : function(grid){
20262         this.grid = grid;
20263         this.initEvents();
20264     },
20265
20266     /**
20267      * Locks the selections.
20268      */
20269     lock : function(){
20270         this.locked = true;
20271     },
20272
20273     /**
20274      * Unlocks the selections.
20275      */
20276     unlock : function(){
20277         this.locked = false;
20278     },
20279
20280     /**
20281      * Returns true if the selections are locked.
20282      * @return {Boolean}
20283      */
20284     isLocked : function(){
20285         return this.locked;
20286     }
20287 });
20288 /**
20289  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20290  * @class Roo.bootstrap.Table.RowSelectionModel
20291  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20292  * It supports multiple selections and keyboard selection/navigation. 
20293  * @constructor
20294  * @param {Object} config
20295  */
20296
20297 Roo.bootstrap.Table.RowSelectionModel = function(config){
20298     Roo.apply(this, config);
20299     this.selections = new Roo.util.MixedCollection(false, function(o){
20300         return o.id;
20301     });
20302
20303     this.last = false;
20304     this.lastActive = false;
20305
20306     this.addEvents({
20307         /**
20308              * @event selectionchange
20309              * Fires when the selection changes
20310              * @param {SelectionModel} this
20311              */
20312             "selectionchange" : true,
20313         /**
20314              * @event afterselectionchange
20315              * Fires after the selection changes (eg. by key press or clicking)
20316              * @param {SelectionModel} this
20317              */
20318             "afterselectionchange" : true,
20319         /**
20320              * @event beforerowselect
20321              * Fires when a row is selected being selected, return false to cancel.
20322              * @param {SelectionModel} this
20323              * @param {Number} rowIndex The selected index
20324              * @param {Boolean} keepExisting False if other selections will be cleared
20325              */
20326             "beforerowselect" : true,
20327         /**
20328              * @event rowselect
20329              * Fires when a row is selected.
20330              * @param {SelectionModel} this
20331              * @param {Number} rowIndex The selected index
20332              * @param {Roo.data.Record} r The record
20333              */
20334             "rowselect" : true,
20335         /**
20336              * @event rowdeselect
20337              * Fires when a row is deselected.
20338              * @param {SelectionModel} this
20339              * @param {Number} rowIndex The selected index
20340              */
20341         "rowdeselect" : true
20342     });
20343     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20344     this.locked = false;
20345 };
20346
20347 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20348     /**
20349      * @cfg {Boolean} singleSelect
20350      * True to allow selection of only one row at a time (defaults to false)
20351      */
20352     singleSelect : false,
20353
20354     // private
20355     initEvents : function(){
20356
20357         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20358             this.grid.on("mousedown", this.handleMouseDown, this);
20359         }else{ // allow click to work like normal
20360             this.grid.on("rowclick", this.handleDragableRowClick, this);
20361         }
20362
20363         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20364             "up" : function(e){
20365                 if(!e.shiftKey){
20366                     this.selectPrevious(e.shiftKey);
20367                 }else if(this.last !== false && this.lastActive !== false){
20368                     var last = this.last;
20369                     this.selectRange(this.last,  this.lastActive-1);
20370                     this.grid.getView().focusRow(this.lastActive);
20371                     if(last !== false){
20372                         this.last = last;
20373                     }
20374                 }else{
20375                     this.selectFirstRow();
20376                 }
20377                 this.fireEvent("afterselectionchange", this);
20378             },
20379             "down" : function(e){
20380                 if(!e.shiftKey){
20381                     this.selectNext(e.shiftKey);
20382                 }else if(this.last !== false && this.lastActive !== false){
20383                     var last = this.last;
20384                     this.selectRange(this.last,  this.lastActive+1);
20385                     this.grid.getView().focusRow(this.lastActive);
20386                     if(last !== false){
20387                         this.last = last;
20388                     }
20389                 }else{
20390                     this.selectFirstRow();
20391                 }
20392                 this.fireEvent("afterselectionchange", this);
20393             },
20394             scope: this
20395         });
20396
20397         var view = this.grid.view;
20398         view.on("refresh", this.onRefresh, this);
20399         view.on("rowupdated", this.onRowUpdated, this);
20400         view.on("rowremoved", this.onRemove, this);
20401     },
20402
20403     // private
20404     onRefresh : function(){
20405         var ds = this.grid.dataSource, i, v = this.grid.view;
20406         var s = this.selections;
20407         s.each(function(r){
20408             if((i = ds.indexOfId(r.id)) != -1){
20409                 v.onRowSelect(i);
20410             }else{
20411                 s.remove(r);
20412             }
20413         });
20414     },
20415
20416     // private
20417     onRemove : function(v, index, r){
20418         this.selections.remove(r);
20419     },
20420
20421     // private
20422     onRowUpdated : function(v, index, r){
20423         if(this.isSelected(r)){
20424             v.onRowSelect(index);
20425         }
20426     },
20427
20428     /**
20429      * Select records.
20430      * @param {Array} records The records to select
20431      * @param {Boolean} keepExisting (optional) True to keep existing selections
20432      */
20433     selectRecords : function(records, keepExisting){
20434         if(!keepExisting){
20435             this.clearSelections();
20436         }
20437         var ds = this.grid.dataSource;
20438         for(var i = 0, len = records.length; i < len; i++){
20439             this.selectRow(ds.indexOf(records[i]), true);
20440         }
20441     },
20442
20443     /**
20444      * Gets the number of selected rows.
20445      * @return {Number}
20446      */
20447     getCount : function(){
20448         return this.selections.length;
20449     },
20450
20451     /**
20452      * Selects the first row in the grid.
20453      */
20454     selectFirstRow : function(){
20455         this.selectRow(0);
20456     },
20457
20458     /**
20459      * Select the last row.
20460      * @param {Boolean} keepExisting (optional) True to keep existing selections
20461      */
20462     selectLastRow : function(keepExisting){
20463         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20464     },
20465
20466     /**
20467      * Selects the row immediately following the last selected row.
20468      * @param {Boolean} keepExisting (optional) True to keep existing selections
20469      */
20470     selectNext : function(keepExisting){
20471         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20472             this.selectRow(this.last+1, keepExisting);
20473             this.grid.getView().focusRow(this.last);
20474         }
20475     },
20476
20477     /**
20478      * Selects the row that precedes the last selected row.
20479      * @param {Boolean} keepExisting (optional) True to keep existing selections
20480      */
20481     selectPrevious : function(keepExisting){
20482         if(this.last){
20483             this.selectRow(this.last-1, keepExisting);
20484             this.grid.getView().focusRow(this.last);
20485         }
20486     },
20487
20488     /**
20489      * Returns the selected records
20490      * @return {Array} Array of selected records
20491      */
20492     getSelections : function(){
20493         return [].concat(this.selections.items);
20494     },
20495
20496     /**
20497      * Returns the first selected record.
20498      * @return {Record}
20499      */
20500     getSelected : function(){
20501         return this.selections.itemAt(0);
20502     },
20503
20504
20505     /**
20506      * Clears all selections.
20507      */
20508     clearSelections : function(fast){
20509         if(this.locked) return;
20510         if(fast !== true){
20511             var ds = this.grid.dataSource;
20512             var s = this.selections;
20513             s.each(function(r){
20514                 this.deselectRow(ds.indexOfId(r.id));
20515             }, this);
20516             s.clear();
20517         }else{
20518             this.selections.clear();
20519         }
20520         this.last = false;
20521     },
20522
20523
20524     /**
20525      * Selects all rows.
20526      */
20527     selectAll : function(){
20528         if(this.locked) return;
20529         this.selections.clear();
20530         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20531             this.selectRow(i, true);
20532         }
20533     },
20534
20535     /**
20536      * Returns True if there is a selection.
20537      * @return {Boolean}
20538      */
20539     hasSelection : function(){
20540         return this.selections.length > 0;
20541     },
20542
20543     /**
20544      * Returns True if the specified row is selected.
20545      * @param {Number/Record} record The record or index of the record to check
20546      * @return {Boolean}
20547      */
20548     isSelected : function(index){
20549         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20550         return (r && this.selections.key(r.id) ? true : false);
20551     },
20552
20553     /**
20554      * Returns True if the specified record id is selected.
20555      * @param {String} id The id of record to check
20556      * @return {Boolean}
20557      */
20558     isIdSelected : function(id){
20559         return (this.selections.key(id) ? true : false);
20560     },
20561
20562     // private
20563     handleMouseDown : function(e, t){
20564         var view = this.grid.getView(), rowIndex;
20565         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20566             return;
20567         };
20568         if(e.shiftKey && this.last !== false){
20569             var last = this.last;
20570             this.selectRange(last, rowIndex, e.ctrlKey);
20571             this.last = last; // reset the last
20572             view.focusRow(rowIndex);
20573         }else{
20574             var isSelected = this.isSelected(rowIndex);
20575             if(e.button !== 0 && isSelected){
20576                 view.focusRow(rowIndex);
20577             }else if(e.ctrlKey && isSelected){
20578                 this.deselectRow(rowIndex);
20579             }else if(!isSelected){
20580                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20581                 view.focusRow(rowIndex);
20582             }
20583         }
20584         this.fireEvent("afterselectionchange", this);
20585     },
20586     // private
20587     handleDragableRowClick :  function(grid, rowIndex, e) 
20588     {
20589         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20590             this.selectRow(rowIndex, false);
20591             grid.view.focusRow(rowIndex);
20592              this.fireEvent("afterselectionchange", this);
20593         }
20594     },
20595     
20596     /**
20597      * Selects multiple rows.
20598      * @param {Array} rows Array of the indexes of the row to select
20599      * @param {Boolean} keepExisting (optional) True to keep existing selections
20600      */
20601     selectRows : function(rows, keepExisting){
20602         if(!keepExisting){
20603             this.clearSelections();
20604         }
20605         for(var i = 0, len = rows.length; i < len; i++){
20606             this.selectRow(rows[i], true);
20607         }
20608     },
20609
20610     /**
20611      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20612      * @param {Number} startRow The index of the first row in the range
20613      * @param {Number} endRow The index of the last row in the range
20614      * @param {Boolean} keepExisting (optional) True to retain existing selections
20615      */
20616     selectRange : function(startRow, endRow, keepExisting){
20617         if(this.locked) return;
20618         if(!keepExisting){
20619             this.clearSelections();
20620         }
20621         if(startRow <= endRow){
20622             for(var i = startRow; i <= endRow; i++){
20623                 this.selectRow(i, true);
20624             }
20625         }else{
20626             for(var i = startRow; i >= endRow; i--){
20627                 this.selectRow(i, true);
20628             }
20629         }
20630     },
20631
20632     /**
20633      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20634      * @param {Number} startRow The index of the first row in the range
20635      * @param {Number} endRow The index of the last row in the range
20636      */
20637     deselectRange : function(startRow, endRow, preventViewNotify){
20638         if(this.locked) return;
20639         for(var i = startRow; i <= endRow; i++){
20640             this.deselectRow(i, preventViewNotify);
20641         }
20642     },
20643
20644     /**
20645      * Selects a row.
20646      * @param {Number} row The index of the row to select
20647      * @param {Boolean} keepExisting (optional) True to keep existing selections
20648      */
20649     selectRow : function(index, keepExisting, preventViewNotify){
20650         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20651         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20652             if(!keepExisting || this.singleSelect){
20653                 this.clearSelections();
20654             }
20655             var r = this.grid.dataSource.getAt(index);
20656             this.selections.add(r);
20657             this.last = this.lastActive = index;
20658             if(!preventViewNotify){
20659                 this.grid.getView().onRowSelect(index);
20660             }
20661             this.fireEvent("rowselect", this, index, r);
20662             this.fireEvent("selectionchange", this);
20663         }
20664     },
20665
20666     /**
20667      * Deselects a row.
20668      * @param {Number} row The index of the row to deselect
20669      */
20670     deselectRow : function(index, preventViewNotify){
20671         if(this.locked) return;
20672         if(this.last == index){
20673             this.last = false;
20674         }
20675         if(this.lastActive == index){
20676             this.lastActive = false;
20677         }
20678         var r = this.grid.dataSource.getAt(index);
20679         this.selections.remove(r);
20680         if(!preventViewNotify){
20681             this.grid.getView().onRowDeselect(index);
20682         }
20683         this.fireEvent("rowdeselect", this, index);
20684         this.fireEvent("selectionchange", this);
20685     },
20686
20687     // private
20688     restoreLast : function(){
20689         if(this._last){
20690             this.last = this._last;
20691         }
20692     },
20693
20694     // private
20695     acceptsNav : function(row, col, cm){
20696         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20697     },
20698
20699     // private
20700     onEditorKey : function(field, e){
20701         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20702         if(k == e.TAB){
20703             e.stopEvent();
20704             ed.completeEdit();
20705             if(e.shiftKey){
20706                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20707             }else{
20708                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20709             }
20710         }else if(k == e.ENTER && !e.ctrlKey){
20711             e.stopEvent();
20712             ed.completeEdit();
20713             if(e.shiftKey){
20714                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20715             }else{
20716                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20717             }
20718         }else if(k == e.ESC){
20719             ed.cancelEdit();
20720         }
20721         if(newCell){
20722             g.startEditing(newCell[0], newCell[1]);
20723         }
20724     }
20725 });/*
20726  * Based on:
20727  * Ext JS Library 1.1.1
20728  * Copyright(c) 2006-2007, Ext JS, LLC.
20729  *
20730  * Originally Released Under LGPL - original licence link has changed is not relivant.
20731  *
20732  * Fork - LGPL
20733  * <script type="text/javascript">
20734  */
20735  
20736 /**
20737  * @class Roo.bootstrap.PagingToolbar
20738  * @extends Roo.Row
20739  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20740  * @constructor
20741  * Create a new PagingToolbar
20742  * @param {Object} config The config object
20743  */
20744 Roo.bootstrap.PagingToolbar = function(config)
20745 {
20746     // old args format still supported... - xtype is prefered..
20747         // created from xtype...
20748     var ds = config.dataSource;
20749     this.toolbarItems = [];
20750     if (config.items) {
20751         this.toolbarItems = config.items;
20752 //        config.items = [];
20753     }
20754     
20755     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20756     this.ds = ds;
20757     this.cursor = 0;
20758     if (ds) { 
20759         this.bind(ds);
20760     }
20761     
20762     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20763     
20764 };
20765
20766 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20767     /**
20768      * @cfg {Roo.data.Store} dataSource
20769      * The underlying data store providing the paged data
20770      */
20771     /**
20772      * @cfg {String/HTMLElement/Element} container
20773      * container The id or element that will contain the toolbar
20774      */
20775     /**
20776      * @cfg {Boolean} displayInfo
20777      * True to display the displayMsg (defaults to false)
20778      */
20779     /**
20780      * @cfg {Number} pageSize
20781      * The number of records to display per page (defaults to 20)
20782      */
20783     pageSize: 20,
20784     /**
20785      * @cfg {String} displayMsg
20786      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20787      */
20788     displayMsg : 'Displaying {0} - {1} of {2}',
20789     /**
20790      * @cfg {String} emptyMsg
20791      * The message to display when no records are found (defaults to "No data to display")
20792      */
20793     emptyMsg : 'No data to display',
20794     /**
20795      * Customizable piece of the default paging text (defaults to "Page")
20796      * @type String
20797      */
20798     beforePageText : "Page",
20799     /**
20800      * Customizable piece of the default paging text (defaults to "of %0")
20801      * @type String
20802      */
20803     afterPageText : "of {0}",
20804     /**
20805      * Customizable piece of the default paging text (defaults to "First Page")
20806      * @type String
20807      */
20808     firstText : "First Page",
20809     /**
20810      * Customizable piece of the default paging text (defaults to "Previous Page")
20811      * @type String
20812      */
20813     prevText : "Previous Page",
20814     /**
20815      * Customizable piece of the default paging text (defaults to "Next Page")
20816      * @type String
20817      */
20818     nextText : "Next Page",
20819     /**
20820      * Customizable piece of the default paging text (defaults to "Last Page")
20821      * @type String
20822      */
20823     lastText : "Last Page",
20824     /**
20825      * Customizable piece of the default paging text (defaults to "Refresh")
20826      * @type String
20827      */
20828     refreshText : "Refresh",
20829
20830     buttons : false,
20831     // private
20832     onRender : function(ct, position) 
20833     {
20834         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20835         this.navgroup.parentId = this.id;
20836         this.navgroup.onRender(this.el, null);
20837         // add the buttons to the navgroup
20838         
20839         if(this.displayInfo){
20840             Roo.log(this.el.select('ul.navbar-nav',true).first());
20841             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20842             this.displayEl = this.el.select('.x-paging-info', true).first();
20843 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20844 //            this.displayEl = navel.el.select('span',true).first();
20845         }
20846         
20847         var _this = this;
20848         
20849         if(this.buttons){
20850             Roo.each(_this.buttons, function(e){
20851                Roo.factory(e).onRender(_this.el, null);
20852             });
20853         }
20854             
20855         Roo.each(_this.toolbarItems, function(e) {
20856             _this.navgroup.addItem(e);
20857         });
20858         
20859         
20860         this.first = this.navgroup.addItem({
20861             tooltip: this.firstText,
20862             cls: "prev",
20863             icon : 'fa fa-backward',
20864             disabled: true,
20865             preventDefault: true,
20866             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20867         });
20868         
20869         this.prev =  this.navgroup.addItem({
20870             tooltip: this.prevText,
20871             cls: "prev",
20872             icon : 'fa fa-step-backward',
20873             disabled: true,
20874             preventDefault: true,
20875             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20876         });
20877     //this.addSeparator();
20878         
20879         
20880         var field = this.navgroup.addItem( {
20881             tagtype : 'span',
20882             cls : 'x-paging-position',
20883             
20884             html : this.beforePageText  +
20885                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20886                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20887          } ); //?? escaped?
20888         
20889         this.field = field.el.select('input', true).first();
20890         this.field.on("keydown", this.onPagingKeydown, this);
20891         this.field.on("focus", function(){this.dom.select();});
20892     
20893     
20894         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20895         //this.field.setHeight(18);
20896         //this.addSeparator();
20897         this.next = this.navgroup.addItem({
20898             tooltip: this.nextText,
20899             cls: "next",
20900             html : ' <i class="fa fa-step-forward">',
20901             disabled: true,
20902             preventDefault: true,
20903             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20904         });
20905         this.last = this.navgroup.addItem({
20906             tooltip: this.lastText,
20907             icon : 'fa fa-forward',
20908             cls: "next",
20909             disabled: true,
20910             preventDefault: true,
20911             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20912         });
20913     //this.addSeparator();
20914         this.loading = this.navgroup.addItem({
20915             tooltip: this.refreshText,
20916             icon: 'fa fa-refresh',
20917             preventDefault: true,
20918             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20919         });
20920
20921     },
20922
20923     // private
20924     updateInfo : function(){
20925         if(this.displayEl){
20926             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20927             var msg = count == 0 ?
20928                 this.emptyMsg :
20929                 String.format(
20930                     this.displayMsg,
20931                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20932                 );
20933             this.displayEl.update(msg);
20934         }
20935     },
20936
20937     // private
20938     onLoad : function(ds, r, o){
20939        this.cursor = o.params ? o.params.start : 0;
20940        var d = this.getPageData(),
20941             ap = d.activePage,
20942             ps = d.pages;
20943         
20944        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20945        this.field.dom.value = ap;
20946        this.first.setDisabled(ap == 1);
20947        this.prev.setDisabled(ap == 1);
20948        this.next.setDisabled(ap == ps);
20949        this.last.setDisabled(ap == ps);
20950        this.loading.enable();
20951        this.updateInfo();
20952     },
20953
20954     // private
20955     getPageData : function(){
20956         var total = this.ds.getTotalCount();
20957         return {
20958             total : total,
20959             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20960             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20961         };
20962     },
20963
20964     // private
20965     onLoadError : function(){
20966         this.loading.enable();
20967     },
20968
20969     // private
20970     onPagingKeydown : function(e){
20971         var k = e.getKey();
20972         var d = this.getPageData();
20973         if(k == e.RETURN){
20974             var v = this.field.dom.value, pageNum;
20975             if(!v || isNaN(pageNum = parseInt(v, 10))){
20976                 this.field.dom.value = d.activePage;
20977                 return;
20978             }
20979             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20980             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20981             e.stopEvent();
20982         }
20983         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))
20984         {
20985           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20986           this.field.dom.value = pageNum;
20987           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20988           e.stopEvent();
20989         }
20990         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20991         {
20992           var v = this.field.dom.value, pageNum; 
20993           var increment = (e.shiftKey) ? 10 : 1;
20994           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20995             increment *= -1;
20996           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20997             this.field.dom.value = d.activePage;
20998             return;
20999           }
21000           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21001           {
21002             this.field.dom.value = parseInt(v, 10) + increment;
21003             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21004             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21005           }
21006           e.stopEvent();
21007         }
21008     },
21009
21010     // private
21011     beforeLoad : function(){
21012         if(this.loading){
21013             this.loading.disable();
21014         }
21015     },
21016
21017     // private
21018     onClick : function(which){
21019         
21020         var ds = this.ds;
21021         if (!ds) {
21022             return;
21023         }
21024         
21025         switch(which){
21026             case "first":
21027                 ds.load({params:{start: 0, limit: this.pageSize}});
21028             break;
21029             case "prev":
21030                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21031             break;
21032             case "next":
21033                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21034             break;
21035             case "last":
21036                 var total = ds.getTotalCount();
21037                 var extra = total % this.pageSize;
21038                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21039                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21040             break;
21041             case "refresh":
21042                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21043             break;
21044         }
21045     },
21046
21047     /**
21048      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21049      * @param {Roo.data.Store} store The data store to unbind
21050      */
21051     unbind : function(ds){
21052         ds.un("beforeload", this.beforeLoad, this);
21053         ds.un("load", this.onLoad, this);
21054         ds.un("loadexception", this.onLoadError, this);
21055         ds.un("remove", this.updateInfo, this);
21056         ds.un("add", this.updateInfo, this);
21057         this.ds = undefined;
21058     },
21059
21060     /**
21061      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21062      * @param {Roo.data.Store} store The data store to bind
21063      */
21064     bind : function(ds){
21065         ds.on("beforeload", this.beforeLoad, this);
21066         ds.on("load", this.onLoad, this);
21067         ds.on("loadexception", this.onLoadError, this);
21068         ds.on("remove", this.updateInfo, this);
21069         ds.on("add", this.updateInfo, this);
21070         this.ds = ds;
21071     }
21072 });/*
21073  * - LGPL
21074  *
21075  * element
21076  * 
21077  */
21078
21079 /**
21080  * @class Roo.bootstrap.MessageBar
21081  * @extends Roo.bootstrap.Component
21082  * Bootstrap MessageBar class
21083  * @cfg {String} html contents of the MessageBar
21084  * @cfg {String} weight (info | success | warning | danger) default info
21085  * @cfg {String} beforeClass insert the bar before the given class
21086  * @cfg {Boolean} closable (true | false) default false
21087  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21088  * 
21089  * @constructor
21090  * Create a new Element
21091  * @param {Object} config The config object
21092  */
21093
21094 Roo.bootstrap.MessageBar = function(config){
21095     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21096 };
21097
21098 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21099     
21100     html: '',
21101     weight: 'info',
21102     closable: false,
21103     fixed: false,
21104     beforeClass: 'bootstrap-sticky-wrap',
21105     
21106     getAutoCreate : function(){
21107         
21108         var cfg = {
21109             tag: 'div',
21110             cls: 'alert alert-dismissable alert-' + this.weight,
21111             cn: [
21112                 {
21113                     tag: 'span',
21114                     cls: 'message',
21115                     html: this.html || ''
21116                 }
21117             ]
21118         }
21119         
21120         if(this.fixed){
21121             cfg.cls += ' alert-messages-fixed';
21122         }
21123         
21124         if(this.closable){
21125             cfg.cn.push({
21126                 tag: 'button',
21127                 cls: 'close',
21128                 html: 'x'
21129             });
21130         }
21131         
21132         return cfg;
21133     },
21134     
21135     onRender : function(ct, position)
21136     {
21137         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21138         
21139         if(!this.el){
21140             var cfg = Roo.apply({},  this.getAutoCreate());
21141             cfg.id = Roo.id();
21142             
21143             if (this.cls) {
21144                 cfg.cls += ' ' + this.cls;
21145             }
21146             if (this.style) {
21147                 cfg.style = this.style;
21148             }
21149             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21150             
21151             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21152         }
21153         
21154         this.el.select('>button.close').on('click', this.hide, this);
21155         
21156     },
21157     
21158     show : function()
21159     {
21160         if (!this.rendered) {
21161             this.render();
21162         }
21163         
21164         this.el.show();
21165         
21166         this.fireEvent('show', this);
21167         
21168     },
21169     
21170     hide : function()
21171     {
21172         if (!this.rendered) {
21173             this.render();
21174         }
21175         
21176         this.el.hide();
21177         
21178         this.fireEvent('hide', this);
21179     },
21180     
21181     update : function()
21182     {
21183 //        var e = this.el.dom.firstChild;
21184 //        
21185 //        if(this.closable){
21186 //            e = e.nextSibling;
21187 //        }
21188 //        
21189 //        e.data = this.html || '';
21190
21191         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21192     }
21193    
21194 });
21195
21196  
21197
21198      /*
21199  * - LGPL
21200  *
21201  * Graph
21202  * 
21203  */
21204
21205
21206 /**
21207  * @class Roo.bootstrap.Graph
21208  * @extends Roo.bootstrap.Component
21209  * Bootstrap Graph class
21210 > Prameters
21211  -sm {number} sm 4
21212  -md {number} md 5
21213  @cfg {String} graphtype  bar | vbar | pie
21214  @cfg {number} g_x coodinator | centre x (pie)
21215  @cfg {number} g_y coodinator | centre y (pie)
21216  @cfg {number} g_r radius (pie)
21217  @cfg {number} g_height height of the chart (respected by all elements in the set)
21218  @cfg {number} g_width width of the chart (respected by all elements in the set)
21219  @cfg {Object} title The title of the chart
21220     
21221  -{Array}  values
21222  -opts (object) options for the chart 
21223      o {
21224      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21225      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21226      o vgutter (number)
21227      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.
21228      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21229      o to
21230      o stretch (boolean)
21231      o }
21232  -opts (object) options for the pie
21233      o{
21234      o cut
21235      o startAngle (number)
21236      o endAngle (number)
21237      } 
21238  *
21239  * @constructor
21240  * Create a new Input
21241  * @param {Object} config The config object
21242  */
21243
21244 Roo.bootstrap.Graph = function(config){
21245     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21246     
21247     this.addEvents({
21248         // img events
21249         /**
21250          * @event click
21251          * The img click event for the img.
21252          * @param {Roo.EventObject} e
21253          */
21254         "click" : true
21255     });
21256 };
21257
21258 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21259     
21260     sm: 4,
21261     md: 5,
21262     graphtype: 'bar',
21263     g_height: 250,
21264     g_width: 400,
21265     g_x: 50,
21266     g_y: 50,
21267     g_r: 30,
21268     opts:{
21269         //g_colors: this.colors,
21270         g_type: 'soft',
21271         g_gutter: '20%'
21272
21273     },
21274     title : false,
21275
21276     getAutoCreate : function(){
21277         
21278         var cfg = {
21279             tag: 'div',
21280             html : null
21281         }
21282         
21283         
21284         return  cfg;
21285     },
21286
21287     onRender : function(ct,position){
21288         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21289         this.raphael = Raphael(this.el.dom);
21290         
21291                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21292                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21293                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21294                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21295                 /*
21296                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21297                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21298                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21299                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21300                 
21301                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21302                 r.barchart(330, 10, 300, 220, data1);
21303                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21304                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21305                 */
21306                 
21307                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21308                 // r.barchart(30, 30, 560, 250,  xdata, {
21309                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21310                 //     axis : "0 0 1 1",
21311                 //     axisxlabels :  xdata
21312                 //     //yvalues : cols,
21313                    
21314                 // });
21315 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21316 //        
21317 //        this.load(null,xdata,{
21318 //                axis : "0 0 1 1",
21319 //                axisxlabels :  xdata
21320 //                });
21321
21322     },
21323
21324     load : function(graphtype,xdata,opts){
21325         this.raphael.clear();
21326         if(!graphtype) {
21327             graphtype = this.graphtype;
21328         }
21329         if(!opts){
21330             opts = this.opts;
21331         }
21332         var r = this.raphael,
21333             fin = function () {
21334                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21335             },
21336             fout = function () {
21337                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21338             },
21339             pfin = function() {
21340                 this.sector.stop();
21341                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21342
21343                 if (this.label) {
21344                     this.label[0].stop();
21345                     this.label[0].attr({ r: 7.5 });
21346                     this.label[1].attr({ "font-weight": 800 });
21347                 }
21348             },
21349             pfout = function() {
21350                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21351
21352                 if (this.label) {
21353                     this.label[0].animate({ r: 5 }, 500, "bounce");
21354                     this.label[1].attr({ "font-weight": 400 });
21355                 }
21356             };
21357
21358         switch(graphtype){
21359             case 'bar':
21360                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21361                 break;
21362             case 'hbar':
21363                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21364                 break;
21365             case 'pie':
21366 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21367 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21368 //            
21369                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21370                 
21371                 break;
21372
21373         }
21374         
21375         if(this.title){
21376             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21377         }
21378         
21379     },
21380     
21381     setTitle: function(o)
21382     {
21383         this.title = o;
21384     },
21385     
21386     initEvents: function() {
21387         
21388         if(!this.href){
21389             this.el.on('click', this.onClick, this);
21390         }
21391     },
21392     
21393     onClick : function(e)
21394     {
21395         Roo.log('img onclick');
21396         this.fireEvent('click', this, e);
21397     }
21398    
21399 });
21400
21401  
21402 /*
21403  * - LGPL
21404  *
21405  * numberBox
21406  * 
21407  */
21408 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21409
21410 /**
21411  * @class Roo.bootstrap.dash.NumberBox
21412  * @extends Roo.bootstrap.Component
21413  * Bootstrap NumberBox class
21414  * @cfg {String} headline Box headline
21415  * @cfg {String} content Box content
21416  * @cfg {String} icon Box icon
21417  * @cfg {String} footer Footer text
21418  * @cfg {String} fhref Footer href
21419  * 
21420  * @constructor
21421  * Create a new NumberBox
21422  * @param {Object} config The config object
21423  */
21424
21425
21426 Roo.bootstrap.dash.NumberBox = function(config){
21427     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21428     
21429 };
21430
21431 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21432     
21433     headline : '',
21434     content : '',
21435     icon : '',
21436     footer : '',
21437     fhref : '',
21438     ficon : '',
21439     
21440     getAutoCreate : function(){
21441         
21442         var cfg = {
21443             tag : 'div',
21444             cls : 'small-box ',
21445             cn : [
21446                 {
21447                     tag : 'div',
21448                     cls : 'inner',
21449                     cn :[
21450                         {
21451                             tag : 'h3',
21452                             cls : 'roo-headline',
21453                             html : this.headline
21454                         },
21455                         {
21456                             tag : 'p',
21457                             cls : 'roo-content',
21458                             html : this.content
21459                         }
21460                     ]
21461                 }
21462             ]
21463         }
21464         
21465         if(this.icon){
21466             cfg.cn.push({
21467                 tag : 'div',
21468                 cls : 'icon',
21469                 cn :[
21470                     {
21471                         tag : 'i',
21472                         cls : 'ion ' + this.icon
21473                     }
21474                 ]
21475             });
21476         }
21477         
21478         if(this.footer){
21479             var footer = {
21480                 tag : 'a',
21481                 cls : 'small-box-footer',
21482                 href : this.fhref || '#',
21483                 html : this.footer
21484             };
21485             
21486             cfg.cn.push(footer);
21487             
21488         }
21489         
21490         return  cfg;
21491     },
21492
21493     onRender : function(ct,position){
21494         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21495
21496
21497        
21498                 
21499     },
21500
21501     setHeadline: function (value)
21502     {
21503         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21504     },
21505     
21506     setFooter: function (value, href)
21507     {
21508         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21509         
21510         if(href){
21511             this.el.select('a.small-box-footer',true).first().attr('href', href);
21512         }
21513         
21514     },
21515
21516     setContent: function (value)
21517     {
21518         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21519     },
21520
21521     initEvents: function() 
21522     {   
21523         
21524     }
21525     
21526 });
21527
21528  
21529 /*
21530  * - LGPL
21531  *
21532  * TabBox
21533  * 
21534  */
21535 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21536
21537 /**
21538  * @class Roo.bootstrap.dash.TabBox
21539  * @extends Roo.bootstrap.Component
21540  * Bootstrap TabBox class
21541  * @cfg {String} title Title of the TabBox
21542  * @cfg {String} icon Icon of the TabBox
21543  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21544  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21545  * 
21546  * @constructor
21547  * Create a new TabBox
21548  * @param {Object} config The config object
21549  */
21550
21551
21552 Roo.bootstrap.dash.TabBox = function(config){
21553     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21554     this.addEvents({
21555         // raw events
21556         /**
21557          * @event addpane
21558          * When a pane is added
21559          * @param {Roo.bootstrap.dash.TabPane} pane
21560          */
21561         "addpane" : true,
21562         /**
21563          * @event activatepane
21564          * When a pane is activated
21565          * @param {Roo.bootstrap.dash.TabPane} pane
21566          */
21567         "activatepane" : true
21568         
21569          
21570     });
21571     
21572     this.panes = [];
21573 };
21574
21575 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21576
21577     title : '',
21578     icon : false,
21579     showtabs : true,
21580     tabScrollable : false,
21581     
21582     getChildContainer : function()
21583     {
21584         return this.el.select('.tab-content', true).first();
21585     },
21586     
21587     getAutoCreate : function(){
21588         
21589         var header = {
21590             tag: 'li',
21591             cls: 'pull-left header',
21592             html: this.title,
21593             cn : []
21594         };
21595         
21596         if(this.icon){
21597             header.cn.push({
21598                 tag: 'i',
21599                 cls: 'fa ' + this.icon
21600             });
21601         }
21602         
21603         var h = {
21604             tag: 'ul',
21605             cls: 'nav nav-tabs pull-right',
21606             cn: [
21607                 header
21608             ]
21609         };
21610         
21611         if(this.tabScrollable){
21612             h = {
21613                 tag: 'div',
21614                 cls: 'tab-header',
21615                 cn: [
21616                     {
21617                         tag: 'ul',
21618                         cls: 'nav nav-tabs pull-right',
21619                         cn: [
21620                             header
21621                         ]
21622                     }
21623                 ]
21624             }
21625         }
21626         
21627         var cfg = {
21628             tag: 'div',
21629             cls: 'nav-tabs-custom',
21630             cn: [
21631                 h,
21632                 {
21633                     tag: 'div',
21634                     cls: 'tab-content no-padding',
21635                     cn: []
21636                 }
21637             ]
21638         }
21639
21640         return  cfg;
21641     },
21642     initEvents : function()
21643     {
21644         //Roo.log('add add pane handler');
21645         this.on('addpane', this.onAddPane, this);
21646     },
21647      /**
21648      * Updates the box title
21649      * @param {String} html to set the title to.
21650      */
21651     setTitle : function(value)
21652     {
21653         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21654     },
21655     onAddPane : function(pane)
21656     {
21657         this.panes.push(pane);
21658         //Roo.log('addpane');
21659         //Roo.log(pane);
21660         // tabs are rendere left to right..
21661         if(!this.showtabs){
21662             return;
21663         }
21664         
21665         var ctr = this.el.select('.nav-tabs', true).first();
21666          
21667          
21668         var existing = ctr.select('.nav-tab',true);
21669         var qty = existing.getCount();;
21670         
21671         
21672         var tab = ctr.createChild({
21673             tag : 'li',
21674             cls : 'nav-tab' + (qty ? '' : ' active'),
21675             cn : [
21676                 {
21677                     tag : 'a',
21678                     href:'#',
21679                     html : pane.title
21680                 }
21681             ]
21682         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21683         pane.tab = tab;
21684         
21685         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21686         if (!qty) {
21687             pane.el.addClass('active');
21688         }
21689         
21690                 
21691     },
21692     onTabClick : function(ev,un,ob,pane)
21693     {
21694         //Roo.log('tab - prev default');
21695         ev.preventDefault();
21696         
21697         
21698         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21699         pane.tab.addClass('active');
21700         //Roo.log(pane.title);
21701         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21702         // technically we should have a deactivate event.. but maybe add later.
21703         // and it should not de-activate the selected tab...
21704         this.fireEvent('activatepane', pane);
21705         pane.el.addClass('active');
21706         pane.fireEvent('activate');
21707         
21708         
21709     },
21710     
21711     getActivePane : function()
21712     {
21713         var r = false;
21714         Roo.each(this.panes, function(p) {
21715             if(p.el.hasClass('active')){
21716                 r = p;
21717                 return false;
21718             }
21719             
21720             return;
21721         });
21722         
21723         return r;
21724     }
21725     
21726     
21727 });
21728
21729  
21730 /*
21731  * - LGPL
21732  *
21733  * Tab pane
21734  * 
21735  */
21736 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21737 /**
21738  * @class Roo.bootstrap.TabPane
21739  * @extends Roo.bootstrap.Component
21740  * Bootstrap TabPane class
21741  * @cfg {Boolean} active (false | true) Default false
21742  * @cfg {String} title title of panel
21743
21744  * 
21745  * @constructor
21746  * Create a new TabPane
21747  * @param {Object} config The config object
21748  */
21749
21750 Roo.bootstrap.dash.TabPane = function(config){
21751     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21752     
21753     this.addEvents({
21754         // raw events
21755         /**
21756          * @event activate
21757          * When a pane is activated
21758          * @param {Roo.bootstrap.dash.TabPane} pane
21759          */
21760         "activate" : true
21761          
21762     });
21763 };
21764
21765 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21766     
21767     active : false,
21768     title : '',
21769     
21770     // the tabBox that this is attached to.
21771     tab : false,
21772      
21773     getAutoCreate : function() 
21774     {
21775         var cfg = {
21776             tag: 'div',
21777             cls: 'tab-pane'
21778         }
21779         
21780         if(this.active){
21781             cfg.cls += ' active';
21782         }
21783         
21784         return cfg;
21785     },
21786     initEvents  : function()
21787     {
21788         //Roo.log('trigger add pane handler');
21789         this.parent().fireEvent('addpane', this)
21790     },
21791     
21792      /**
21793      * Updates the tab title 
21794      * @param {String} html to set the title to.
21795      */
21796     setTitle: function(str)
21797     {
21798         if (!this.tab) {
21799             return;
21800         }
21801         this.title = str;
21802         this.tab.select('a', true).first().dom.innerHTML = str;
21803         
21804     }
21805     
21806     
21807     
21808 });
21809
21810  
21811
21812
21813  /*
21814  * - LGPL
21815  *
21816  * menu
21817  * 
21818  */
21819 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21820
21821 /**
21822  * @class Roo.bootstrap.menu.Menu
21823  * @extends Roo.bootstrap.Component
21824  * Bootstrap Menu class - container for Menu
21825  * @cfg {String} html Text of the menu
21826  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21827  * @cfg {String} icon Font awesome icon
21828  * @cfg {String} pos Menu align to (top | bottom) default bottom
21829  * 
21830  * 
21831  * @constructor
21832  * Create a new Menu
21833  * @param {Object} config The config object
21834  */
21835
21836
21837 Roo.bootstrap.menu.Menu = function(config){
21838     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21839     
21840     this.addEvents({
21841         /**
21842          * @event beforeshow
21843          * Fires before this menu is displayed
21844          * @param {Roo.bootstrap.menu.Menu} this
21845          */
21846         beforeshow : true,
21847         /**
21848          * @event beforehide
21849          * Fires before this menu is hidden
21850          * @param {Roo.bootstrap.menu.Menu} this
21851          */
21852         beforehide : true,
21853         /**
21854          * @event show
21855          * Fires after this menu is displayed
21856          * @param {Roo.bootstrap.menu.Menu} this
21857          */
21858         show : true,
21859         /**
21860          * @event hide
21861          * Fires after this menu is hidden
21862          * @param {Roo.bootstrap.menu.Menu} this
21863          */
21864         hide : true,
21865         /**
21866          * @event click
21867          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21868          * @param {Roo.bootstrap.menu.Menu} this
21869          * @param {Roo.EventObject} e
21870          */
21871         click : true
21872     });
21873     
21874 };
21875
21876 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21877     
21878     submenu : false,
21879     html : '',
21880     weight : 'default',
21881     icon : false,
21882     pos : 'bottom',
21883     
21884     
21885     getChildContainer : function() {
21886         if(this.isSubMenu){
21887             return this.el;
21888         }
21889         
21890         return this.el.select('ul.dropdown-menu', true).first();  
21891     },
21892     
21893     getAutoCreate : function()
21894     {
21895         var text = [
21896             {
21897                 tag : 'span',
21898                 cls : 'roo-menu-text',
21899                 html : this.html
21900             }
21901         ];
21902         
21903         if(this.icon){
21904             text.unshift({
21905                 tag : 'i',
21906                 cls : 'fa ' + this.icon
21907             })
21908         }
21909         
21910         
21911         var cfg = {
21912             tag : 'div',
21913             cls : 'btn-group',
21914             cn : [
21915                 {
21916                     tag : 'button',
21917                     cls : 'dropdown-button btn btn-' + this.weight,
21918                     cn : text
21919                 },
21920                 {
21921                     tag : 'button',
21922                     cls : 'dropdown-toggle btn btn-' + this.weight,
21923                     cn : [
21924                         {
21925                             tag : 'span',
21926                             cls : 'caret'
21927                         }
21928                     ]
21929                 },
21930                 {
21931                     tag : 'ul',
21932                     cls : 'dropdown-menu'
21933                 }
21934             ]
21935             
21936         };
21937         
21938         if(this.pos == 'top'){
21939             cfg.cls += ' dropup';
21940         }
21941         
21942         if(this.isSubMenu){
21943             cfg = {
21944                 tag : 'ul',
21945                 cls : 'dropdown-menu'
21946             }
21947         }
21948         
21949         return cfg;
21950     },
21951     
21952     onRender : function(ct, position)
21953     {
21954         this.isSubMenu = ct.hasClass('dropdown-submenu');
21955         
21956         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21957     },
21958     
21959     initEvents : function() 
21960     {
21961         if(this.isSubMenu){
21962             return;
21963         }
21964         
21965         this.hidden = true;
21966         
21967         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21968         this.triggerEl.on('click', this.onTriggerPress, this);
21969         
21970         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21971         this.buttonEl.on('click', this.onClick, this);
21972         
21973     },
21974     
21975     list : function()
21976     {
21977         if(this.isSubMenu){
21978             return this.el;
21979         }
21980         
21981         return this.el.select('ul.dropdown-menu', true).first();
21982     },
21983     
21984     onClick : function(e)
21985     {
21986         this.fireEvent("click", this, e);
21987     },
21988     
21989     onTriggerPress  : function(e)
21990     {   
21991         if (this.isVisible()) {
21992             this.hide();
21993         } else {
21994             this.show();
21995         }
21996     },
21997     
21998     isVisible : function(){
21999         return !this.hidden;
22000     },
22001     
22002     show : function()
22003     {
22004         this.fireEvent("beforeshow", this);
22005         
22006         this.hidden = false;
22007         this.el.addClass('open');
22008         
22009         Roo.get(document).on("mouseup", this.onMouseUp, this);
22010         
22011         this.fireEvent("show", this);
22012         
22013         
22014     },
22015     
22016     hide : function()
22017     {
22018         this.fireEvent("beforehide", this);
22019         
22020         this.hidden = true;
22021         this.el.removeClass('open');
22022         
22023         Roo.get(document).un("mouseup", this.onMouseUp);
22024         
22025         this.fireEvent("hide", this);
22026     },
22027     
22028     onMouseUp : function()
22029     {
22030         this.hide();
22031     }
22032     
22033 });
22034
22035  
22036  /*
22037  * - LGPL
22038  *
22039  * menu item
22040  * 
22041  */
22042 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22043
22044 /**
22045  * @class Roo.bootstrap.menu.Item
22046  * @extends Roo.bootstrap.Component
22047  * Bootstrap MenuItem class
22048  * @cfg {Boolean} submenu (true | false) default false
22049  * @cfg {String} html text of the item
22050  * @cfg {String} href the link
22051  * @cfg {Boolean} disable (true | false) default false
22052  * @cfg {Boolean} preventDefault (true | false) default true
22053  * @cfg {String} icon Font awesome icon
22054  * @cfg {String} pos Submenu align to (left | right) default right 
22055  * 
22056  * 
22057  * @constructor
22058  * Create a new Item
22059  * @param {Object} config The config object
22060  */
22061
22062
22063 Roo.bootstrap.menu.Item = function(config){
22064     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22065     this.addEvents({
22066         /**
22067          * @event mouseover
22068          * Fires when the mouse is hovering over this menu
22069          * @param {Roo.bootstrap.menu.Item} this
22070          * @param {Roo.EventObject} e
22071          */
22072         mouseover : true,
22073         /**
22074          * @event mouseout
22075          * Fires when the mouse exits this menu
22076          * @param {Roo.bootstrap.menu.Item} this
22077          * @param {Roo.EventObject} e
22078          */
22079         mouseout : true,
22080         // raw events
22081         /**
22082          * @event click
22083          * The raw click event for the entire grid.
22084          * @param {Roo.EventObject} e
22085          */
22086         click : true
22087     });
22088 };
22089
22090 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22091     
22092     submenu : false,
22093     href : '',
22094     html : '',
22095     preventDefault: true,
22096     disable : false,
22097     icon : false,
22098     pos : 'right',
22099     
22100     getAutoCreate : function()
22101     {
22102         var text = [
22103             {
22104                 tag : 'span',
22105                 cls : 'roo-menu-item-text',
22106                 html : this.html
22107             }
22108         ];
22109         
22110         if(this.icon){
22111             text.unshift({
22112                 tag : 'i',
22113                 cls : 'fa ' + this.icon
22114             })
22115         }
22116         
22117         var cfg = {
22118             tag : 'li',
22119             cn : [
22120                 {
22121                     tag : 'a',
22122                     href : this.href || '#',
22123                     cn : text
22124                 }
22125             ]
22126         };
22127         
22128         if(this.disable){
22129             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22130         }
22131         
22132         if(this.submenu){
22133             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22134             
22135             if(this.pos == 'left'){
22136                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22137             }
22138         }
22139         
22140         return cfg;
22141     },
22142     
22143     initEvents : function() 
22144     {
22145         this.el.on('mouseover', this.onMouseOver, this);
22146         this.el.on('mouseout', this.onMouseOut, this);
22147         
22148         this.el.select('a', true).first().on('click', this.onClick, this);
22149         
22150     },
22151     
22152     onClick : function(e)
22153     {
22154         if(this.preventDefault){
22155             e.preventDefault();
22156         }
22157         
22158         this.fireEvent("click", this, e);
22159     },
22160     
22161     onMouseOver : function(e)
22162     {
22163         if(this.submenu && this.pos == 'left'){
22164             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22165         }
22166         
22167         this.fireEvent("mouseover", this, e);
22168     },
22169     
22170     onMouseOut : function(e)
22171     {
22172         this.fireEvent("mouseout", this, e);
22173     }
22174 });
22175
22176  
22177
22178  /*
22179  * - LGPL
22180  *
22181  * menu separator
22182  * 
22183  */
22184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22185
22186 /**
22187  * @class Roo.bootstrap.menu.Separator
22188  * @extends Roo.bootstrap.Component
22189  * Bootstrap Separator class
22190  * 
22191  * @constructor
22192  * Create a new Separator
22193  * @param {Object} config The config object
22194  */
22195
22196
22197 Roo.bootstrap.menu.Separator = function(config){
22198     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22199 };
22200
22201 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22202     
22203     getAutoCreate : function(){
22204         var cfg = {
22205             tag : 'li',
22206             cls: 'divider'
22207         };
22208         
22209         return cfg;
22210     }
22211    
22212 });
22213
22214  
22215
22216  /*
22217  * - LGPL
22218  *
22219  * Tooltip
22220  * 
22221  */
22222
22223 /**
22224  * @class Roo.bootstrap.Tooltip
22225  * Bootstrap Tooltip class
22226  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22227  * to determine which dom element triggers the tooltip.
22228  * 
22229  * It needs to add support for additional attributes like tooltip-position
22230  * 
22231  * @constructor
22232  * Create a new Toolti
22233  * @param {Object} config The config object
22234  */
22235
22236 Roo.bootstrap.Tooltip = function(config){
22237     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22238 };
22239
22240 Roo.apply(Roo.bootstrap.Tooltip, {
22241     /**
22242      * @function init initialize tooltip monitoring.
22243      * @static
22244      */
22245     currentEl : false,
22246     currentTip : false,
22247     currentRegion : false,
22248     
22249     //  init : delay?
22250     
22251     init : function()
22252     {
22253         Roo.get(document).on('mouseover', this.enter ,this);
22254         Roo.get(document).on('mouseout', this.leave, this);
22255          
22256         
22257         this.currentTip = new Roo.bootstrap.Tooltip();
22258     },
22259     
22260     enter : function(ev)
22261     {
22262         var dom = ev.getTarget();
22263         
22264         //Roo.log(['enter',dom]);
22265         var el = Roo.fly(dom);
22266         if (this.currentEl) {
22267             //Roo.log(dom);
22268             //Roo.log(this.currentEl);
22269             //Roo.log(this.currentEl.contains(dom));
22270             if (this.currentEl == el) {
22271                 return;
22272             }
22273             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22274                 return;
22275             }
22276
22277         }
22278         
22279         
22280         
22281         if (this.currentTip.el) {
22282             this.currentTip.el.hide(); // force hiding...
22283         }    
22284         //Roo.log(ev);
22285         var bindEl = el;
22286         
22287         // you can not look for children, as if el is the body.. then everythign is the child..
22288         if (!el.attr('tooltip')) { //
22289             if (!el.select("[tooltip]").elements.length) {
22290                 return;
22291             }
22292             // is the mouse over this child...?
22293             bindEl = el.select("[tooltip]").first();
22294             var xy = ev.getXY();
22295             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22296                 //Roo.log("not in region.");
22297                 return;
22298             }
22299             //Roo.log("child element over..");
22300             
22301         }
22302         this.currentEl = bindEl;
22303         this.currentTip.bind(bindEl);
22304         this.currentRegion = Roo.lib.Region.getRegion(dom);
22305         this.currentTip.enter();
22306         
22307     },
22308     leave : function(ev)
22309     {
22310         var dom = ev.getTarget();
22311         //Roo.log(['leave',dom]);
22312         if (!this.currentEl) {
22313             return;
22314         }
22315         
22316         
22317         if (dom != this.currentEl.dom) {
22318             return;
22319         }
22320         var xy = ev.getXY();
22321         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22322             return;
22323         }
22324         // only activate leave if mouse cursor is outside... bounding box..
22325         
22326         
22327         
22328         
22329         if (this.currentTip) {
22330             this.currentTip.leave();
22331         }
22332         //Roo.log('clear currentEl');
22333         this.currentEl = false;
22334         
22335         
22336     },
22337     alignment : {
22338         'left' : ['r-l', [-2,0], 'right'],
22339         'right' : ['l-r', [2,0], 'left'],
22340         'bottom' : ['t-b', [0,2], 'top'],
22341         'top' : [ 'b-t', [0,-2], 'bottom']
22342     }
22343     
22344 });
22345
22346
22347 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22348     
22349     
22350     bindEl : false,
22351     
22352     delay : null, // can be { show : 300 , hide: 500}
22353     
22354     timeout : null,
22355     
22356     hoverState : null, //???
22357     
22358     placement : 'bottom', 
22359     
22360     getAutoCreate : function(){
22361     
22362         var cfg = {
22363            cls : 'tooltip',
22364            role : 'tooltip',
22365            cn : [
22366                 {
22367                     cls : 'tooltip-arrow'
22368                 },
22369                 {
22370                     cls : 'tooltip-inner'
22371                 }
22372            ]
22373         };
22374         
22375         return cfg;
22376     },
22377     bind : function(el)
22378     {
22379         this.bindEl = el;
22380     },
22381       
22382     
22383     enter : function () {
22384        
22385         if (this.timeout != null) {
22386             clearTimeout(this.timeout);
22387         }
22388         
22389         this.hoverState = 'in';
22390          //Roo.log("enter - show");
22391         if (!this.delay || !this.delay.show) {
22392             this.show();
22393             return;
22394         }
22395         var _t = this;
22396         this.timeout = setTimeout(function () {
22397             if (_t.hoverState == 'in') {
22398                 _t.show();
22399             }
22400         }, this.delay.show);
22401     },
22402     leave : function()
22403     {
22404         clearTimeout(this.timeout);
22405     
22406         this.hoverState = 'out';
22407          if (!this.delay || !this.delay.hide) {
22408             this.hide();
22409             return;
22410         }
22411        
22412         var _t = this;
22413         this.timeout = setTimeout(function () {
22414             //Roo.log("leave - timeout");
22415             
22416             if (_t.hoverState == 'out') {
22417                 _t.hide();
22418                 Roo.bootstrap.Tooltip.currentEl = false;
22419             }
22420         }, delay);
22421     },
22422     
22423     show : function ()
22424     {
22425         if (!this.el) {
22426             this.render(document.body);
22427         }
22428         // set content.
22429         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22430         
22431         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22432         
22433         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22434         
22435         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22436         
22437         var placement = typeof this.placement == 'function' ?
22438             this.placement.call(this, this.el, on_el) :
22439             this.placement;
22440             
22441         var autoToken = /\s?auto?\s?/i;
22442         var autoPlace = autoToken.test(placement);
22443         if (autoPlace) {
22444             placement = placement.replace(autoToken, '') || 'top';
22445         }
22446         
22447         //this.el.detach()
22448         //this.el.setXY([0,0]);
22449         this.el.show();
22450         //this.el.dom.style.display='block';
22451         this.el.addClass(placement);
22452         
22453         //this.el.appendTo(on_el);
22454         
22455         var p = this.getPosition();
22456         var box = this.el.getBox();
22457         
22458         if (autoPlace) {
22459             // fixme..
22460         }
22461         var align = Roo.bootstrap.Tooltip.alignment[placement];
22462         this.el.alignTo(this.bindEl, align[0],align[1]);
22463         //var arrow = this.el.select('.arrow',true).first();
22464         //arrow.set(align[2], 
22465         
22466         this.el.addClass('in fade');
22467         this.hoverState = null;
22468         
22469         if (this.el.hasClass('fade')) {
22470             // fade it?
22471         }
22472         
22473     },
22474     hide : function()
22475     {
22476          
22477         if (!this.el) {
22478             return;
22479         }
22480         //this.el.setXY([0,0]);
22481         this.el.removeClass('in');
22482         //this.el.hide();
22483         
22484     }
22485     
22486 });
22487  
22488
22489  /*
22490  * - LGPL
22491  *
22492  * Location Picker
22493  * 
22494  */
22495
22496 /**
22497  * @class Roo.bootstrap.LocationPicker
22498  * @extends Roo.bootstrap.Component
22499  * Bootstrap LocationPicker class
22500  * @cfg {Number} latitude Position when init default 0
22501  * @cfg {Number} longitude Position when init default 0
22502  * @cfg {Number} zoom default 15
22503  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22504  * @cfg {Boolean} mapTypeControl default false
22505  * @cfg {Boolean} disableDoubleClickZoom default false
22506  * @cfg {Boolean} scrollwheel default true
22507  * @cfg {Boolean} streetViewControl default false
22508  * @cfg {Number} radius default 0
22509  * @cfg {String} locationName
22510  * @cfg {Boolean} draggable default true
22511  * @cfg {Boolean} enableAutocomplete default false
22512  * @cfg {Boolean} enableReverseGeocode default true
22513  * @cfg {String} markerTitle
22514  * 
22515  * @constructor
22516  * Create a new LocationPicker
22517  * @param {Object} config The config object
22518  */
22519
22520
22521 Roo.bootstrap.LocationPicker = function(config){
22522     
22523     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22524     
22525     this.addEvents({
22526         /**
22527          * @event initial
22528          * Fires when the picker initialized.
22529          * @param {Roo.bootstrap.LocationPicker} this
22530          * @param {Google Location} location
22531          */
22532         initial : true,
22533         /**
22534          * @event positionchanged
22535          * Fires when the picker position changed.
22536          * @param {Roo.bootstrap.LocationPicker} this
22537          * @param {Google Location} location
22538          */
22539         positionchanged : true,
22540         /**
22541          * @event resize
22542          * Fires when the map resize.
22543          * @param {Roo.bootstrap.LocationPicker} this
22544          */
22545         resize : true,
22546         /**
22547          * @event show
22548          * Fires when the map show.
22549          * @param {Roo.bootstrap.LocationPicker} this
22550          */
22551         show : true,
22552         /**
22553          * @event hide
22554          * Fires when the map hide.
22555          * @param {Roo.bootstrap.LocationPicker} this
22556          */
22557         hide : true,
22558         /**
22559          * @event mapClick
22560          * Fires when click the map.
22561          * @param {Roo.bootstrap.LocationPicker} this
22562          * @param {Map event} e
22563          */
22564         mapClick : true,
22565         /**
22566          * @event mapRightClick
22567          * Fires when right click the map.
22568          * @param {Roo.bootstrap.LocationPicker} this
22569          * @param {Map event} e
22570          */
22571         mapRightClick : true,
22572         /**
22573          * @event markerClick
22574          * Fires when click the marker.
22575          * @param {Roo.bootstrap.LocationPicker} this
22576          * @param {Map event} e
22577          */
22578         markerClick : true,
22579         /**
22580          * @event markerRightClick
22581          * Fires when right click the marker.
22582          * @param {Roo.bootstrap.LocationPicker} this
22583          * @param {Map event} e
22584          */
22585         markerRightClick : true,
22586         /**
22587          * @event OverlayViewDraw
22588          * Fires when OverlayView Draw
22589          * @param {Roo.bootstrap.LocationPicker} this
22590          */
22591         OverlayViewDraw : true,
22592         /**
22593          * @event OverlayViewOnAdd
22594          * Fires when OverlayView Draw
22595          * @param {Roo.bootstrap.LocationPicker} this
22596          */
22597         OverlayViewOnAdd : true,
22598         /**
22599          * @event OverlayViewOnRemove
22600          * Fires when OverlayView Draw
22601          * @param {Roo.bootstrap.LocationPicker} this
22602          */
22603         OverlayViewOnRemove : true,
22604         /**
22605          * @event OverlayViewShow
22606          * Fires when OverlayView Draw
22607          * @param {Roo.bootstrap.LocationPicker} this
22608          * @param {Pixel} cpx
22609          */
22610         OverlayViewShow : true,
22611         /**
22612          * @event OverlayViewHide
22613          * Fires when OverlayView Draw
22614          * @param {Roo.bootstrap.LocationPicker} this
22615          */
22616         OverlayViewHide : true
22617     });
22618         
22619 };
22620
22621 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22622     
22623     gMapContext: false,
22624     
22625     latitude: 0,
22626     longitude: 0,
22627     zoom: 15,
22628     mapTypeId: false,
22629     mapTypeControl: false,
22630     disableDoubleClickZoom: false,
22631     scrollwheel: true,
22632     streetViewControl: false,
22633     radius: 0,
22634     locationName: '',
22635     draggable: true,
22636     enableAutocomplete: false,
22637     enableReverseGeocode: true,
22638     markerTitle: '',
22639     
22640     getAutoCreate: function()
22641     {
22642
22643         var cfg = {
22644             tag: 'div',
22645             cls: 'roo-location-picker'
22646         };
22647         
22648         return cfg
22649     },
22650     
22651     initEvents: function(ct, position)
22652     {       
22653         if(!this.el.getWidth() || this.isApplied()){
22654             return;
22655         }
22656         
22657         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22658         
22659         this.initial();
22660     },
22661     
22662     initial: function()
22663     {
22664         if(!this.mapTypeId){
22665             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22666         }
22667         
22668         this.gMapContext = this.GMapContext();
22669         
22670         this.initOverlayView();
22671         
22672         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22673         
22674         var _this = this;
22675                 
22676         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22677             _this.setPosition(_this.gMapContext.marker.position);
22678         });
22679         
22680         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22681             _this.fireEvent('mapClick', this, event);
22682             
22683         });
22684
22685         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22686             _this.fireEvent('mapRightClick', this, event);
22687             
22688         });
22689         
22690         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22691             _this.fireEvent('markerClick', this, event);
22692             
22693         });
22694
22695         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22696             _this.fireEvent('markerRightClick', this, event);
22697             
22698         });
22699         
22700         this.setPosition(this.gMapContext.location);
22701         
22702         this.fireEvent('initial', this, this.gMapContext.location);
22703     },
22704     
22705     initOverlayView: function()
22706     {
22707         var _this = this;
22708         
22709         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22710             
22711             draw: function()
22712             {
22713                 _this.fireEvent('OverlayViewDraw', _this);
22714             },
22715             
22716             onAdd: function()
22717             {
22718                 _this.fireEvent('OverlayViewOnAdd', _this);
22719             },
22720             
22721             onRemove: function()
22722             {
22723                 _this.fireEvent('OverlayViewOnRemove', _this);
22724             },
22725             
22726             show: function(cpx)
22727             {
22728                 _this.fireEvent('OverlayViewShow', _this, cpx);
22729             },
22730             
22731             hide: function()
22732             {
22733                 _this.fireEvent('OverlayViewHide', _this);
22734             }
22735             
22736         });
22737     },
22738     
22739     fromLatLngToContainerPixel: function(event)
22740     {
22741         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22742     },
22743     
22744     isApplied: function() 
22745     {
22746         return this.getGmapContext() == false ? false : true;
22747     },
22748     
22749     getGmapContext: function() 
22750     {
22751         return this.gMapContext
22752     },
22753     
22754     GMapContext: function() 
22755     {
22756         var position = new google.maps.LatLng(this.latitude, this.longitude);
22757         
22758         var _map = new google.maps.Map(this.el.dom, {
22759             center: position,
22760             zoom: this.zoom,
22761             mapTypeId: this.mapTypeId,
22762             mapTypeControl: this.mapTypeControl,
22763             disableDoubleClickZoom: this.disableDoubleClickZoom,
22764             scrollwheel: this.scrollwheel,
22765             streetViewControl: this.streetViewControl,
22766             locationName: this.locationName,
22767             draggable: this.draggable,
22768             enableAutocomplete: this.enableAutocomplete,
22769             enableReverseGeocode: this.enableReverseGeocode
22770         });
22771         
22772         var _marker = new google.maps.Marker({
22773             position: position,
22774             map: _map,
22775             title: this.markerTitle,
22776             draggable: this.draggable
22777         });
22778         
22779         return {
22780             map: _map,
22781             marker: _marker,
22782             circle: null,
22783             location: position,
22784             radius: this.radius,
22785             locationName: this.locationName,
22786             addressComponents: {
22787                 formatted_address: null,
22788                 addressLine1: null,
22789                 addressLine2: null,
22790                 streetName: null,
22791                 streetNumber: null,
22792                 city: null,
22793                 district: null,
22794                 state: null,
22795                 stateOrProvince: null
22796             },
22797             settings: this,
22798             domContainer: this.el.dom,
22799             geodecoder: new google.maps.Geocoder()
22800         };
22801     },
22802     
22803     drawCircle: function(center, radius, options) 
22804     {
22805         if (this.gMapContext.circle != null) {
22806             this.gMapContext.circle.setMap(null);
22807         }
22808         if (radius > 0) {
22809             radius *= 1;
22810             options = Roo.apply({}, options, {
22811                 strokeColor: "#0000FF",
22812                 strokeOpacity: .35,
22813                 strokeWeight: 2,
22814                 fillColor: "#0000FF",
22815                 fillOpacity: .2
22816             });
22817             
22818             options.map = this.gMapContext.map;
22819             options.radius = radius;
22820             options.center = center;
22821             this.gMapContext.circle = new google.maps.Circle(options);
22822             return this.gMapContext.circle;
22823         }
22824         
22825         return null;
22826     },
22827     
22828     setPosition: function(location) 
22829     {
22830         this.gMapContext.location = location;
22831         this.gMapContext.marker.setPosition(location);
22832         this.gMapContext.map.panTo(location);
22833         this.drawCircle(location, this.gMapContext.radius, {});
22834         
22835         var _this = this;
22836         
22837         if (this.gMapContext.settings.enableReverseGeocode) {
22838             this.gMapContext.geodecoder.geocode({
22839                 latLng: this.gMapContext.location
22840             }, function(results, status) {
22841                 
22842                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22843                     _this.gMapContext.locationName = results[0].formatted_address;
22844                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22845                     
22846                     _this.fireEvent('positionchanged', this, location);
22847                 }
22848             });
22849             
22850             return;
22851         }
22852         
22853         this.fireEvent('positionchanged', this, location);
22854     },
22855     
22856     resize: function()
22857     {
22858         google.maps.event.trigger(this.gMapContext.map, "resize");
22859         
22860         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22861         
22862         this.fireEvent('resize', this);
22863     },
22864     
22865     setPositionByLatLng: function(latitude, longitude)
22866     {
22867         this.setPosition(new google.maps.LatLng(latitude, longitude));
22868     },
22869     
22870     getCurrentPosition: function() 
22871     {
22872         return {
22873             latitude: this.gMapContext.location.lat(),
22874             longitude: this.gMapContext.location.lng()
22875         };
22876     },
22877     
22878     getAddressName: function() 
22879     {
22880         return this.gMapContext.locationName;
22881     },
22882     
22883     getAddressComponents: function() 
22884     {
22885         return this.gMapContext.addressComponents;
22886     },
22887     
22888     address_component_from_google_geocode: function(address_components) 
22889     {
22890         var result = {};
22891         
22892         for (var i = 0; i < address_components.length; i++) {
22893             var component = address_components[i];
22894             if (component.types.indexOf("postal_code") >= 0) {
22895                 result.postalCode = component.short_name;
22896             } else if (component.types.indexOf("street_number") >= 0) {
22897                 result.streetNumber = component.short_name;
22898             } else if (component.types.indexOf("route") >= 0) {
22899                 result.streetName = component.short_name;
22900             } else if (component.types.indexOf("neighborhood") >= 0) {
22901                 result.city = component.short_name;
22902             } else if (component.types.indexOf("locality") >= 0) {
22903                 result.city = component.short_name;
22904             } else if (component.types.indexOf("sublocality") >= 0) {
22905                 result.district = component.short_name;
22906             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22907                 result.stateOrProvince = component.short_name;
22908             } else if (component.types.indexOf("country") >= 0) {
22909                 result.country = component.short_name;
22910             }
22911         }
22912         
22913         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22914         result.addressLine2 = "";
22915         return result;
22916     },
22917     
22918     setZoomLevel: function(zoom)
22919     {
22920         this.gMapContext.map.setZoom(zoom);
22921     },
22922     
22923     show: function()
22924     {
22925         if(!this.el){
22926             return;
22927         }
22928         
22929         this.el.show();
22930         
22931         this.resize();
22932         
22933         this.fireEvent('show', this);
22934     },
22935     
22936     hide: function()
22937     {
22938         if(!this.el){
22939             return;
22940         }
22941         
22942         this.el.hide();
22943         
22944         this.fireEvent('hide', this);
22945     }
22946     
22947 });
22948
22949 Roo.apply(Roo.bootstrap.LocationPicker, {
22950     
22951     OverlayView : function(map, options)
22952     {
22953         options = options || {};
22954         
22955         this.setMap(map);
22956     }
22957     
22958     
22959 });/*
22960  * - LGPL
22961  *
22962  * Alert
22963  * 
22964  */
22965
22966 /**
22967  * @class Roo.bootstrap.Alert
22968  * @extends Roo.bootstrap.Component
22969  * Bootstrap Alert class
22970  * @cfg {String} title The title of alert
22971  * @cfg {String} html The content of alert
22972  * @cfg {String} weight (  success | info | warning | danger )
22973  * @cfg {String} faicon font-awesomeicon
22974  * 
22975  * @constructor
22976  * Create a new alert
22977  * @param {Object} config The config object
22978  */
22979
22980
22981 Roo.bootstrap.Alert = function(config){
22982     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22983     
22984 };
22985
22986 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22987     
22988     title: '',
22989     html: '',
22990     weight: false,
22991     faicon: false,
22992     
22993     getAutoCreate : function()
22994     {
22995         
22996         var cfg = {
22997             tag : 'div',
22998             cls : 'alert',
22999             cn : [
23000                 {
23001                     tag : 'i',
23002                     cls : 'roo-alert-icon'
23003                     
23004                 },
23005                 {
23006                     tag : 'b',
23007                     cls : 'roo-alert-title',
23008                     html : this.title
23009                 },
23010                 {
23011                     tag : 'span',
23012                     cls : 'roo-alert-text',
23013                     html : this.html
23014                 }
23015             ]
23016         };
23017         
23018         if(this.faicon){
23019             cfg.cn[0].cls += ' fa ' + this.faicon;
23020         }
23021         
23022         if(this.weight){
23023             cfg.cls += ' alert-' + this.weight;
23024         }
23025         
23026         return cfg;
23027     },
23028     
23029     initEvents: function() 
23030     {
23031         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23032     },
23033     
23034     setTitle : function(str)
23035     {
23036         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23037     },
23038     
23039     setText : function(str)
23040     {
23041         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23042     },
23043     
23044     setWeight : function(weight)
23045     {
23046         if(this.weight){
23047             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23048         }
23049         
23050         this.weight = weight;
23051         
23052         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23053     },
23054     
23055     setIcon : function(icon)
23056     {
23057         if(this.faicon){
23058             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23059         }
23060         
23061         this.faicon = icon
23062         
23063         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23064     },
23065     
23066     hide: function() 
23067     {
23068         this.el.hide();   
23069     },
23070     
23071     show: function() 
23072     {  
23073         this.el.show();   
23074     }
23075     
23076 });
23077
23078