Roo/data/JsonReader.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * 
1330  * @constructor
1331  * Create a new Input
1332  * @param {Object} config The config object
1333  */
1334
1335 Roo.bootstrap.Img = function(config){
1336     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1337     
1338     this.addEvents({
1339         // img events
1340         /**
1341          * @event click
1342          * The img click event for the img.
1343          * @param {Roo.EventObject} e
1344          */
1345         "click" : true
1346     });
1347 };
1348
1349 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1350     
1351     imgResponsive: true,
1352     border: '',
1353     src: '',
1354     href: false,
1355     target: false,
1356
1357     getAutoCreate : function(){
1358         
1359         var cfg = {
1360             tag: 'img',
1361             cls: (this.imgResponsive) ? 'img-responsive' : '',
1362             html : null
1363         }
1364         
1365         cfg.html = this.html || cfg.html;
1366         
1367         cfg.src = this.src || cfg.src;
1368         
1369         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1370             cfg.cls += ' img-' + this.border;
1371         }
1372         
1373         if(this.alt){
1374             cfg.alt = this.alt;
1375         }
1376         
1377         if(this.href){
1378             var a = {
1379                 tag: 'a',
1380                 href: this.href,
1381                 cn: [
1382                     cfg
1383                 ]
1384             }
1385             
1386             if(this.target){
1387                 a.target = this.target;
1388             }
1389             
1390         }
1391         
1392         
1393         return (this.href) ? a : cfg;
1394     },
1395     
1396     initEvents: function() {
1397         
1398         if(!this.href){
1399             this.el.on('click', this.onClick, this);
1400         }
1401     },
1402     
1403     onClick : function(e)
1404     {
1405         Roo.log('img onclick');
1406         this.fireEvent('click', this, e);
1407     }
1408    
1409 });
1410
1411  /*
1412  * - LGPL
1413  *
1414  * image
1415  * 
1416  */
1417
1418
1419 /**
1420  * @class Roo.bootstrap.Link
1421  * @extends Roo.bootstrap.Component
1422  * Bootstrap Link Class
1423  * @cfg {String} alt image alternative text
1424  * @cfg {String} href a tag href
1425  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1426  * @cfg {String} html the content of the link.
1427  * @cfg {String} anchor name for the anchor link
1428
1429  * @cfg {Boolean} preventDefault (true | false) default false
1430
1431  * 
1432  * @constructor
1433  * Create a new Input
1434  * @param {Object} config The config object
1435  */
1436
1437 Roo.bootstrap.Link = function(config){
1438     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1439     
1440     this.addEvents({
1441         // img events
1442         /**
1443          * @event click
1444          * The img click event for the img.
1445          * @param {Roo.EventObject} e
1446          */
1447         "click" : true
1448     });
1449 };
1450
1451 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1452     
1453     href: false,
1454     target: false,
1455     preventDefault: false,
1456     anchor : false,
1457     alt : false,
1458
1459     getAutoCreate : function()
1460     {
1461         
1462         var cfg = {
1463             tag: 'a'
1464         };
1465         // anchor's do not require html/href...
1466         if (this.anchor === false) {
1467             cfg.html = this.html || 'html-missing';
1468             cfg.href = this.href || '#';
1469         } else {
1470             cfg.name = this.anchor;
1471             if (this.html !== false) {
1472                 cfg.html = this.html;
1473             }
1474             if (this.href !== false) {
1475                 cfg.href = this.href;
1476             }
1477         }
1478         
1479         if(this.alt !== false){
1480             cfg.alt = this.alt;
1481         }
1482         
1483         
1484         if(this.target !== false) {
1485             cfg.target = this.target;
1486         }
1487         
1488         return cfg;
1489     },
1490     
1491     initEvents: function() {
1492         
1493         if(!this.href || this.preventDefault){
1494             this.el.on('click', this.onClick, this);
1495         }
1496     },
1497     
1498     onClick : function(e)
1499     {
1500         if(this.preventDefault){
1501             e.preventDefault();
1502         }
1503         //Roo.log('img onclick');
1504         this.fireEvent('click', this, e);
1505     }
1506    
1507 });
1508
1509  /*
1510  * - LGPL
1511  *
1512  * header
1513  * 
1514  */
1515
1516 /**
1517  * @class Roo.bootstrap.Header
1518  * @extends Roo.bootstrap.Component
1519  * Bootstrap Header class
1520  * @cfg {String} html content of header
1521  * @cfg {Number} level (1|2|3|4|5|6) default 1
1522  * 
1523  * @constructor
1524  * Create a new Header
1525  * @param {Object} config The config object
1526  */
1527
1528
1529 Roo.bootstrap.Header  = function(config){
1530     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1531 };
1532
1533 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1534     
1535     //href : false,
1536     html : false,
1537     level : 1,
1538     
1539     
1540     
1541     getAutoCreate : function(){
1542         
1543         
1544         
1545         var cfg = {
1546             tag: 'h' + (1 *this.level),
1547             html: this.html || ''
1548         } ;
1549         
1550         return cfg;
1551     }
1552    
1553 });
1554
1555  
1556
1557  /*
1558  * Based on:
1559  * Ext JS Library 1.1.1
1560  * Copyright(c) 2006-2007, Ext JS, LLC.
1561  *
1562  * Originally Released Under LGPL - original licence link has changed is not relivant.
1563  *
1564  * Fork - LGPL
1565  * <script type="text/javascript">
1566  */
1567  
1568 /**
1569  * @class Roo.bootstrap.MenuMgr
1570  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1571  * @singleton
1572  */
1573 Roo.bootstrap.MenuMgr = function(){
1574    var menus, active, groups = {}, attached = false, lastShow = new Date();
1575
1576    // private - called when first menu is created
1577    function init(){
1578        menus = {};
1579        active = new Roo.util.MixedCollection();
1580        Roo.get(document).addKeyListener(27, function(){
1581            if(active.length > 0){
1582                hideAll();
1583            }
1584        });
1585    }
1586
1587    // private
1588    function hideAll(){
1589        if(active && active.length > 0){
1590            var c = active.clone();
1591            c.each(function(m){
1592                m.hide();
1593            });
1594        }
1595    }
1596
1597    // private
1598    function onHide(m){
1599        active.remove(m);
1600        if(active.length < 1){
1601            Roo.get(document).un("mouseup", onMouseDown);
1602             
1603            attached = false;
1604        }
1605    }
1606
1607    // private
1608    function onShow(m){
1609        var last = active.last();
1610        lastShow = new Date();
1611        active.add(m);
1612        if(!attached){
1613           Roo.get(document).on("mouseup", onMouseDown);
1614            
1615            attached = true;
1616        }
1617        if(m.parentMenu){
1618           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1619           m.parentMenu.activeChild = m;
1620        }else if(last && last.isVisible()){
1621           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1622        }
1623    }
1624
1625    // private
1626    function onBeforeHide(m){
1627        if(m.activeChild){
1628            m.activeChild.hide();
1629        }
1630        if(m.autoHideTimer){
1631            clearTimeout(m.autoHideTimer);
1632            delete m.autoHideTimer;
1633        }
1634    }
1635
1636    // private
1637    function onBeforeShow(m){
1638        var pm = m.parentMenu;
1639        if(!pm && !m.allowOtherMenus){
1640            hideAll();
1641        }else if(pm && pm.activeChild && active != m){
1642            pm.activeChild.hide();
1643        }
1644    }
1645
1646    // private this should really trigger on mouseup..
1647    function onMouseDown(e){
1648         Roo.log("on Mouse Up");
1649         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1650             Roo.log("hideAll");
1651             hideAll();
1652             e.stopEvent();
1653         }
1654         
1655         
1656    }
1657
1658    // private
1659    function onBeforeCheck(mi, state){
1660        if(state){
1661            var g = groups[mi.group];
1662            for(var i = 0, l = g.length; i < l; i++){
1663                if(g[i] != mi){
1664                    g[i].setChecked(false);
1665                }
1666            }
1667        }
1668    }
1669
1670    return {
1671
1672        /**
1673         * Hides all menus that are currently visible
1674         */
1675        hideAll : function(){
1676             hideAll();  
1677        },
1678
1679        // private
1680        register : function(menu){
1681            if(!menus){
1682                init();
1683            }
1684            menus[menu.id] = menu;
1685            menu.on("beforehide", onBeforeHide);
1686            menu.on("hide", onHide);
1687            menu.on("beforeshow", onBeforeShow);
1688            menu.on("show", onShow);
1689            var g = menu.group;
1690            if(g && menu.events["checkchange"]){
1691                if(!groups[g]){
1692                    groups[g] = [];
1693                }
1694                groups[g].push(menu);
1695                menu.on("checkchange", onCheck);
1696            }
1697        },
1698
1699         /**
1700          * Returns a {@link Roo.menu.Menu} object
1701          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1702          * be used to generate and return a new Menu instance.
1703          */
1704        get : function(menu){
1705            if(typeof menu == "string"){ // menu id
1706                return menus[menu];
1707            }else if(menu.events){  // menu instance
1708                return menu;
1709            }
1710            /*else if(typeof menu.length == 'number'){ // array of menu items?
1711                return new Roo.bootstrap.Menu({items:menu});
1712            }else{ // otherwise, must be a config
1713                return new Roo.bootstrap.Menu(menu);
1714            }
1715            */
1716            return false;
1717        },
1718
1719        // private
1720        unregister : function(menu){
1721            delete menus[menu.id];
1722            menu.un("beforehide", onBeforeHide);
1723            menu.un("hide", onHide);
1724            menu.un("beforeshow", onBeforeShow);
1725            menu.un("show", onShow);
1726            var g = menu.group;
1727            if(g && menu.events["checkchange"]){
1728                groups[g].remove(menu);
1729                menu.un("checkchange", onCheck);
1730            }
1731        },
1732
1733        // private
1734        registerCheckable : function(menuItem){
1735            var g = menuItem.group;
1736            if(g){
1737                if(!groups[g]){
1738                    groups[g] = [];
1739                }
1740                groups[g].push(menuItem);
1741                menuItem.on("beforecheckchange", onBeforeCheck);
1742            }
1743        },
1744
1745        // private
1746        unregisterCheckable : function(menuItem){
1747            var g = menuItem.group;
1748            if(g){
1749                groups[g].remove(menuItem);
1750                menuItem.un("beforecheckchange", onBeforeCheck);
1751            }
1752        }
1753    };
1754 }();/*
1755  * - LGPL
1756  *
1757  * menu
1758  * 
1759  */
1760
1761 /**
1762  * @class Roo.bootstrap.Menu
1763  * @extends Roo.bootstrap.Component
1764  * Bootstrap Menu class - container for MenuItems
1765  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1766  * 
1767  * @constructor
1768  * Create a new Menu
1769  * @param {Object} config The config object
1770  */
1771
1772
1773 Roo.bootstrap.Menu = function(config){
1774     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1775     if (this.registerMenu) {
1776         Roo.bootstrap.MenuMgr.register(this);
1777     }
1778     this.addEvents({
1779         /**
1780          * @event beforeshow
1781          * Fires before this menu is displayed
1782          * @param {Roo.menu.Menu} this
1783          */
1784         beforeshow : true,
1785         /**
1786          * @event beforehide
1787          * Fires before this menu is hidden
1788          * @param {Roo.menu.Menu} this
1789          */
1790         beforehide : true,
1791         /**
1792          * @event show
1793          * Fires after this menu is displayed
1794          * @param {Roo.menu.Menu} this
1795          */
1796         show : true,
1797         /**
1798          * @event hide
1799          * Fires after this menu is hidden
1800          * @param {Roo.menu.Menu} this
1801          */
1802         hide : true,
1803         /**
1804          * @event click
1805          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1806          * @param {Roo.menu.Menu} this
1807          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1808          * @param {Roo.EventObject} e
1809          */
1810         click : true,
1811         /**
1812          * @event mouseover
1813          * Fires when the mouse is hovering over this menu
1814          * @param {Roo.menu.Menu} this
1815          * @param {Roo.EventObject} e
1816          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1817          */
1818         mouseover : true,
1819         /**
1820          * @event mouseout
1821          * Fires when the mouse exits this menu
1822          * @param {Roo.menu.Menu} this
1823          * @param {Roo.EventObject} e
1824          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1825          */
1826         mouseout : true,
1827         /**
1828          * @event itemclick
1829          * Fires when a menu item contained in this menu is clicked
1830          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1831          * @param {Roo.EventObject} e
1832          */
1833         itemclick: true
1834     });
1835     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1836 };
1837
1838 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1839     
1840    /// html : false,
1841     //align : '',
1842     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1843     type: false,
1844     /**
1845      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1846      */
1847     registerMenu : true,
1848     
1849     menuItems :false, // stores the menu items..
1850     
1851     hidden:true,
1852     
1853     parentMenu : false,
1854     
1855     getChildContainer : function() {
1856         return this.el;  
1857     },
1858     
1859     getAutoCreate : function(){
1860          
1861         //if (['right'].indexOf(this.align)!==-1) {
1862         //    cfg.cn[1].cls += ' pull-right'
1863         //}
1864         
1865         
1866         var cfg = {
1867             tag : 'ul',
1868             cls : 'dropdown-menu' ,
1869             style : 'z-index:1000'
1870             
1871         }
1872         
1873         if (this.type === 'submenu') {
1874             cfg.cls = 'submenu active';
1875         }
1876         if (this.type === 'treeview') {
1877             cfg.cls = 'treeview-menu';
1878         }
1879         
1880         return cfg;
1881     },
1882     initEvents : function() {
1883         
1884        // Roo.log("ADD event");
1885        // Roo.log(this.triggerEl.dom);
1886         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1887         
1888         this.triggerEl.addClass('dropdown-toggle');
1889         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1890
1891         this.el.on("mouseover", this.onMouseOver, this);
1892         this.el.on("mouseout", this.onMouseOut, this);
1893         
1894         
1895     },
1896     findTargetItem : function(e){
1897         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1898         if(!t){
1899             return false;
1900         }
1901         //Roo.log(t);         Roo.log(t.id);
1902         if(t && t.id){
1903             //Roo.log(this.menuitems);
1904             return this.menuitems.get(t.id);
1905             
1906             //return this.items.get(t.menuItemId);
1907         }
1908         
1909         return false;
1910     },
1911     onClick : function(e){
1912         Roo.log("menu.onClick");
1913         var t = this.findTargetItem(e);
1914         if(!t || t.isContainer){
1915             return;
1916         }
1917         Roo.log(e);
1918         /*
1919         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1920             if(t == this.activeItem && t.shouldDeactivate(e)){
1921                 this.activeItem.deactivate();
1922                 delete this.activeItem;
1923                 return;
1924             }
1925             if(t.canActivate){
1926                 this.setActiveItem(t, true);
1927             }
1928             return;
1929             
1930             
1931         }
1932         */
1933        
1934         Roo.log('pass click event');
1935         
1936         t.onClick(e);
1937         
1938         this.fireEvent("click", this, t, e);
1939         
1940         this.hide();
1941     },
1942      onMouseOver : function(e){
1943         var t  = this.findTargetItem(e);
1944         //Roo.log(t);
1945         //if(t){
1946         //    if(t.canActivate && !t.disabled){
1947         //        this.setActiveItem(t, true);
1948         //    }
1949         //}
1950         
1951         this.fireEvent("mouseover", this, e, t);
1952     },
1953     isVisible : function(){
1954         return !this.hidden;
1955     },
1956      onMouseOut : function(e){
1957         var t  = this.findTargetItem(e);
1958         
1959         //if(t ){
1960         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1961         //        this.activeItem.deactivate();
1962         //        delete this.activeItem;
1963         //    }
1964         //}
1965         this.fireEvent("mouseout", this, e, t);
1966     },
1967     
1968     
1969     /**
1970      * Displays this menu relative to another element
1971      * @param {String/HTMLElement/Roo.Element} element The element to align to
1972      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1973      * the element (defaults to this.defaultAlign)
1974      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1975      */
1976     show : function(el, pos, parentMenu){
1977         this.parentMenu = parentMenu;
1978         if(!this.el){
1979             this.render();
1980         }
1981         this.fireEvent("beforeshow", this);
1982         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1983     },
1984      /**
1985      * Displays this menu at a specific xy position
1986      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1987      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1988      */
1989     showAt : function(xy, parentMenu, /* private: */_e){
1990         this.parentMenu = parentMenu;
1991         if(!this.el){
1992             this.render();
1993         }
1994         if(_e !== false){
1995             this.fireEvent("beforeshow", this);
1996             //xy = this.el.adjustForConstraints(xy);
1997         }
1998         
1999         //this.el.show();
2000         this.hideMenuItems();
2001         this.hidden = false;
2002         this.triggerEl.addClass('open');
2003         
2004         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2005             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2006         }
2007         
2008         this.el.setXY(xy);
2009         this.focus();
2010         this.fireEvent("show", this);
2011     },
2012     
2013     focus : function(){
2014         return;
2015         if(!this.hidden){
2016             this.doFocus.defer(50, this);
2017         }
2018     },
2019
2020     doFocus : function(){
2021         if(!this.hidden){
2022             this.focusEl.focus();
2023         }
2024     },
2025
2026     /**
2027      * Hides this menu and optionally all parent menus
2028      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2029      */
2030     hide : function(deep){
2031         
2032         this.hideMenuItems();
2033         if(this.el && this.isVisible()){
2034             this.fireEvent("beforehide", this);
2035             if(this.activeItem){
2036                 this.activeItem.deactivate();
2037                 this.activeItem = null;
2038             }
2039             this.triggerEl.removeClass('open');;
2040             this.hidden = true;
2041             this.fireEvent("hide", this);
2042         }
2043         if(deep === true && this.parentMenu){
2044             this.parentMenu.hide(true);
2045         }
2046     },
2047     
2048     onTriggerPress  : function(e)
2049     {
2050         
2051         Roo.log('trigger press');
2052         //Roo.log(e.getTarget());
2053        // Roo.log(this.triggerEl.dom);
2054         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2055             return;
2056         }
2057         
2058         if (this.isVisible()) {
2059             Roo.log('hide');
2060             this.hide();
2061         } else {
2062             Roo.log('show');
2063             this.show(this.triggerEl, false, false);
2064         }
2065         
2066         e.stopEvent();
2067     },
2068     
2069          
2070        
2071     
2072     hideMenuItems : function()
2073     {
2074         //$(backdrop).remove()
2075         Roo.select('.open',true).each(function(aa) {
2076             
2077             aa.removeClass('open');
2078           //var parent = getParent($(this))
2079           //var relatedTarget = { relatedTarget: this }
2080           
2081            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2082           //if (e.isDefaultPrevented()) return
2083            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2084         })
2085     },
2086     addxtypeChild : function (tree, cntr) {
2087         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2088           
2089         this.menuitems.add(comp);
2090         return comp;
2091
2092     },
2093     getEl : function()
2094     {
2095         Roo.log(this.el);
2096         return this.el;
2097     }
2098 });
2099
2100  
2101  /*
2102  * - LGPL
2103  *
2104  * menu item
2105  * 
2106  */
2107
2108
2109 /**
2110  * @class Roo.bootstrap.MenuItem
2111  * @extends Roo.bootstrap.Component
2112  * Bootstrap MenuItem class
2113  * @cfg {String} html the menu label
2114  * @cfg {String} href the link
2115  * @cfg {Boolean} preventDefault (true | false) default true
2116  * @cfg {Boolean} isContainer (true | false) default false
2117  * 
2118  * 
2119  * @constructor
2120  * Create a new MenuItem
2121  * @param {Object} config The config object
2122  */
2123
2124
2125 Roo.bootstrap.MenuItem = function(config){
2126     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2127     this.addEvents({
2128         // raw events
2129         /**
2130          * @event click
2131          * The raw click event for the entire grid.
2132          * @param {Roo.bootstrap.MenuItem} this
2133          * @param {Roo.EventObject} e
2134          */
2135         "click" : true
2136     });
2137 };
2138
2139 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2140     
2141     href : false,
2142     html : false,
2143     preventDefault: true,
2144     isContainer : false,
2145     
2146     getAutoCreate : function(){
2147         
2148         if(this.isContainer){
2149             return {
2150                 tag: 'li',
2151                 cls: 'dropdown-menu-item'
2152             };
2153         }
2154         
2155         var cfg= {
2156             tag: 'li',
2157             cls: 'dropdown-menu-item',
2158             cn: [
2159                     {
2160                         tag : 'a',
2161                         href : '#',
2162                         html : 'Link'
2163                     }
2164                 ]
2165         };
2166         if (this.parent().type == 'treeview') {
2167             cfg.cls = 'treeview-menu';
2168         }
2169         
2170         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2171         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2172         return cfg;
2173     },
2174     
2175     initEvents: function() {
2176         
2177         //this.el.select('a').on('click', this.onClick, this);
2178         
2179     },
2180     onClick : function(e)
2181     {
2182         Roo.log('item on click ');
2183         //if(this.preventDefault){
2184         //    e.preventDefault();
2185         //}
2186         //this.parent().hideMenuItems();
2187         
2188         this.fireEvent('click', this, e);
2189     },
2190     getEl : function()
2191     {
2192         return this.el;
2193     }
2194 });
2195
2196  
2197
2198  /*
2199  * - LGPL
2200  *
2201  * menu separator
2202  * 
2203  */
2204
2205
2206 /**
2207  * @class Roo.bootstrap.MenuSeparator
2208  * @extends Roo.bootstrap.Component
2209  * Bootstrap MenuSeparator class
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuSeparator = function(config){
2218     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2219 };
2220
2221 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2222     
2223     getAutoCreate : function(){
2224         var cfg = {
2225             cls: 'divider',
2226             tag : 'li'
2227         };
2228         
2229         return cfg;
2230     }
2231    
2232 });
2233
2234  
2235
2236  
2237 /*
2238 * Licence: LGPL
2239 */
2240
2241 /**
2242  * @class Roo.bootstrap.Modal
2243  * @extends Roo.bootstrap.Component
2244  * Bootstrap Modal class
2245  * @cfg {String} title Title of dialog
2246  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2247  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2248  * @cfg {Boolean} specificTitle default false
2249  * @cfg {Array} buttons Array of buttons or standard button set..
2250  * @cfg {String} buttonPosition (left|right|center) default right
2251  * @cfg {Boolean} animate default true
2252  * @cfg {Boolean} allow_close default true
2253  * 
2254  * @constructor
2255  * Create a new Modal Dialog
2256  * @param {Object} config The config object
2257  */
2258
2259 Roo.bootstrap.Modal = function(config){
2260     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2261     this.addEvents({
2262         // raw events
2263         /**
2264          * @event btnclick
2265          * The raw btnclick event for the button
2266          * @param {Roo.EventObject} e
2267          */
2268         "btnclick" : true
2269     });
2270     this.buttons = this.buttons || [];
2271      
2272     if (this.tmpl) {
2273         this.tmpl = Roo.factory(this.tmpl);
2274     }
2275     
2276 };
2277
2278 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2279     
2280     title : 'test dialog',
2281    
2282     buttons : false,
2283     
2284     // set on load...
2285      
2286     html: false,
2287     
2288     tmp: false,
2289     
2290     specificTitle: false,
2291     
2292     buttonPosition: 'right',
2293     
2294     allow_close : true,
2295     
2296     animate : true,
2297     
2298     
2299      // private
2300     bodyEl:  false,
2301     footerEl:  false,
2302     titleEl:  false,
2303     closeEl:  false,
2304     
2305     
2306     onRender : function(ct, position)
2307     {
2308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2309      
2310         if(!this.el){
2311             var cfg = Roo.apply({},  this.getAutoCreate());
2312             cfg.id = Roo.id();
2313             //if(!cfg.name){
2314             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2315             //}
2316             //if (!cfg.name.length) {
2317             //    delete cfg.name;
2318            // }
2319             if (this.cls) {
2320                 cfg.cls += ' ' + this.cls;
2321             }
2322             if (this.style) {
2323                 cfg.style = this.style;
2324             }
2325             this.el = Roo.get(document.body).createChild(cfg, position);
2326         }
2327         //var type = this.el.dom.type;
2328         
2329         
2330         
2331         
2332         if(this.tabIndex !== undefined){
2333             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2334         }
2335         
2336         
2337         this.bodyEl = this.el.select('.modal-body',true).first();
2338         this.closeEl = this.el.select('.modal-header .close', true).first();
2339         this.footerEl = this.el.select('.modal-footer',true).first();
2340         this.titleEl = this.el.select('.modal-title',true).first();
2341         
2342         
2343          
2344         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2345         this.maskEl.enableDisplayMode("block");
2346         this.maskEl.hide();
2347         //this.el.addClass("x-dlg-modal");
2348     
2349         if (this.buttons.length) {
2350             Roo.each(this.buttons, function(bb) {
2351                 b = Roo.apply({}, bb);
2352                 b.xns = b.xns || Roo.bootstrap;
2353                 b.xtype = b.xtype || 'Button';
2354                 if (typeof(b.listeners) == 'undefined') {
2355                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2356                 }
2357                 
2358                 var btn = Roo.factory(b);
2359                 
2360                 btn.onRender(this.el.select('.modal-footer div').first());
2361                 
2362             },this);
2363         }
2364         // render the children.
2365         var nitems = [];
2366         
2367         if(typeof(this.items) != 'undefined'){
2368             var items = this.items;
2369             delete this.items;
2370
2371             for(var i =0;i < items.length;i++) {
2372                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2373             }
2374         }
2375         
2376         this.items = nitems;
2377         
2378         // where are these used - they used to be body/close/footer
2379         
2380        
2381         this.initEvents();
2382         //this.el.addClass([this.fieldClass, this.cls]);
2383         
2384     },
2385     getAutoCreate : function(){
2386         
2387         
2388         var bdy = {
2389                 cls : 'modal-body',
2390                 html : this.html || ''
2391         };
2392         
2393         var title = {
2394             tag: 'h4',
2395             cls : 'modal-title',
2396             html : this.title
2397         };
2398         
2399         if(this.specificTitle){
2400             title = this.title;
2401             
2402         };
2403         
2404         var header = [];
2405         if (this.allow_close) {
2406             header.push({
2407                 tag: 'button',
2408                 cls : 'close',
2409                 html : '&times'
2410             });
2411         }
2412         header.push(title);
2413         
2414         var modal = {
2415             cls: "modal",
2416             style : 'display: none',
2417             cn : [
2418                 {
2419                     cls: "modal-dialog",
2420                     cn : [
2421                         {
2422                             cls : "modal-content",
2423                             cn : [
2424                                 {
2425                                     cls : 'modal-header',
2426                                     cn : header
2427                                 },
2428                                 bdy,
2429                                 {
2430                                     cls : 'modal-footer',
2431                                     cn : [
2432                                         {
2433                                             tag: 'div',
2434                                             cls: 'btn-' + this.buttonPosition
2435                                         }
2436                                     ]
2437                                     
2438                                 }
2439                                 
2440                                 
2441                             ]
2442                             
2443                         }
2444                     ]
2445                         
2446                 }
2447             ]
2448         };
2449         
2450         if(this.animate){
2451             modal.cls += ' fade';
2452         }
2453         
2454         return modal;
2455           
2456     },
2457     getChildContainer : function() {
2458          
2459          return this.bodyEl;
2460         
2461     },
2462     getButtonContainer : function() {
2463          return this.el.select('.modal-footer div',true).first();
2464         
2465     },
2466     initEvents : function()
2467     {
2468         if (this.allow_close) {
2469             this.closeEl.on('click', this.hide, this);
2470         }
2471
2472     },
2473     show : function() {
2474         
2475         if (!this.rendered) {
2476             this.render();
2477         }
2478         
2479         this.el.setStyle('display', 'block');
2480         
2481         if(this.animate){
2482             var _this = this;
2483             (function(){ _this.el.addClass('in'); }).defer(50);
2484         }else{
2485             this.el.addClass('in');
2486         }
2487         
2488         // not sure how we can show data in here.. 
2489         //if (this.tmpl) {
2490         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2491         //}
2492         
2493         Roo.get(document.body).addClass("x-body-masked");
2494         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2495         this.maskEl.show();
2496         this.el.setStyle('zIndex', '10001');
2497        
2498         this.fireEvent('show', this);
2499         
2500         
2501     },
2502     hide : function()
2503     {
2504         this.maskEl.hide();
2505         Roo.get(document.body).removeClass("x-body-masked");
2506         this.el.removeClass('in');
2507         
2508         if(this.animate){
2509             var _this = this;
2510             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2511         }else{
2512             this.el.setStyle('display', 'none');
2513         }
2514         
2515         this.fireEvent('hide', this);
2516     },
2517     
2518     addButton : function(str, cb)
2519     {
2520          
2521         
2522         var b = Roo.apply({}, { html : str } );
2523         b.xns = b.xns || Roo.bootstrap;
2524         b.xtype = b.xtype || 'Button';
2525         if (typeof(b.listeners) == 'undefined') {
2526             b.listeners = { click : cb.createDelegate(this)  };
2527         }
2528         
2529         var btn = Roo.factory(b);
2530            
2531         btn.onRender(this.el.select('.modal-footer div').first());
2532         
2533         return btn;   
2534        
2535     },
2536     
2537     setDefaultButton : function(btn)
2538     {
2539         //this.el.select('.modal-footer').()
2540     },
2541     resizeTo: function(w,h)
2542     {
2543         // skip..
2544     },
2545     setContentSize  : function(w, h)
2546     {
2547         
2548     },
2549     onButtonClick: function(btn,e)
2550     {
2551         //Roo.log([a,b,c]);
2552         this.fireEvent('btnclick', btn.name, e);
2553     },
2554      /**
2555      * Set the title of the Dialog
2556      * @param {String} str new Title
2557      */
2558     setTitle: function(str) {
2559         this.titleEl.dom.innerHTML = str;    
2560     },
2561     /**
2562      * Set the body of the Dialog
2563      * @param {String} str new Title
2564      */
2565     setBody: function(str) {
2566         this.bodyEl.dom.innerHTML = str;    
2567     },
2568     /**
2569      * Set the body of the Dialog using the template
2570      * @param {Obj} data - apply this data to the template and replace the body contents.
2571      */
2572     applyBody: function(obj)
2573     {
2574         if (!this.tmpl) {
2575             Roo.log("Error - using apply Body without a template");
2576             //code
2577         }
2578         this.tmpl.overwrite(this.bodyEl, obj);
2579     }
2580     
2581 });
2582
2583
2584 Roo.apply(Roo.bootstrap.Modal,  {
2585     /**
2586          * Button config that displays a single OK button
2587          * @type Object
2588          */
2589         OK :  [{
2590             name : 'ok',
2591             weight : 'primary',
2592             html : 'OK'
2593         }], 
2594         /**
2595          * Button config that displays Yes and No buttons
2596          * @type Object
2597          */
2598         YESNO : [
2599             {
2600                 name  : 'no',
2601                 html : 'No'
2602             },
2603             {
2604                 name  :'yes',
2605                 weight : 'primary',
2606                 html : 'Yes'
2607             }
2608         ],
2609         
2610         /**
2611          * Button config that displays OK and Cancel buttons
2612          * @type Object
2613          */
2614         OKCANCEL : [
2615             {
2616                name : 'cancel',
2617                 html : 'Cancel'
2618             },
2619             {
2620                 name : 'ok',
2621                 weight : 'primary',
2622                 html : 'OK'
2623             }
2624         ],
2625         /**
2626          * Button config that displays Yes, No and Cancel buttons
2627          * @type Object
2628          */
2629         YESNOCANCEL : [
2630             {
2631                 name : 'yes',
2632                 weight : 'primary',
2633                 html : 'Yes'
2634             },
2635             {
2636                 name : 'no',
2637                 html : 'No'
2638             },
2639             {
2640                 name : 'cancel',
2641                 html : 'Cancel'
2642             }
2643         ]
2644 });
2645  
2646  /*
2647  * - LGPL
2648  *
2649  * messagebox - can be used as a replace
2650  * 
2651  */
2652 /**
2653  * @class Roo.MessageBox
2654  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2655  * Example usage:
2656  *<pre><code>
2657 // Basic alert:
2658 Roo.Msg.alert('Status', 'Changes saved successfully.');
2659
2660 // Prompt for user data:
2661 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2662     if (btn == 'ok'){
2663         // process text value...
2664     }
2665 });
2666
2667 // Show a dialog using config options:
2668 Roo.Msg.show({
2669    title:'Save Changes?',
2670    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2671    buttons: Roo.Msg.YESNOCANCEL,
2672    fn: processResult,
2673    animEl: 'elId'
2674 });
2675 </code></pre>
2676  * @singleton
2677  */
2678 Roo.bootstrap.MessageBox = function(){
2679     var dlg, opt, mask, waitTimer;
2680     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2681     var buttons, activeTextEl, bwidth;
2682
2683     
2684     // private
2685     var handleButton = function(button){
2686         dlg.hide();
2687         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2688     };
2689
2690     // private
2691     var handleHide = function(){
2692         if(opt && opt.cls){
2693             dlg.el.removeClass(opt.cls);
2694         }
2695         //if(waitTimer){
2696         //    Roo.TaskMgr.stop(waitTimer);
2697         //    waitTimer = null;
2698         //}
2699     };
2700
2701     // private
2702     var updateButtons = function(b){
2703         var width = 0;
2704         if(!b){
2705             buttons["ok"].hide();
2706             buttons["cancel"].hide();
2707             buttons["yes"].hide();
2708             buttons["no"].hide();
2709             //dlg.footer.dom.style.display = 'none';
2710             return width;
2711         }
2712         dlg.footerEl.dom.style.display = '';
2713         for(var k in buttons){
2714             if(typeof buttons[k] != "function"){
2715                 if(b[k]){
2716                     buttons[k].show();
2717                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2718                     width += buttons[k].el.getWidth()+15;
2719                 }else{
2720                     buttons[k].hide();
2721                 }
2722             }
2723         }
2724         return width;
2725     };
2726
2727     // private
2728     var handleEsc = function(d, k, e){
2729         if(opt && opt.closable !== false){
2730             dlg.hide();
2731         }
2732         if(e){
2733             e.stopEvent();
2734         }
2735     };
2736
2737     return {
2738         /**
2739          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2740          * @return {Roo.BasicDialog} The BasicDialog element
2741          */
2742         getDialog : function(){
2743            if(!dlg){
2744                 dlg = new Roo.bootstrap.Modal( {
2745                     //draggable: true,
2746                     //resizable:false,
2747                     //constraintoviewport:false,
2748                     //fixedcenter:true,
2749                     //collapsible : false,
2750                     //shim:true,
2751                     //modal: true,
2752                   //  width:400,
2753                   //  height:100,
2754                     //buttonAlign:"center",
2755                     closeClick : function(){
2756                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2757                             handleButton("no");
2758                         }else{
2759                             handleButton("cancel");
2760                         }
2761                     }
2762                 });
2763                 dlg.render();
2764                 dlg.on("hide", handleHide);
2765                 mask = dlg.mask;
2766                 //dlg.addKeyListener(27, handleEsc);
2767                 buttons = {};
2768                 this.buttons = buttons;
2769                 var bt = this.buttonText;
2770                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2771                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2772                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2773                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2774                 Roo.log(buttons)
2775                 bodyEl = dlg.bodyEl.createChild({
2776
2777                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2778                         '<textarea class="roo-mb-textarea"></textarea>' +
2779                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2780                 });
2781                 msgEl = bodyEl.dom.firstChild;
2782                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2783                 textboxEl.enableDisplayMode();
2784                 textboxEl.addKeyListener([10,13], function(){
2785                     if(dlg.isVisible() && opt && opt.buttons){
2786                         if(opt.buttons.ok){
2787                             handleButton("ok");
2788                         }else if(opt.buttons.yes){
2789                             handleButton("yes");
2790                         }
2791                     }
2792                 });
2793                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2794                 textareaEl.enableDisplayMode();
2795                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2796                 progressEl.enableDisplayMode();
2797                 var pf = progressEl.dom.firstChild;
2798                 if (pf) {
2799                     pp = Roo.get(pf.firstChild);
2800                     pp.setHeight(pf.offsetHeight);
2801                 }
2802                 
2803             }
2804             return dlg;
2805         },
2806
2807         /**
2808          * Updates the message box body text
2809          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2810          * the XHTML-compliant non-breaking space character '&amp;#160;')
2811          * @return {Roo.MessageBox} This message box
2812          */
2813         updateText : function(text){
2814             if(!dlg.isVisible() && !opt.width){
2815                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2816             }
2817             msgEl.innerHTML = text || '&#160;';
2818       
2819             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2820             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2821             var w = Math.max(
2822                     Math.min(opt.width || cw , this.maxWidth), 
2823                     Math.max(opt.minWidth || this.minWidth, bwidth)
2824             );
2825             if(opt.prompt){
2826                 activeTextEl.setWidth(w);
2827             }
2828             if(dlg.isVisible()){
2829                 dlg.fixedcenter = false;
2830             }
2831             // to big, make it scroll. = But as usual stupid IE does not support
2832             // !important..
2833             
2834             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2835                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2836                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2837             } else {
2838                 bodyEl.dom.style.height = '';
2839                 bodyEl.dom.style.overflowY = '';
2840             }
2841             if (cw > w) {
2842                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2843             } else {
2844                 bodyEl.dom.style.overflowX = '';
2845             }
2846             
2847             dlg.setContentSize(w, bodyEl.getHeight());
2848             if(dlg.isVisible()){
2849                 dlg.fixedcenter = true;
2850             }
2851             return this;
2852         },
2853
2854         /**
2855          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2856          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2857          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2858          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2859          * @return {Roo.MessageBox} This message box
2860          */
2861         updateProgress : function(value, text){
2862             if(text){
2863                 this.updateText(text);
2864             }
2865             if (pp) { // weird bug on my firefox - for some reason this is not defined
2866                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2867             }
2868             return this;
2869         },        
2870
2871         /**
2872          * Returns true if the message box is currently displayed
2873          * @return {Boolean} True if the message box is visible, else false
2874          */
2875         isVisible : function(){
2876             return dlg && dlg.isVisible();  
2877         },
2878
2879         /**
2880          * Hides the message box if it is displayed
2881          */
2882         hide : function(){
2883             if(this.isVisible()){
2884                 dlg.hide();
2885             }  
2886         },
2887
2888         /**
2889          * Displays a new message box, or reinitializes an existing message box, based on the config options
2890          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2891          * The following config object properties are supported:
2892          * <pre>
2893 Property    Type             Description
2894 ----------  ---------------  ------------------------------------------------------------------------------------
2895 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2896                                    closes (defaults to undefined)
2897 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2898                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2899 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2900                                    progress and wait dialogs will ignore this property and always hide the
2901                                    close button as they can only be closed programmatically.
2902 cls               String           A custom CSS class to apply to the message box element
2903 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2904                                    displayed (defaults to 75)
2905 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2906                                    function will be btn (the name of the button that was clicked, if applicable,
2907                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2908                                    Progress and wait dialogs will ignore this option since they do not respond to
2909                                    user actions and can only be closed programmatically, so any required function
2910                                    should be called by the same code after it closes the dialog.
2911 icon              String           A CSS class that provides a background image to be used as an icon for
2912                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2913 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2914 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2915 modal             Boolean          False to allow user interaction with the page while the message box is
2916                                    displayed (defaults to true)
2917 msg               String           A string that will replace the existing message box body text (defaults
2918                                    to the XHTML-compliant non-breaking space character '&#160;')
2919 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2920 progress          Boolean          True to display a progress bar (defaults to false)
2921 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2922 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2923 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2924 title             String           The title text
2925 value             String           The string value to set into the active textbox element if displayed
2926 wait              Boolean          True to display a progress bar (defaults to false)
2927 width             Number           The width of the dialog in pixels
2928 </pre>
2929          *
2930          * Example usage:
2931          * <pre><code>
2932 Roo.Msg.show({
2933    title: 'Address',
2934    msg: 'Please enter your address:',
2935    width: 300,
2936    buttons: Roo.MessageBox.OKCANCEL,
2937    multiline: true,
2938    fn: saveAddress,
2939    animEl: 'addAddressBtn'
2940 });
2941 </code></pre>
2942          * @param {Object} config Configuration options
2943          * @return {Roo.MessageBox} This message box
2944          */
2945         show : function(options)
2946         {
2947             
2948             // this causes nightmares if you show one dialog after another
2949             // especially on callbacks..
2950              
2951             if(this.isVisible()){
2952                 
2953                 this.hide();
2954                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2955                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2956                 Roo.log("New Dialog Message:" +  options.msg )
2957                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2958                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2959                 
2960             }
2961             var d = this.getDialog();
2962             opt = options;
2963             d.setTitle(opt.title || "&#160;");
2964             d.closeEl.setDisplayed(opt.closable !== false);
2965             activeTextEl = textboxEl;
2966             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2967             if(opt.prompt){
2968                 if(opt.multiline){
2969                     textboxEl.hide();
2970                     textareaEl.show();
2971                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2972                         opt.multiline : this.defaultTextHeight);
2973                     activeTextEl = textareaEl;
2974                 }else{
2975                     textboxEl.show();
2976                     textareaEl.hide();
2977                 }
2978             }else{
2979                 textboxEl.hide();
2980                 textareaEl.hide();
2981             }
2982             progressEl.setDisplayed(opt.progress === true);
2983             this.updateProgress(0);
2984             activeTextEl.dom.value = opt.value || "";
2985             if(opt.prompt){
2986                 dlg.setDefaultButton(activeTextEl);
2987             }else{
2988                 var bs = opt.buttons;
2989                 var db = null;
2990                 if(bs && bs.ok){
2991                     db = buttons["ok"];
2992                 }else if(bs && bs.yes){
2993                     db = buttons["yes"];
2994                 }
2995                 dlg.setDefaultButton(db);
2996             }
2997             bwidth = updateButtons(opt.buttons);
2998             this.updateText(opt.msg);
2999             if(opt.cls){
3000                 d.el.addClass(opt.cls);
3001             }
3002             d.proxyDrag = opt.proxyDrag === true;
3003             d.modal = opt.modal !== false;
3004             d.mask = opt.modal !== false ? mask : false;
3005             if(!d.isVisible()){
3006                 // force it to the end of the z-index stack so it gets a cursor in FF
3007                 document.body.appendChild(dlg.el.dom);
3008                 d.animateTarget = null;
3009                 d.show(options.animEl);
3010             }
3011             return this;
3012         },
3013
3014         /**
3015          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3016          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3017          * and closing the message box when the process is complete.
3018          * @param {String} title The title bar text
3019          * @param {String} msg The message box body text
3020          * @return {Roo.MessageBox} This message box
3021          */
3022         progress : function(title, msg){
3023             this.show({
3024                 title : title,
3025                 msg : msg,
3026                 buttons: false,
3027                 progress:true,
3028                 closable:false,
3029                 minWidth: this.minProgressWidth,
3030                 modal : true
3031             });
3032             return this;
3033         },
3034
3035         /**
3036          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3037          * If a callback function is passed it will be called after the user clicks the button, and the
3038          * id of the button that was clicked will be passed as the only parameter to the callback
3039          * (could also be the top-right close button).
3040          * @param {String} title The title bar text
3041          * @param {String} msg The message box body text
3042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3043          * @param {Object} scope (optional) The scope of the callback function
3044          * @return {Roo.MessageBox} This message box
3045          */
3046         alert : function(title, msg, fn, scope){
3047             this.show({
3048                 title : title,
3049                 msg : msg,
3050                 buttons: this.OK,
3051                 fn: fn,
3052                 scope : scope,
3053                 modal : true
3054             });
3055             return this;
3056         },
3057
3058         /**
3059          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3060          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3061          * You are responsible for closing the message box when the process is complete.
3062          * @param {String} msg The message box body text
3063          * @param {String} title (optional) The title bar text
3064          * @return {Roo.MessageBox} This message box
3065          */
3066         wait : function(msg, title){
3067             this.show({
3068                 title : title,
3069                 msg : msg,
3070                 buttons: false,
3071                 closable:false,
3072                 progress:true,
3073                 modal:true,
3074                 width:300,
3075                 wait:true
3076             });
3077             waitTimer = Roo.TaskMgr.start({
3078                 run: function(i){
3079                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3080                 },
3081                 interval: 1000
3082             });
3083             return this;
3084         },
3085
3086         /**
3087          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3088          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3089          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3090          * @param {String} title The title bar text
3091          * @param {String} msg The message box body text
3092          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3093          * @param {Object} scope (optional) The scope of the callback function
3094          * @return {Roo.MessageBox} This message box
3095          */
3096         confirm : function(title, msg, fn, scope){
3097             this.show({
3098                 title : title,
3099                 msg : msg,
3100                 buttons: this.YESNO,
3101                 fn: fn,
3102                 scope : scope,
3103                 modal : true
3104             });
3105             return this;
3106         },
3107
3108         /**
3109          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3110          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3111          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3112          * (could also be the top-right close button) and the text that was entered will be passed as the two
3113          * parameters to the callback.
3114          * @param {String} title The title bar text
3115          * @param {String} msg The message box body text
3116          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3117          * @param {Object} scope (optional) The scope of the callback function
3118          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3119          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3120          * @return {Roo.MessageBox} This message box
3121          */
3122         prompt : function(title, msg, fn, scope, multiline){
3123             this.show({
3124                 title : title,
3125                 msg : msg,
3126                 buttons: this.OKCANCEL,
3127                 fn: fn,
3128                 minWidth:250,
3129                 scope : scope,
3130                 prompt:true,
3131                 multiline: multiline,
3132                 modal : true
3133             });
3134             return this;
3135         },
3136
3137         /**
3138          * Button config that displays a single OK button
3139          * @type Object
3140          */
3141         OK : {ok:true},
3142         /**
3143          * Button config that displays Yes and No buttons
3144          * @type Object
3145          */
3146         YESNO : {yes:true, no:true},
3147         /**
3148          * Button config that displays OK and Cancel buttons
3149          * @type Object
3150          */
3151         OKCANCEL : {ok:true, cancel:true},
3152         /**
3153          * Button config that displays Yes, No and Cancel buttons
3154          * @type Object
3155          */
3156         YESNOCANCEL : {yes:true, no:true, cancel:true},
3157
3158         /**
3159          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3160          * @type Number
3161          */
3162         defaultTextHeight : 75,
3163         /**
3164          * The maximum width in pixels of the message box (defaults to 600)
3165          * @type Number
3166          */
3167         maxWidth : 600,
3168         /**
3169          * The minimum width in pixels of the message box (defaults to 100)
3170          * @type Number
3171          */
3172         minWidth : 100,
3173         /**
3174          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3175          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3176          * @type Number
3177          */
3178         minProgressWidth : 250,
3179         /**
3180          * An object containing the default button text strings that can be overriden for localized language support.
3181          * Supported properties are: ok, cancel, yes and no.
3182          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3183          * @type Object
3184          */
3185         buttonText : {
3186             ok : "OK",
3187             cancel : "Cancel",
3188             yes : "Yes",
3189             no : "No"
3190         }
3191     };
3192 }();
3193
3194 /**
3195  * Shorthand for {@link Roo.MessageBox}
3196  */
3197 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3198 Roo.Msg = Roo.Msg || Roo.MessageBox;
3199 /*
3200  * - LGPL
3201  *
3202  * navbar
3203  * 
3204  */
3205
3206 /**
3207  * @class Roo.bootstrap.Navbar
3208  * @extends Roo.bootstrap.Component
3209  * Bootstrap Navbar class
3210
3211  * @constructor
3212  * Create a new Navbar
3213  * @param {Object} config The config object
3214  */
3215
3216
3217 Roo.bootstrap.Navbar = function(config){
3218     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3219     
3220 };
3221
3222 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3223     
3224     
3225    
3226     // private
3227     navItems : false,
3228     loadMask : false,
3229     
3230     
3231     getAutoCreate : function(){
3232         
3233         
3234         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3235         
3236     },
3237     
3238     initEvents :function ()
3239     {
3240         //Roo.log(this.el.select('.navbar-toggle',true));
3241         this.el.select('.navbar-toggle',true).on('click', function() {
3242            // Roo.log('click');
3243             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3244         }, this);
3245         
3246         var mark = {
3247             tag: "div",
3248             cls:"x-dlg-mask"
3249         }
3250         
3251         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3252         
3253         var size = this.el.getSize();
3254         this.maskEl.setSize(size.width, size.height);
3255         this.maskEl.enableDisplayMode("block");
3256         this.maskEl.hide();
3257         
3258         if(this.loadMask){
3259             this.maskEl.show();
3260         }
3261     },
3262     
3263     
3264     getChildContainer : function()
3265     {
3266         if (this.el.select('.collapse').getCount()) {
3267             return this.el.select('.collapse',true).first();
3268         }
3269         
3270         return this.el;
3271     },
3272     
3273     mask : function()
3274     {
3275         this.maskEl.show();
3276     },
3277     
3278     unmask : function()
3279     {
3280         this.maskEl.hide();
3281     } 
3282     
3283     
3284     
3285     
3286 });
3287
3288
3289
3290  
3291
3292  /*
3293  * - LGPL
3294  *
3295  * navbar
3296  * 
3297  */
3298
3299 /**
3300  * @class Roo.bootstrap.NavSimplebar
3301  * @extends Roo.bootstrap.Navbar
3302  * Bootstrap Sidebar class
3303  *
3304  * @cfg {Boolean} inverse is inverted color
3305  * 
3306  * @cfg {String} type (nav | pills | tabs)
3307  * @cfg {Boolean} arrangement stacked | justified
3308  * @cfg {String} align (left | right) alignment
3309  * 
3310  * @cfg {Boolean} main (true|false) main nav bar? default false
3311  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3312  * 
3313  * @cfg {String} tag (header|footer|nav|div) default is nav 
3314
3315  * 
3316  * 
3317  * 
3318  * @constructor
3319  * Create a new Sidebar
3320  * @param {Object} config The config object
3321  */
3322
3323
3324 Roo.bootstrap.NavSimplebar = function(config){
3325     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3326 };
3327
3328 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3329     
3330     inverse: false,
3331     
3332     type: false,
3333     arrangement: '',
3334     align : false,
3335     
3336     
3337     
3338     main : false,
3339     
3340     
3341     tag : false,
3342     
3343     
3344     getAutoCreate : function(){
3345         
3346         
3347         var cfg = {
3348             tag : this.tag || 'div',
3349             cls : 'navbar'
3350         };
3351           
3352         
3353         cfg.cn = [
3354             {
3355                 cls: 'nav',
3356                 tag : 'ul'
3357             }
3358         ];
3359         
3360          
3361         this.type = this.type || 'nav';
3362         if (['tabs','pills'].indexOf(this.type)!==-1) {
3363             cfg.cn[0].cls += ' nav-' + this.type
3364         
3365         
3366         } else {
3367             if (this.type!=='nav') {
3368                 Roo.log('nav type must be nav/tabs/pills')
3369             }
3370             cfg.cn[0].cls += ' navbar-nav'
3371         }
3372         
3373         
3374         
3375         
3376         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3377             cfg.cn[0].cls += ' nav-' + this.arrangement;
3378         }
3379         
3380         
3381         if (this.align === 'right') {
3382             cfg.cn[0].cls += ' navbar-right';
3383         }
3384         
3385         if (this.inverse) {
3386             cfg.cls += ' navbar-inverse';
3387             
3388         }
3389         
3390         
3391         return cfg;
3392     
3393         
3394     }
3395     
3396     
3397     
3398 });
3399
3400
3401
3402  
3403
3404  
3405        /*
3406  * - LGPL
3407  *
3408  * navbar
3409  * 
3410  */
3411
3412 /**
3413  * @class Roo.bootstrap.NavHeaderbar
3414  * @extends Roo.bootstrap.NavSimplebar
3415  * Bootstrap Sidebar class
3416  *
3417  * @cfg {String} brand what is brand
3418  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3419  * @cfg {String} brand_href href of the brand
3420  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3421  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3422  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3423  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3424  * 
3425  * @constructor
3426  * Create a new Sidebar
3427  * @param {Object} config The config object
3428  */
3429
3430
3431 Roo.bootstrap.NavHeaderbar = function(config){
3432     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3433       
3434 };
3435
3436 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3437     
3438     position: '',
3439     brand: '',
3440     brand_href: false,
3441     srButton : true,
3442     autohide : false,
3443     desktopCenter : false,
3444    
3445     
3446     getAutoCreate : function(){
3447         
3448         var   cfg = {
3449             tag: this.nav || 'nav',
3450             cls: 'navbar',
3451             role: 'navigation',
3452             cn: []
3453         };
3454         
3455         var cn = cfg.cn;
3456         if (this.desktopCenter) {
3457             cn.push({cls : 'container', cn : []});
3458             cn = cn[0].cn;
3459         }
3460         
3461         if(this.srButton){
3462             cn.push({
3463                 tag: 'div',
3464                 cls: 'navbar-header',
3465                 cn: [
3466                     {
3467                         tag: 'button',
3468                         type: 'button',
3469                         cls: 'navbar-toggle',
3470                         'data-toggle': 'collapse',
3471                         cn: [
3472                             {
3473                                 tag: 'span',
3474                                 cls: 'sr-only',
3475                                 html: 'Toggle navigation'
3476                             },
3477                             {
3478                                 tag: 'span',
3479                                 cls: 'icon-bar'
3480                             },
3481                             {
3482                                 tag: 'span',
3483                                 cls: 'icon-bar'
3484                             },
3485                             {
3486                                 tag: 'span',
3487                                 cls: 'icon-bar'
3488                             }
3489                         ]
3490                     }
3491                 ]
3492             });
3493         }
3494         
3495         cn.push({
3496             tag: 'div',
3497             cls: 'collapse navbar-collapse',
3498             cn : []
3499         });
3500         
3501         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3502         
3503         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3504             cfg.cls += ' navbar-' + this.position;
3505             
3506             // tag can override this..
3507             
3508             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3509         }
3510         
3511         if (this.brand !== '') {
3512             cn[0].cn.push({
3513                 tag: 'a',
3514                 href: this.brand_href ? this.brand_href : '#',
3515                 cls: 'navbar-brand',
3516                 cn: [
3517                 this.brand
3518                 ]
3519             });
3520         }
3521         
3522         if(this.main){
3523             cfg.cls += ' main-nav';
3524         }
3525         
3526         
3527         return cfg;
3528
3529         
3530     },
3531     getHeaderChildContainer : function()
3532     {
3533         if (this.el.select('.navbar-header').getCount()) {
3534             return this.el.select('.navbar-header',true).first();
3535         }
3536         
3537         return this.getChildContainer();
3538     },
3539     
3540     
3541     initEvents : function()
3542     {
3543         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3544         
3545         if (this.autohide) {
3546             
3547             var prevScroll = 0;
3548             var ft = this.el;
3549             
3550             Roo.get(document).on('scroll',function(e) {
3551                 var ns = Roo.get(document).getScroll().top;
3552                 var os = prevScroll;
3553                 prevScroll = ns;
3554                 
3555                 if(ns > os){
3556                     ft.removeClass('slideDown');
3557                     ft.addClass('slideUp');
3558                     return;
3559                 }
3560                 ft.removeClass('slideUp');
3561                 ft.addClass('slideDown');
3562                  
3563               
3564           },this);
3565         }
3566     }    
3567     
3568 });
3569
3570
3571
3572  
3573
3574  /*
3575  * - LGPL
3576  *
3577  * navbar
3578  * 
3579  */
3580
3581 /**
3582  * @class Roo.bootstrap.NavSidebar
3583  * @extends Roo.bootstrap.Navbar
3584  * Bootstrap Sidebar class
3585  * 
3586  * @constructor
3587  * Create a new Sidebar
3588  * @param {Object} config The config object
3589  */
3590
3591
3592 Roo.bootstrap.NavSidebar = function(config){
3593     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3594 };
3595
3596 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3597     
3598     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3599     
3600     getAutoCreate : function(){
3601         
3602         
3603         return  {
3604             tag: 'div',
3605             cls: 'sidebar sidebar-nav'
3606         };
3607     
3608         
3609     }
3610     
3611     
3612     
3613 });
3614
3615
3616
3617  
3618
3619  /*
3620  * - LGPL
3621  *
3622  * nav group
3623  * 
3624  */
3625
3626 /**
3627  * @class Roo.bootstrap.NavGroup
3628  * @extends Roo.bootstrap.Component
3629  * Bootstrap NavGroup class
3630  * @cfg {String} align left | right
3631  * @cfg {Boolean} inverse false | true
3632  * @cfg {String} type (nav|pills|tab) default nav
3633  * @cfg {String} navId - reference Id for navbar.
3634
3635  * 
3636  * @constructor
3637  * Create a new nav group
3638  * @param {Object} config The config object
3639  */
3640
3641 Roo.bootstrap.NavGroup = function(config){
3642     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3643     this.navItems = [];
3644    
3645     Roo.bootstrap.NavGroup.register(this);
3646      this.addEvents({
3647         /**
3648              * @event changed
3649              * Fires when the active item changes
3650              * @param {Roo.bootstrap.NavGroup} this
3651              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3652              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3653          */
3654         'changed': true
3655      });
3656     
3657 };
3658
3659 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3660     
3661     align: '',
3662     inverse: false,
3663     form: false,
3664     type: 'nav',
3665     navId : '',
3666     // private
3667     
3668     navItems : false, 
3669     
3670     getAutoCreate : function()
3671     {
3672         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3673         
3674         cfg = {
3675             tag : 'ul',
3676             cls: 'nav' 
3677         }
3678         
3679         if (['tabs','pills'].indexOf(this.type)!==-1) {
3680             cfg.cls += ' nav-' + this.type
3681         } else {
3682             if (this.type!=='nav') {
3683                 Roo.log('nav type must be nav/tabs/pills')
3684             }
3685             cfg.cls += ' navbar-nav'
3686         }
3687         
3688         if (this.parent().sidebar) {
3689             cfg = {
3690                 tag: 'ul',
3691                 cls: 'dashboard-menu sidebar-menu'
3692             }
3693             
3694             return cfg;
3695         }
3696         
3697         if (this.form === true) {
3698             cfg = {
3699                 tag: 'form',
3700                 cls: 'navbar-form'
3701             }
3702             
3703             if (this.align === 'right') {
3704                 cfg.cls += ' navbar-right';
3705             } else {
3706                 cfg.cls += ' navbar-left';
3707             }
3708         }
3709         
3710         if (this.align === 'right') {
3711             cfg.cls += ' navbar-right';
3712         }
3713         
3714         if (this.inverse) {
3715             cfg.cls += ' navbar-inverse';
3716             
3717         }
3718         
3719         
3720         return cfg;
3721     },
3722     /**
3723     * sets the active Navigation item
3724     * @param {Roo.bootstrap.NavItem} the new current navitem
3725     */
3726     setActiveItem : function(item)
3727     {
3728         var prev = false;
3729         Roo.each(this.navItems, function(v){
3730             if (v == item) {
3731                 return ;
3732             }
3733             if (v.isActive()) {
3734                 v.setActive(false, true);
3735                 prev = v;
3736                 
3737             }
3738             
3739         });
3740
3741         item.setActive(true, true);
3742         this.fireEvent('changed', this, item, prev);
3743         
3744         
3745     },
3746     /**
3747     * gets the active Navigation item
3748     * @return {Roo.bootstrap.NavItem} the current navitem
3749     */
3750     getActive : function()
3751     {
3752         
3753         var prev = false;
3754         Roo.each(this.navItems, function(v){
3755             
3756             if (v.isActive()) {
3757                 prev = v;
3758                 
3759             }
3760             
3761         });
3762         return prev;
3763     },
3764     
3765     indexOfNav : function()
3766     {
3767         
3768         var prev = false;
3769         Roo.each(this.navItems, function(v,i){
3770             
3771             if (v.isActive()) {
3772                 prev = i;
3773                 
3774             }
3775             
3776         });
3777         return prev;
3778     },
3779     /**
3780     * adds a Navigation item
3781     * @param {Roo.bootstrap.NavItem} the navitem to add
3782     */
3783     addItem : function(cfg)
3784     {
3785         var cn = new Roo.bootstrap.NavItem(cfg);
3786         this.register(cn);
3787         cn.parentId = this.id;
3788         cn.onRender(this.el, null);
3789         return cn;
3790     },
3791     /**
3792     * register a Navigation item
3793     * @param {Roo.bootstrap.NavItem} the navitem to add
3794     */
3795     register : function(item)
3796     {
3797         this.navItems.push( item);
3798         item.navId = this.navId;
3799     
3800     },
3801     
3802     /**
3803     * clear all the Navigation item
3804     */
3805    
3806     clearAll : function()
3807     {
3808         this.navItems = [];
3809         this.el.dom.innerHTML = '';
3810     },
3811     
3812     getNavItem: function(tabId)
3813     {
3814         var ret = false;
3815         Roo.each(this.navItems, function(e) {
3816             if (e.tabId == tabId) {
3817                ret =  e;
3818                return false;
3819             }
3820             return true;
3821             
3822         });
3823         return ret;
3824     },
3825     
3826     setActiveNext : function()
3827     {
3828         var i = this.indexOfNav(this.getActive());
3829         if (i > this.navItems.length) {
3830             return;
3831         }
3832         this.setActiveItem(this.navItems[i+1]);
3833     },
3834     setActivePrev : function()
3835     {
3836         var i = this.indexOfNav(this.getActive());
3837         if (i  < 1) {
3838             return;
3839         }
3840         this.setActiveItem(this.navItems[i-1]);
3841     },
3842     clearWasActive : function(except) {
3843         Roo.each(this.navItems, function(e) {
3844             if (e.tabId != except.tabId && e.was_active) {
3845                e.was_active = false;
3846                return false;
3847             }
3848             return true;
3849             
3850         });
3851     },
3852     getWasActive : function ()
3853     {
3854         var r = false;
3855         Roo.each(this.navItems, function(e) {
3856             if (e.was_active) {
3857                r = e;
3858                return false;
3859             }
3860             return true;
3861             
3862         });
3863         return r;
3864     }
3865     
3866     
3867 });
3868
3869  
3870 Roo.apply(Roo.bootstrap.NavGroup, {
3871     
3872     groups: {},
3873      /**
3874     * register a Navigation Group
3875     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3876     */
3877     register : function(navgrp)
3878     {
3879         this.groups[navgrp.navId] = navgrp;
3880         
3881     },
3882     /**
3883     * fetch a Navigation Group based on the navigation ID
3884     * @param {string} the navgroup to add
3885     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3886     */
3887     get: function(navId) {
3888         if (typeof(this.groups[navId]) == 'undefined') {
3889             return false;
3890             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3891         }
3892         return this.groups[navId] ;
3893     }
3894     
3895     
3896     
3897 });
3898
3899  /*
3900  * - LGPL
3901  *
3902  * row
3903  * 
3904  */
3905
3906 /**
3907  * @class Roo.bootstrap.NavItem
3908  * @extends Roo.bootstrap.Component
3909  * Bootstrap Navbar.NavItem class
3910  * @cfg {String} href  link to
3911  * @cfg {String} html content of button
3912  * @cfg {String} badge text inside badge
3913  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3914  * @cfg {String} glyphicon name of glyphicon
3915  * @cfg {String} icon name of font awesome icon
3916  * @cfg {Boolean} active Is item active
3917  * @cfg {Boolean} disabled Is item disabled
3918  
3919  * @cfg {Boolean} preventDefault (true | false) default false
3920  * @cfg {String} tabId the tab that this item activates.
3921  * @cfg {String} tagtype (a|span) render as a href or span?
3922  * @cfg {Boolean} animateRef (true|false) link to element default false  
3923   
3924  * @constructor
3925  * Create a new Navbar Item
3926  * @param {Object} config The config object
3927  */
3928 Roo.bootstrap.NavItem = function(config){
3929     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3930     this.addEvents({
3931         // raw events
3932         /**
3933          * @event click
3934          * The raw click event for the entire grid.
3935          * @param {Roo.EventObject} e
3936          */
3937         "click" : true,
3938          /**
3939             * @event changed
3940             * Fires when the active item active state changes
3941             * @param {Roo.bootstrap.NavItem} this
3942             * @param {boolean} state the new state
3943              
3944          */
3945         'changed': true,
3946         /**
3947             * @event scrollto
3948             * Fires when scroll to element
3949             * @param {Roo.bootstrap.NavItem} this
3950             * @param {Object} options
3951             * @param {Roo.EventObject} e
3952              
3953          */
3954         'scrollto': true
3955     });
3956    
3957 };
3958
3959 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3960     
3961     href: false,
3962     html: '',
3963     badge: '',
3964     icon: false,
3965     glyphicon: false,
3966     active: false,
3967     preventDefault : false,
3968     tabId : false,
3969     tagtype : 'a',
3970     disabled : false,
3971     animateRef : false,
3972     was_active : false,
3973     
3974     getAutoCreate : function(){
3975          
3976         var cfg = {
3977             tag: 'li',
3978             cls: 'nav-item'
3979             
3980         }
3981         if (this.active) {
3982             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3983         }
3984         if (this.disabled) {
3985             cfg.cls += ' disabled';
3986         }
3987         
3988         if (this.href || this.html || this.glyphicon || this.icon) {
3989             cfg.cn = [
3990                 {
3991                     tag: this.tagtype,
3992                     href : this.href || "#",
3993                     html: this.html || ''
3994                 }
3995             ];
3996             
3997             if (this.icon) {
3998                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3999             }
4000
4001             if(this.glyphicon) {
4002                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4003             }
4004             
4005             if (this.menu) {
4006                 
4007                 cfg.cn[0].html += " <span class='caret'></span>";
4008              
4009             }
4010             
4011             if (this.badge !== '') {
4012                  
4013                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4014             }
4015         }
4016         
4017         
4018         
4019         return cfg;
4020     },
4021     initEvents: function() 
4022     {
4023         if (typeof (this.menu) != 'undefined') {
4024             this.menu.parentType = this.xtype;
4025             this.menu.triggerEl = this.el;
4026             this.menu = this.addxtype(Roo.apply({}, this.menu));
4027         }
4028         
4029         this.el.select('a',true).on('click', this.onClick, this);
4030         
4031         if(this.tagtype == 'span'){
4032             this.el.select('span',true).on('click', this.onClick, this);
4033         }
4034        
4035         // at this point parent should be available..
4036         this.parent().register(this);
4037     },
4038     
4039     onClick : function(e)
4040     {
4041         if(
4042                 this.preventDefault || 
4043                 this.href == '#' 
4044         ){
4045             
4046             e.preventDefault();
4047         }
4048         
4049         if (this.disabled) {
4050             return;
4051         }
4052         
4053         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4054         if (tg && tg.transition) {
4055             Roo.log("waiting for the transitionend");
4056             return;
4057         }
4058         
4059         
4060         
4061         //Roo.log("fire event clicked");
4062         if(this.fireEvent('click', this, e) === false){
4063             return;
4064         };
4065         
4066         if(this.tagtype == 'span'){
4067             return;
4068         }
4069         
4070         //Roo.log(this.href);
4071         var ael = this.el.select('a',true).first();
4072         //Roo.log(ael);
4073         
4074         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4075             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4076             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4077                 return; // ignore... - it's a 'hash' to another page.
4078             }
4079             
4080             e.preventDefault();
4081             this.scrollToElement(e);
4082         }
4083         
4084         
4085         var p =  this.parent();
4086    
4087         if (['tabs','pills'].indexOf(p.type)!==-1) {
4088             if (typeof(p.setActiveItem) !== 'undefined') {
4089                 p.setActiveItem(this);
4090             }
4091         }
4092         
4093         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4094         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4095             // remove the collapsed menu expand...
4096             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4097         }
4098     },
4099     
4100     isActive: function () {
4101         return this.active
4102     },
4103     setActive : function(state, fire, is_was_active)
4104     {
4105         if (this.active && !state & this.navId) {
4106             this.was_active = true;
4107             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4108             if (nv) {
4109                 nv.clearWasActive(this);
4110             }
4111             
4112         }
4113         this.active = state;
4114         
4115         if (!state ) {
4116             this.el.removeClass('active');
4117         } else if (!this.el.hasClass('active')) {
4118             this.el.addClass('active');
4119         }
4120         if (fire) {
4121             this.fireEvent('changed', this, state);
4122         }
4123         
4124         // show a panel if it's registered and related..
4125         
4126         if (!this.navId || !this.tabId || !state || is_was_active) {
4127             return;
4128         }
4129         
4130         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4131         if (!tg) {
4132             return;
4133         }
4134         var pan = tg.getPanelByName(this.tabId);
4135         if (!pan) {
4136             return;
4137         }
4138         // if we can not flip to new panel - go back to old nav highlight..
4139         if (false == tg.showPanel(pan)) {
4140             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4141             if (nv) {
4142                 var onav = nv.getWasActive();
4143                 if (onav) {
4144                     onav.setActive(true, false, true);
4145                 }
4146             }
4147             
4148         }
4149         
4150         
4151         
4152     },
4153      // this should not be here...
4154     setDisabled : function(state)
4155     {
4156         this.disabled = state;
4157         if (!state ) {
4158             this.el.removeClass('disabled');
4159         } else if (!this.el.hasClass('disabled')) {
4160             this.el.addClass('disabled');
4161         }
4162         
4163     },
4164     
4165     /**
4166      * Fetch the element to display the tooltip on.
4167      * @return {Roo.Element} defaults to this.el
4168      */
4169     tooltipEl : function()
4170     {
4171         return this.el.select('' + this.tagtype + '', true).first();
4172     },
4173     
4174     scrollToElement : function(e)
4175     {
4176         var c = document.body;
4177         
4178         /*
4179          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4180          */
4181         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4182             c = document.documentElement;
4183         }
4184         
4185         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4186         
4187         if(!target){
4188             return;
4189         }
4190
4191         var o = target.calcOffsetsTo(c);
4192         
4193         var options = {
4194             target : target,
4195             value : o[1]
4196         }
4197         
4198         this.fireEvent('scrollto', this, options, e);
4199         
4200         Roo.get(c).scrollTo('top', options.value, true);
4201         
4202         return;
4203     }
4204 });
4205  
4206
4207  /*
4208  * - LGPL
4209  *
4210  * sidebar item
4211  *
4212  *  li
4213  *    <span> icon </span>
4214  *    <span> text </span>
4215  *    <span>badge </span>
4216  */
4217
4218 /**
4219  * @class Roo.bootstrap.NavSidebarItem
4220  * @extends Roo.bootstrap.NavItem
4221  * Bootstrap Navbar.NavSidebarItem class
4222  * @constructor
4223  * Create a new Navbar Button
4224  * @param {Object} config The config object
4225  */
4226 Roo.bootstrap.NavSidebarItem = function(config){
4227     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4228     this.addEvents({
4229         // raw events
4230         /**
4231          * @event click
4232          * The raw click event for the entire grid.
4233          * @param {Roo.EventObject} e
4234          */
4235         "click" : true,
4236          /**
4237             * @event changed
4238             * Fires when the active item active state changes
4239             * @param {Roo.bootstrap.NavSidebarItem} this
4240             * @param {boolean} state the new state
4241              
4242          */
4243         'changed': true
4244     });
4245    
4246 };
4247
4248 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4249     
4250     
4251     getAutoCreate : function(){
4252         
4253         
4254         var a = {
4255                 tag: 'a',
4256                 href : this.href || '#',
4257                 cls: '',
4258                 html : '',
4259                 cn : []
4260         };
4261         var cfg = {
4262             tag: 'li',
4263             cls: '',
4264             cn: [ a ]
4265         }
4266         var span = {
4267             tag: 'span',
4268             html : this.html || ''
4269         }
4270         
4271         
4272         if (this.active) {
4273             cfg.cls += ' active';
4274         }
4275         
4276         // left icon..
4277         if (this.glyphicon || this.icon) {
4278             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4279             a.cn.push({ tag : 'i', cls : c }) ;
4280         }
4281         // html..
4282         a.cn.push(span);
4283         // then badge..
4284         if (this.badge !== '') {
4285             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4286         }
4287         // fi
4288         if (this.menu) {
4289             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4290             a.cls += 'dropdown-toggle treeview' ;
4291             
4292         }
4293         
4294         
4295         
4296         return cfg;
4297          
4298            
4299     }
4300    
4301      
4302  
4303 });
4304  
4305
4306  /*
4307  * - LGPL
4308  *
4309  * row
4310  * 
4311  */
4312
4313 /**
4314  * @class Roo.bootstrap.Row
4315  * @extends Roo.bootstrap.Component
4316  * Bootstrap Row class (contains columns...)
4317  * 
4318  * @constructor
4319  * Create a new Row
4320  * @param {Object} config The config object
4321  */
4322
4323 Roo.bootstrap.Row = function(config){
4324     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4325 };
4326
4327 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4328     
4329     getAutoCreate : function(){
4330        return {
4331             cls: 'row clearfix'
4332        };
4333     }
4334     
4335     
4336 });
4337
4338  
4339
4340  /*
4341  * - LGPL
4342  *
4343  * element
4344  * 
4345  */
4346
4347 /**
4348  * @class Roo.bootstrap.Element
4349  * @extends Roo.bootstrap.Component
4350  * Bootstrap Element class
4351  * @cfg {String} html contents of the element
4352  * @cfg {String} tag tag of the element
4353  * @cfg {String} cls class of the element
4354  * @cfg {Boolean} preventDefault (true|false) default false
4355  * @cfg {Boolean} clickable (true|false) default false
4356  * 
4357  * @constructor
4358  * Create a new Element
4359  * @param {Object} config The config object
4360  */
4361
4362 Roo.bootstrap.Element = function(config){
4363     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4364     
4365     this.addEvents({
4366         // raw events
4367         /**
4368          * @event click
4369          * When a element is chick
4370          * @param {Roo.bootstrap.Element} this
4371          * @param {Roo.EventObject} e
4372          */
4373         "click" : true
4374     });
4375 };
4376
4377 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4378     
4379     tag: 'div',
4380     cls: '',
4381     html: '',
4382     preventDefault: false, 
4383     clickable: false,
4384     
4385     getAutoCreate : function(){
4386         
4387         var cfg = {
4388             tag: this.tag,
4389             cls: this.cls,
4390             html: this.html
4391         }
4392         
4393         return cfg;
4394     },
4395     
4396     initEvents: function() 
4397     {
4398         Roo.bootstrap.Element.superclass.initEvents.call(this);
4399         
4400         if(this.clickable){
4401             this.el.on('click', this.onClick, this);
4402         }
4403         
4404     },
4405     
4406     onClick : function(e)
4407     {
4408         if(this.preventDefault){
4409             e.preventDefault();
4410         }
4411         
4412         this.fireEvent('click', this, e);
4413     },
4414     
4415     getValue : function()
4416     {
4417         return this.el.dom.innerHTML;
4418     },
4419     
4420     setValue : function(value)
4421     {
4422         this.el.dom.innerHTML = value;
4423     }
4424    
4425 });
4426
4427  
4428
4429  /*
4430  * - LGPL
4431  *
4432  * pagination
4433  * 
4434  */
4435
4436 /**
4437  * @class Roo.bootstrap.Pagination
4438  * @extends Roo.bootstrap.Component
4439  * Bootstrap Pagination class
4440  * @cfg {String} size xs | sm | md | lg
4441  * @cfg {Boolean} inverse false | true
4442  * 
4443  * @constructor
4444  * Create a new Pagination
4445  * @param {Object} config The config object
4446  */
4447
4448 Roo.bootstrap.Pagination = function(config){
4449     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4450 };
4451
4452 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4453     
4454     cls: false,
4455     size: false,
4456     inverse: false,
4457     
4458     getAutoCreate : function(){
4459         var cfg = {
4460             tag: 'ul',
4461                 cls: 'pagination'
4462         };
4463         if (this.inverse) {
4464             cfg.cls += ' inverse';
4465         }
4466         if (this.html) {
4467             cfg.html=this.html;
4468         }
4469         if (this.cls) {
4470             cfg.cls += " " + this.cls;
4471         }
4472         return cfg;
4473     }
4474    
4475 });
4476
4477  
4478
4479  /*
4480  * - LGPL
4481  *
4482  * Pagination item
4483  * 
4484  */
4485
4486
4487 /**
4488  * @class Roo.bootstrap.PaginationItem
4489  * @extends Roo.bootstrap.Component
4490  * Bootstrap PaginationItem class
4491  * @cfg {String} html text
4492  * @cfg {String} href the link
4493  * @cfg {Boolean} preventDefault (true | false) default true
4494  * @cfg {Boolean} active (true | false) default false
4495  * @cfg {Boolean} disabled default false
4496  * 
4497  * 
4498  * @constructor
4499  * Create a new PaginationItem
4500  * @param {Object} config The config object
4501  */
4502
4503
4504 Roo.bootstrap.PaginationItem = function(config){
4505     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4506     this.addEvents({
4507         // raw events
4508         /**
4509          * @event click
4510          * The raw click event for the entire grid.
4511          * @param {Roo.EventObject} e
4512          */
4513         "click" : true
4514     });
4515 };
4516
4517 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4518     
4519     href : false,
4520     html : false,
4521     preventDefault: true,
4522     active : false,
4523     cls : false,
4524     disabled: false,
4525     
4526     getAutoCreate : function(){
4527         var cfg= {
4528             tag: 'li',
4529             cn: [
4530                 {
4531                     tag : 'a',
4532                     href : this.href ? this.href : '#',
4533                     html : this.html ? this.html : ''
4534                 }
4535             ]
4536         };
4537         
4538         if(this.cls){
4539             cfg.cls = this.cls;
4540         }
4541         
4542         if(this.disabled){
4543             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4544         }
4545         
4546         if(this.active){
4547             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4548         }
4549         
4550         return cfg;
4551     },
4552     
4553     initEvents: function() {
4554         
4555         this.el.on('click', this.onClick, this);
4556         
4557     },
4558     onClick : function(e)
4559     {
4560         Roo.log('PaginationItem on click ');
4561         if(this.preventDefault){
4562             e.preventDefault();
4563         }
4564         
4565         if(this.disabled){
4566             return;
4567         }
4568         
4569         this.fireEvent('click', this, e);
4570     }
4571    
4572 });
4573
4574  
4575
4576  /*
4577  * - LGPL
4578  *
4579  * slider
4580  * 
4581  */
4582
4583
4584 /**
4585  * @class Roo.bootstrap.Slider
4586  * @extends Roo.bootstrap.Component
4587  * Bootstrap Slider class
4588  *    
4589  * @constructor
4590  * Create a new Slider
4591  * @param {Object} config The config object
4592  */
4593
4594 Roo.bootstrap.Slider = function(config){
4595     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4596 };
4597
4598 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4599     
4600     getAutoCreate : function(){
4601         
4602         var cfg = {
4603             tag: 'div',
4604             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4605             cn: [
4606                 {
4607                     tag: 'a',
4608                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4609                 }
4610             ]
4611         }
4612         
4613         return cfg;
4614     }
4615    
4616 });
4617
4618  /*
4619  * Based on:
4620  * Ext JS Library 1.1.1
4621  * Copyright(c) 2006-2007, Ext JS, LLC.
4622  *
4623  * Originally Released Under LGPL - original licence link has changed is not relivant.
4624  *
4625  * Fork - LGPL
4626  * <script type="text/javascript">
4627  */
4628  
4629
4630 /**
4631  * @class Roo.grid.ColumnModel
4632  * @extends Roo.util.Observable
4633  * This is the default implementation of a ColumnModel used by the Grid. It defines
4634  * the columns in the grid.
4635  * <br>Usage:<br>
4636  <pre><code>
4637  var colModel = new Roo.grid.ColumnModel([
4638         {header: "Ticker", width: 60, sortable: true, locked: true},
4639         {header: "Company Name", width: 150, sortable: true},
4640         {header: "Market Cap.", width: 100, sortable: true},
4641         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4642         {header: "Employees", width: 100, sortable: true, resizable: false}
4643  ]);
4644  </code></pre>
4645  * <p>
4646  
4647  * The config options listed for this class are options which may appear in each
4648  * individual column definition.
4649  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4650  * @constructor
4651  * @param {Object} config An Array of column config objects. See this class's
4652  * config objects for details.
4653 */
4654 Roo.grid.ColumnModel = function(config){
4655         /**
4656      * The config passed into the constructor
4657      */
4658     this.config = config;
4659     this.lookup = {};
4660
4661     // if no id, create one
4662     // if the column does not have a dataIndex mapping,
4663     // map it to the order it is in the config
4664     for(var i = 0, len = config.length; i < len; i++){
4665         var c = config[i];
4666         if(typeof c.dataIndex == "undefined"){
4667             c.dataIndex = i;
4668         }
4669         if(typeof c.renderer == "string"){
4670             c.renderer = Roo.util.Format[c.renderer];
4671         }
4672         if(typeof c.id == "undefined"){
4673             c.id = Roo.id();
4674         }
4675         if(c.editor && c.editor.xtype){
4676             c.editor  = Roo.factory(c.editor, Roo.grid);
4677         }
4678         if(c.editor && c.editor.isFormField){
4679             c.editor = new Roo.grid.GridEditor(c.editor);
4680         }
4681         this.lookup[c.id] = c;
4682     }
4683
4684     /**
4685      * The width of columns which have no width specified (defaults to 100)
4686      * @type Number
4687      */
4688     this.defaultWidth = 100;
4689
4690     /**
4691      * Default sortable of columns which have no sortable specified (defaults to false)
4692      * @type Boolean
4693      */
4694     this.defaultSortable = false;
4695
4696     this.addEvents({
4697         /**
4698              * @event widthchange
4699              * Fires when the width of a column changes.
4700              * @param {ColumnModel} this
4701              * @param {Number} columnIndex The column index
4702              * @param {Number} newWidth The new width
4703              */
4704             "widthchange": true,
4705         /**
4706              * @event headerchange
4707              * Fires when the text of a header changes.
4708              * @param {ColumnModel} this
4709              * @param {Number} columnIndex The column index
4710              * @param {Number} newText The new header text
4711              */
4712             "headerchange": true,
4713         /**
4714              * @event hiddenchange
4715              * Fires when a column is hidden or "unhidden".
4716              * @param {ColumnModel} this
4717              * @param {Number} columnIndex The column index
4718              * @param {Boolean} hidden true if hidden, false otherwise
4719              */
4720             "hiddenchange": true,
4721             /**
4722          * @event columnmoved
4723          * Fires when a column is moved.
4724          * @param {ColumnModel} this
4725          * @param {Number} oldIndex
4726          * @param {Number} newIndex
4727          */
4728         "columnmoved" : true,
4729         /**
4730          * @event columlockchange
4731          * Fires when a column's locked state is changed
4732          * @param {ColumnModel} this
4733          * @param {Number} colIndex
4734          * @param {Boolean} locked true if locked
4735          */
4736         "columnlockchange" : true
4737     });
4738     Roo.grid.ColumnModel.superclass.constructor.call(this);
4739 };
4740 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4741     /**
4742      * @cfg {String} header The header text to display in the Grid view.
4743      */
4744     /**
4745      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4746      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4747      * specified, the column's index is used as an index into the Record's data Array.
4748      */
4749     /**
4750      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4751      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4752      */
4753     /**
4754      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4755      * Defaults to the value of the {@link #defaultSortable} property.
4756      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4757      */
4758     /**
4759      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4760      */
4761     /**
4762      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4763      */
4764     /**
4765      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4766      */
4767     /**
4768      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4769      */
4770     /**
4771      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4772      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4773      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4774      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4775      */
4776        /**
4777      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4778      */
4779     /**
4780      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4781      */
4782     /**
4783      * @cfg {String} cursor (Optional)
4784      */
4785     /**
4786      * @cfg {String} tooltip (Optional)
4787      */
4788     /**
4789      * Returns the id of the column at the specified index.
4790      * @param {Number} index The column index
4791      * @return {String} the id
4792      */
4793     getColumnId : function(index){
4794         return this.config[index].id;
4795     },
4796
4797     /**
4798      * Returns the column for a specified id.
4799      * @param {String} id The column id
4800      * @return {Object} the column
4801      */
4802     getColumnById : function(id){
4803         return this.lookup[id];
4804     },
4805
4806     
4807     /**
4808      * Returns the column for a specified dataIndex.
4809      * @param {String} dataIndex The column dataIndex
4810      * @return {Object|Boolean} the column or false if not found
4811      */
4812     getColumnByDataIndex: function(dataIndex){
4813         var index = this.findColumnIndex(dataIndex);
4814         return index > -1 ? this.config[index] : false;
4815     },
4816     
4817     /**
4818      * Returns the index for a specified column id.
4819      * @param {String} id The column id
4820      * @return {Number} the index, or -1 if not found
4821      */
4822     getIndexById : function(id){
4823         for(var i = 0, len = this.config.length; i < len; i++){
4824             if(this.config[i].id == id){
4825                 return i;
4826             }
4827         }
4828         return -1;
4829     },
4830     
4831     /**
4832      * Returns the index for a specified column dataIndex.
4833      * @param {String} dataIndex The column dataIndex
4834      * @return {Number} the index, or -1 if not found
4835      */
4836     
4837     findColumnIndex : function(dataIndex){
4838         for(var i = 0, len = this.config.length; i < len; i++){
4839             if(this.config[i].dataIndex == dataIndex){
4840                 return i;
4841             }
4842         }
4843         return -1;
4844     },
4845     
4846     
4847     moveColumn : function(oldIndex, newIndex){
4848         var c = this.config[oldIndex];
4849         this.config.splice(oldIndex, 1);
4850         this.config.splice(newIndex, 0, c);
4851         this.dataMap = null;
4852         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4853     },
4854
4855     isLocked : function(colIndex){
4856         return this.config[colIndex].locked === true;
4857     },
4858
4859     setLocked : function(colIndex, value, suppressEvent){
4860         if(this.isLocked(colIndex) == value){
4861             return;
4862         }
4863         this.config[colIndex].locked = value;
4864         if(!suppressEvent){
4865             this.fireEvent("columnlockchange", this, colIndex, value);
4866         }
4867     },
4868
4869     getTotalLockedWidth : function(){
4870         var totalWidth = 0;
4871         for(var i = 0; i < this.config.length; i++){
4872             if(this.isLocked(i) && !this.isHidden(i)){
4873                 this.totalWidth += this.getColumnWidth(i);
4874             }
4875         }
4876         return totalWidth;
4877     },
4878
4879     getLockedCount : function(){
4880         for(var i = 0, len = this.config.length; i < len; i++){
4881             if(!this.isLocked(i)){
4882                 return i;
4883             }
4884         }
4885     },
4886
4887     /**
4888      * Returns the number of columns.
4889      * @return {Number}
4890      */
4891     getColumnCount : function(visibleOnly){
4892         if(visibleOnly === true){
4893             var c = 0;
4894             for(var i = 0, len = this.config.length; i < len; i++){
4895                 if(!this.isHidden(i)){
4896                     c++;
4897                 }
4898             }
4899             return c;
4900         }
4901         return this.config.length;
4902     },
4903
4904     /**
4905      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4906      * @param {Function} fn
4907      * @param {Object} scope (optional)
4908      * @return {Array} result
4909      */
4910     getColumnsBy : function(fn, scope){
4911         var r = [];
4912         for(var i = 0, len = this.config.length; i < len; i++){
4913             var c = this.config[i];
4914             if(fn.call(scope||this, c, i) === true){
4915                 r[r.length] = c;
4916             }
4917         }
4918         return r;
4919     },
4920
4921     /**
4922      * Returns true if the specified column is sortable.
4923      * @param {Number} col The column index
4924      * @return {Boolean}
4925      */
4926     isSortable : function(col){
4927         if(typeof this.config[col].sortable == "undefined"){
4928             return this.defaultSortable;
4929         }
4930         return this.config[col].sortable;
4931     },
4932
4933     /**
4934      * Returns the rendering (formatting) function defined for the column.
4935      * @param {Number} col The column index.
4936      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4937      */
4938     getRenderer : function(col){
4939         if(!this.config[col].renderer){
4940             return Roo.grid.ColumnModel.defaultRenderer;
4941         }
4942         return this.config[col].renderer;
4943     },
4944
4945     /**
4946      * Sets the rendering (formatting) function for a column.
4947      * @param {Number} col The column index
4948      * @param {Function} fn The function to use to process the cell's raw data
4949      * to return HTML markup for the grid view. The render function is called with
4950      * the following parameters:<ul>
4951      * <li>Data value.</li>
4952      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4953      * <li>css A CSS style string to apply to the table cell.</li>
4954      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4955      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4956      * <li>Row index</li>
4957      * <li>Column index</li>
4958      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4959      */
4960     setRenderer : function(col, fn){
4961         this.config[col].renderer = fn;
4962     },
4963
4964     /**
4965      * Returns the width for the specified column.
4966      * @param {Number} col The column index
4967      * @return {Number}
4968      */
4969     getColumnWidth : function(col){
4970         return this.config[col].width * 1 || this.defaultWidth;
4971     },
4972
4973     /**
4974      * Sets the width for a column.
4975      * @param {Number} col The column index
4976      * @param {Number} width The new width
4977      */
4978     setColumnWidth : function(col, width, suppressEvent){
4979         this.config[col].width = width;
4980         this.totalWidth = null;
4981         if(!suppressEvent){
4982              this.fireEvent("widthchange", this, col, width);
4983         }
4984     },
4985
4986     /**
4987      * Returns the total width of all columns.
4988      * @param {Boolean} includeHidden True to include hidden column widths
4989      * @return {Number}
4990      */
4991     getTotalWidth : function(includeHidden){
4992         if(!this.totalWidth){
4993             this.totalWidth = 0;
4994             for(var i = 0, len = this.config.length; i < len; i++){
4995                 if(includeHidden || !this.isHidden(i)){
4996                     this.totalWidth += this.getColumnWidth(i);
4997                 }
4998             }
4999         }
5000         return this.totalWidth;
5001     },
5002
5003     /**
5004      * Returns the header for the specified column.
5005      * @param {Number} col The column index
5006      * @return {String}
5007      */
5008     getColumnHeader : function(col){
5009         return this.config[col].header;
5010     },
5011
5012     /**
5013      * Sets the header for a column.
5014      * @param {Number} col The column index
5015      * @param {String} header The new header
5016      */
5017     setColumnHeader : function(col, header){
5018         this.config[col].header = header;
5019         this.fireEvent("headerchange", this, col, header);
5020     },
5021
5022     /**
5023      * Returns the tooltip for the specified column.
5024      * @param {Number} col The column index
5025      * @return {String}
5026      */
5027     getColumnTooltip : function(col){
5028             return this.config[col].tooltip;
5029     },
5030     /**
5031      * Sets the tooltip for a column.
5032      * @param {Number} col The column index
5033      * @param {String} tooltip The new tooltip
5034      */
5035     setColumnTooltip : function(col, tooltip){
5036             this.config[col].tooltip = tooltip;
5037     },
5038
5039     /**
5040      * Returns the dataIndex for the specified column.
5041      * @param {Number} col The column index
5042      * @return {Number}
5043      */
5044     getDataIndex : function(col){
5045         return this.config[col].dataIndex;
5046     },
5047
5048     /**
5049      * Sets the dataIndex for a column.
5050      * @param {Number} col The column index
5051      * @param {Number} dataIndex The new dataIndex
5052      */
5053     setDataIndex : function(col, dataIndex){
5054         this.config[col].dataIndex = dataIndex;
5055     },
5056
5057     
5058     
5059     /**
5060      * Returns true if the cell is editable.
5061      * @param {Number} colIndex The column index
5062      * @param {Number} rowIndex The row index
5063      * @return {Boolean}
5064      */
5065     isCellEditable : function(colIndex, rowIndex){
5066         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5067     },
5068
5069     /**
5070      * Returns the editor defined for the cell/column.
5071      * return false or null to disable editing.
5072      * @param {Number} colIndex The column index
5073      * @param {Number} rowIndex The row index
5074      * @return {Object}
5075      */
5076     getCellEditor : function(colIndex, rowIndex){
5077         return this.config[colIndex].editor;
5078     },
5079
5080     /**
5081      * Sets if a column is editable.
5082      * @param {Number} col The column index
5083      * @param {Boolean} editable True if the column is editable
5084      */
5085     setEditable : function(col, editable){
5086         this.config[col].editable = editable;
5087     },
5088
5089
5090     /**
5091      * Returns true if the column is hidden.
5092      * @param {Number} colIndex The column index
5093      * @return {Boolean}
5094      */
5095     isHidden : function(colIndex){
5096         return this.config[colIndex].hidden;
5097     },
5098
5099
5100     /**
5101      * Returns true if the column width cannot be changed
5102      */
5103     isFixed : function(colIndex){
5104         return this.config[colIndex].fixed;
5105     },
5106
5107     /**
5108      * Returns true if the column can be resized
5109      * @return {Boolean}
5110      */
5111     isResizable : function(colIndex){
5112         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5113     },
5114     /**
5115      * Sets if a column is hidden.
5116      * @param {Number} colIndex The column index
5117      * @param {Boolean} hidden True if the column is hidden
5118      */
5119     setHidden : function(colIndex, hidden){
5120         this.config[colIndex].hidden = hidden;
5121         this.totalWidth = null;
5122         this.fireEvent("hiddenchange", this, colIndex, hidden);
5123     },
5124
5125     /**
5126      * Sets the editor for a column.
5127      * @param {Number} col The column index
5128      * @param {Object} editor The editor object
5129      */
5130     setEditor : function(col, editor){
5131         this.config[col].editor = editor;
5132     }
5133 });
5134
5135 Roo.grid.ColumnModel.defaultRenderer = function(value){
5136         if(typeof value == "string" && value.length < 1){
5137             return "&#160;";
5138         }
5139         return value;
5140 };
5141
5142 // Alias for backwards compatibility
5143 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5144 /*
5145  * Based on:
5146  * Ext JS Library 1.1.1
5147  * Copyright(c) 2006-2007, Ext JS, LLC.
5148  *
5149  * Originally Released Under LGPL - original licence link has changed is not relivant.
5150  *
5151  * Fork - LGPL
5152  * <script type="text/javascript">
5153  */
5154  
5155 /**
5156  * @class Roo.LoadMask
5157  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5158  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5159  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5160  * element's UpdateManager load indicator and will be destroyed after the initial load.
5161  * @constructor
5162  * Create a new LoadMask
5163  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5164  * @param {Object} config The config object
5165  */
5166 Roo.LoadMask = function(el, config){
5167     this.el = Roo.get(el);
5168     Roo.apply(this, config);
5169     if(this.store){
5170         this.store.on('beforeload', this.onBeforeLoad, this);
5171         this.store.on('load', this.onLoad, this);
5172         this.store.on('loadexception', this.onLoadException, this);
5173         this.removeMask = false;
5174     }else{
5175         var um = this.el.getUpdateManager();
5176         um.showLoadIndicator = false; // disable the default indicator
5177         um.on('beforeupdate', this.onBeforeLoad, this);
5178         um.on('update', this.onLoad, this);
5179         um.on('failure', this.onLoad, this);
5180         this.removeMask = true;
5181     }
5182 };
5183
5184 Roo.LoadMask.prototype = {
5185     /**
5186      * @cfg {Boolean} removeMask
5187      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5188      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5189      */
5190     /**
5191      * @cfg {String} msg
5192      * The text to display in a centered loading message box (defaults to 'Loading...')
5193      */
5194     msg : 'Loading...',
5195     /**
5196      * @cfg {String} msgCls
5197      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5198      */
5199     msgCls : 'x-mask-loading',
5200
5201     /**
5202      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5203      * @type Boolean
5204      */
5205     disabled: false,
5206
5207     /**
5208      * Disables the mask to prevent it from being displayed
5209      */
5210     disable : function(){
5211        this.disabled = true;
5212     },
5213
5214     /**
5215      * Enables the mask so that it can be displayed
5216      */
5217     enable : function(){
5218         this.disabled = false;
5219     },
5220     
5221     onLoadException : function()
5222     {
5223         Roo.log(arguments);
5224         
5225         if (typeof(arguments[3]) != 'undefined') {
5226             Roo.MessageBox.alert("Error loading",arguments[3]);
5227         } 
5228         /*
5229         try {
5230             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5231                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5232             }   
5233         } catch(e) {
5234             
5235         }
5236         */
5237     
5238         
5239         
5240         this.el.unmask(this.removeMask);
5241     },
5242     // private
5243     onLoad : function()
5244     {
5245         this.el.unmask(this.removeMask);
5246     },
5247
5248     // private
5249     onBeforeLoad : function(){
5250         if(!this.disabled){
5251             this.el.mask(this.msg, this.msgCls);
5252         }
5253     },
5254
5255     // private
5256     destroy : function(){
5257         if(this.store){
5258             this.store.un('beforeload', this.onBeforeLoad, this);
5259             this.store.un('load', this.onLoad, this);
5260             this.store.un('loadexception', this.onLoadException, this);
5261         }else{
5262             var um = this.el.getUpdateManager();
5263             um.un('beforeupdate', this.onBeforeLoad, this);
5264             um.un('update', this.onLoad, this);
5265             um.un('failure', this.onLoad, this);
5266         }
5267     }
5268 };/*
5269  * - LGPL
5270  *
5271  * table
5272  * 
5273  */
5274
5275 /**
5276  * @class Roo.bootstrap.Table
5277  * @extends Roo.bootstrap.Component
5278  * Bootstrap Table class
5279  * @cfg {String} cls table class
5280  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5281  * @cfg {String} bgcolor Specifies the background color for a table
5282  * @cfg {Number} border Specifies whether the table cells should have borders or not
5283  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5284  * @cfg {Number} cellspacing Specifies the space between cells
5285  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5286  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5287  * @cfg {String} sortable Specifies that the table should be sortable
5288  * @cfg {String} summary Specifies a summary of the content of a table
5289  * @cfg {Number} width Specifies the width of a table
5290  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5291  * 
5292  * @cfg {boolean} striped Should the rows be alternative striped
5293  * @cfg {boolean} bordered Add borders to the table
5294  * @cfg {boolean} hover Add hover highlighting
5295  * @cfg {boolean} condensed Format condensed
5296  * @cfg {boolean} responsive Format condensed
5297  * @cfg {Boolean} loadMask (true|false) default false
5298  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5299  * @cfg {Boolean} thead (true|false) generate thead, default true
5300  * @cfg {Boolean} RowSelection (true|false) default false
5301  * @cfg {Boolean} CellSelection (true|false) default false
5302  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5303  
5304  * 
5305  * @constructor
5306  * Create a new Table
5307  * @param {Object} config The config object
5308  */
5309
5310 Roo.bootstrap.Table = function(config){
5311     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5312     
5313     if (this.sm) {
5314         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5315         this.sm = this.selModel;
5316         this.sm.xmodule = this.xmodule || false;
5317     }
5318     if (this.cm && typeof(this.cm.config) == 'undefined') {
5319         this.colModel = new Roo.grid.ColumnModel(this.cm);
5320         this.cm = this.colModel;
5321         this.cm.xmodule = this.xmodule || false;
5322     }
5323     if (this.store) {
5324         this.store= Roo.factory(this.store, Roo.data);
5325         this.ds = this.store;
5326         this.ds.xmodule = this.xmodule || false;
5327          
5328     }
5329     if (this.footer && this.store) {
5330         this.footer.dataSource = this.ds;
5331         this.footer = Roo.factory(this.footer);
5332     }
5333     
5334     /** @private */
5335     this.addEvents({
5336         /**
5337          * @event cellclick
5338          * Fires when a cell is clicked
5339          * @param {Roo.bootstrap.Table} this
5340          * @param {Roo.Element} el
5341          * @param {Number} rowIndex
5342          * @param {Number} columnIndex
5343          * @param {Roo.EventObject} e
5344          */
5345         "cellclick" : true,
5346         /**
5347          * @event celldblclick
5348          * Fires when a cell is double clicked
5349          * @param {Roo.bootstrap.Table} this
5350          * @param {Roo.Element} el
5351          * @param {Number} rowIndex
5352          * @param {Number} columnIndex
5353          * @param {Roo.EventObject} e
5354          */
5355         "celldblclick" : true,
5356         /**
5357          * @event rowclick
5358          * Fires when a row is clicked
5359          * @param {Roo.bootstrap.Table} this
5360          * @param {Roo.Element} el
5361          * @param {Number} rowIndex
5362          * @param {Roo.EventObject} e
5363          */
5364         "rowclick" : true,
5365         /**
5366          * @event rowdblclick
5367          * Fires when a row is double clicked
5368          * @param {Roo.bootstrap.Table} this
5369          * @param {Roo.Element} el
5370          * @param {Number} rowIndex
5371          * @param {Roo.EventObject} e
5372          */
5373         "rowdblclick" : true,
5374         /**
5375          * @event mouseover
5376          * Fires when a mouseover occur
5377          * @param {Roo.bootstrap.Table} this
5378          * @param {Roo.Element} el
5379          * @param {Number} rowIndex
5380          * @param {Number} columnIndex
5381          * @param {Roo.EventObject} e
5382          */
5383         "mouseover" : true,
5384         /**
5385          * @event mouseout
5386          * Fires when a mouseout occur
5387          * @param {Roo.bootstrap.Table} this
5388          * @param {Roo.Element} el
5389          * @param {Number} rowIndex
5390          * @param {Number} columnIndex
5391          * @param {Roo.EventObject} e
5392          */
5393         "mouseout" : true,
5394         /**
5395          * @event rowclass
5396          * Fires when a row is rendered, so you can change add a style to it.
5397          * @param {Roo.bootstrap.Table} this
5398          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5399          */
5400         'rowclass' : true,
5401           /**
5402          * @event rowsrendered
5403          * Fires when all the  rows have been rendered
5404          * @param {Roo.bootstrap.Table} this
5405          */
5406         'rowsrendered' : true
5407         
5408     });
5409 };
5410
5411 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5412     
5413     cls: false,
5414     align: false,
5415     bgcolor: false,
5416     border: false,
5417     cellpadding: false,
5418     cellspacing: false,
5419     frame: false,
5420     rules: false,
5421     sortable: false,
5422     summary: false,
5423     width: false,
5424     striped : false,
5425     bordered: false,
5426     hover:  false,
5427     condensed : false,
5428     responsive : false,
5429     sm : false,
5430     cm : false,
5431     store : false,
5432     loadMask : false,
5433     tfoot : true,
5434     thead : true,
5435     RowSelection : false,
5436     CellSelection : false,
5437     layout : false,
5438     
5439     // Roo.Element - the tbody
5440     mainBody: false, 
5441     
5442     getAutoCreate : function(){
5443         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5444         
5445         cfg = {
5446             tag: 'table',
5447             cls : 'table',
5448             cn : []
5449         }
5450             
5451         if (this.striped) {
5452             cfg.cls += ' table-striped';
5453         }
5454         
5455         if (this.hover) {
5456             cfg.cls += ' table-hover';
5457         }
5458         if (this.bordered) {
5459             cfg.cls += ' table-bordered';
5460         }
5461         if (this.condensed) {
5462             cfg.cls += ' table-condensed';
5463         }
5464         if (this.responsive) {
5465             cfg.cls += ' table-responsive';
5466         }
5467         
5468         if (this.cls) {
5469             cfg.cls+=  ' ' +this.cls;
5470         }
5471         
5472         // this lot should be simplifed...
5473         
5474         if (this.align) {
5475             cfg.align=this.align;
5476         }
5477         if (this.bgcolor) {
5478             cfg.bgcolor=this.bgcolor;
5479         }
5480         if (this.border) {
5481             cfg.border=this.border;
5482         }
5483         if (this.cellpadding) {
5484             cfg.cellpadding=this.cellpadding;
5485         }
5486         if (this.cellspacing) {
5487             cfg.cellspacing=this.cellspacing;
5488         }
5489         if (this.frame) {
5490             cfg.frame=this.frame;
5491         }
5492         if (this.rules) {
5493             cfg.rules=this.rules;
5494         }
5495         if (this.sortable) {
5496             cfg.sortable=this.sortable;
5497         }
5498         if (this.summary) {
5499             cfg.summary=this.summary;
5500         }
5501         if (this.width) {
5502             cfg.width=this.width;
5503         }
5504         if (this.layout) {
5505             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5506         }
5507         
5508         if(this.store || this.cm){
5509             if(this.thead){
5510                 cfg.cn.push(this.renderHeader());
5511             }
5512             
5513             cfg.cn.push(this.renderBody());
5514             
5515             if(this.tfoot){
5516                 cfg.cn.push(this.renderFooter());
5517             }
5518             
5519             cfg.cls+=  ' TableGrid';
5520         }
5521         
5522         return { cn : [ cfg ] };
5523     },
5524     
5525     initEvents : function()
5526     {   
5527         if(!this.store || !this.cm){
5528             return;
5529         }
5530         
5531         //Roo.log('initEvents with ds!!!!');
5532         
5533         this.mainBody = this.el.select('tbody', true).first();
5534         
5535         
5536         var _this = this;
5537         
5538         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5539             e.on('click', _this.sort, _this);
5540         });
5541         
5542         this.el.on("click", this.onClick, this);
5543         this.el.on("dblclick", this.onDblClick, this);
5544         
5545         // why is this done????? = it breaks dialogs??
5546         //this.parent().el.setStyle('position', 'relative');
5547         
5548         
5549         if (this.footer) {
5550             this.footer.parentId = this.id;
5551             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5552         }
5553         
5554         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5555         
5556         this.store.on('load', this.onLoad, this);
5557         this.store.on('beforeload', this.onBeforeLoad, this);
5558         this.store.on('update', this.onUpdate, this);
5559         this.store.on('add', this.onAdd, this);
5560         
5561     },
5562     
5563     onMouseover : function(e, el)
5564     {
5565         var cell = Roo.get(el);
5566         
5567         if(!cell){
5568             return;
5569         }
5570         
5571         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5572             cell = cell.findParent('td', false, true);
5573         }
5574         
5575         var row = cell.findParent('tr', false, true);
5576         var cellIndex = cell.dom.cellIndex;
5577         var rowIndex = row.dom.rowIndex - 1; // start from 0
5578         
5579         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5580         
5581     },
5582     
5583     onMouseout : function(e, el)
5584     {
5585         var cell = Roo.get(el);
5586         
5587         if(!cell){
5588             return;
5589         }
5590         
5591         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5592             cell = cell.findParent('td', false, true);
5593         }
5594         
5595         var row = cell.findParent('tr', false, true);
5596         var cellIndex = cell.dom.cellIndex;
5597         var rowIndex = row.dom.rowIndex - 1; // start from 0
5598         
5599         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5600         
5601     },
5602     
5603     onClick : function(e, el)
5604     {
5605         var cell = Roo.get(el);
5606         
5607         if(!cell || (!this.CellSelection && !this.RowSelection)){
5608             return;
5609         }
5610         
5611         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5612             cell = cell.findParent('td', false, true);
5613         }
5614         
5615         if(!cell || typeof(cell) == 'undefined'){
5616             return;
5617         }
5618         
5619         var row = cell.findParent('tr', false, true);
5620         
5621         if(!row || typeof(row) == 'undefined'){
5622             return;
5623         }
5624         
5625         var cellIndex = cell.dom.cellIndex;
5626         var rowIndex = this.getRowIndex(row);
5627         
5628         if(this.CellSelection){
5629             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5630         }
5631         
5632         if(this.RowSelection){
5633             this.fireEvent('rowclick', this, row, rowIndex, e);
5634         }
5635         
5636         
5637     },
5638     
5639     onDblClick : function(e,el)
5640     {
5641         var cell = Roo.get(el);
5642         
5643         if(!cell || (!this.CellSelection && !this.RowSelection)){
5644             return;
5645         }
5646         
5647         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5648             cell = cell.findParent('td', false, true);
5649         }
5650         
5651         if(!cell || typeof(cell) == 'undefined'){
5652             return;
5653         }
5654         
5655         var row = cell.findParent('tr', false, true);
5656         
5657         if(!row || typeof(row) == 'undefined'){
5658             return;
5659         }
5660         
5661         var cellIndex = cell.dom.cellIndex;
5662         var rowIndex = this.getRowIndex(row);
5663         
5664         if(this.CellSelection){
5665             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5666         }
5667         
5668         if(this.RowSelection){
5669             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5670         }
5671     },
5672     
5673     sort : function(e,el)
5674     {
5675         var col = Roo.get(el);
5676         
5677         if(!col.hasClass('sortable')){
5678             return;
5679         }
5680         
5681         var sort = col.attr('sort');
5682         var dir = 'ASC';
5683         
5684         if(col.hasClass('glyphicon-arrow-up')){
5685             dir = 'DESC';
5686         }
5687         
5688         this.store.sortInfo = {field : sort, direction : dir};
5689         
5690         if (this.footer) {
5691             Roo.log("calling footer first");
5692             this.footer.onClick('first');
5693         } else {
5694         
5695             this.store.load({ params : { start : 0 } });
5696         }
5697     },
5698     
5699     renderHeader : function()
5700     {
5701         var header = {
5702             tag: 'thead',
5703             cn : []
5704         };
5705         
5706         var cm = this.cm;
5707         
5708         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5709             
5710             var config = cm.config[i];
5711                     
5712             var c = {
5713                 tag: 'th',
5714                 style : '',
5715                 html: cm.getColumnHeader(i)
5716             };
5717             
5718             if(typeof(config.tooltip) != 'undefined'){
5719                 c.tooltip = config.tooltip;
5720             }
5721             
5722             if(typeof(config.colspan) != 'undefined'){
5723                 c.colspan = config.colspan;
5724             }
5725             
5726             if(typeof(config.hidden) != 'undefined' && config.hidden){
5727                 c.style += ' display:none;';
5728             }
5729             
5730             if(typeof(config.dataIndex) != 'undefined'){
5731                 c.sort = config.dataIndex;
5732             }
5733             
5734             if(typeof(config.sortable) != 'undefined' && config.sortable){
5735                 c.cls = 'sortable';
5736             }
5737             
5738             if(typeof(config.align) != 'undefined' && config.align.length){
5739                 c.style += ' text-align:' + config.align + ';';
5740             }
5741             
5742             if(typeof(config.width) != 'undefined'){
5743                 c.style += ' width:' + config.width + 'px;';
5744             }
5745             
5746             if(typeof(config.cls) != 'undefined'){
5747                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5748             }
5749             
5750             header.cn.push(c)
5751         }
5752         
5753         return header;
5754     },
5755     
5756     renderBody : function()
5757     {
5758         var body = {
5759             tag: 'tbody',
5760             cn : [
5761                 {
5762                     tag: 'tr',
5763                     cn : [
5764                         {
5765                             tag : 'td',
5766                             colspan :  this.cm.getColumnCount()
5767                         }
5768                     ]
5769                 }
5770             ]
5771         };
5772         
5773         return body;
5774     },
5775     
5776     renderFooter : function()
5777     {
5778         var footer = {
5779             tag: 'tfoot',
5780             cn : [
5781                 {
5782                     tag: 'tr',
5783                     cn : [
5784                         {
5785                             tag : 'td',
5786                             colspan :  this.cm.getColumnCount()
5787                         }
5788                     ]
5789                 }
5790             ]
5791         };
5792         
5793         return footer;
5794     },
5795     
5796     
5797     
5798     onLoad : function()
5799     {
5800         Roo.log('ds onload');
5801         this.clear();
5802         
5803         var _this = this;
5804         var cm = this.cm;
5805         var ds = this.store;
5806         
5807         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5808             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5809             
5810             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5811                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5812             }
5813             
5814             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5815                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5816             }
5817         });
5818         
5819         var tbody =  this.mainBody;
5820               
5821         if(ds.getCount() > 0){
5822             ds.data.each(function(d,rowIndex){
5823                 var row =  this.renderRow(cm, ds, rowIndex);
5824                 
5825                 tbody.createChild(row);
5826                 
5827                 var _this = this;
5828                 
5829                 if(row.cellObjects.length){
5830                     Roo.each(row.cellObjects, function(r){
5831                         _this.renderCellObject(r);
5832                     })
5833                 }
5834                 
5835             }, this);
5836         }
5837         
5838         Roo.each(this.el.select('tbody td', true).elements, function(e){
5839             e.on('mouseover', _this.onMouseover, _this);
5840         });
5841         
5842         Roo.each(this.el.select('tbody td', true).elements, function(e){
5843             e.on('mouseout', _this.onMouseout, _this);
5844         });
5845         this.fireEvent('rowsrendered', this);
5846         //if(this.loadMask){
5847         //    this.maskEl.hide();
5848         //}
5849     },
5850     
5851     
5852     onUpdate : function(ds,record)
5853     {
5854         this.refreshRow(record);
5855     },
5856     
5857     onRemove : function(ds, record, index, isUpdate){
5858         if(isUpdate !== true){
5859             this.fireEvent("beforerowremoved", this, index, record);
5860         }
5861         var bt = this.mainBody.dom;
5862         
5863         var rows = this.el.select('tbody > tr', true).elements;
5864         
5865         if(typeof(rows[index]) != 'undefined'){
5866             bt.removeChild(rows[index].dom);
5867         }
5868         
5869 //        if(bt.rows[index]){
5870 //            bt.removeChild(bt.rows[index]);
5871 //        }
5872         
5873         if(isUpdate !== true){
5874             //this.stripeRows(index);
5875             //this.syncRowHeights(index, index);
5876             //this.layout();
5877             this.fireEvent("rowremoved", this, index, record);
5878         }
5879     },
5880     
5881     onAdd : function(ds, records, rowIndex)
5882     {
5883         //Roo.log('on Add called');
5884         // - note this does not handle multiple adding very well..
5885         var bt = this.mainBody.dom;
5886         for (var i =0 ; i < records.length;i++) {
5887             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5888             //Roo.log(records[i]);
5889             //Roo.log(this.store.getAt(rowIndex+i));
5890             this.insertRow(this.store, rowIndex + i, false);
5891             return;
5892         }
5893         
5894     },
5895     
5896     
5897     refreshRow : function(record){
5898         var ds = this.store, index;
5899         if(typeof record == 'number'){
5900             index = record;
5901             record = ds.getAt(index);
5902         }else{
5903             index = ds.indexOf(record);
5904         }
5905         this.insertRow(ds, index, true);
5906         this.onRemove(ds, record, index+1, true);
5907         //this.syncRowHeights(index, index);
5908         //this.layout();
5909         this.fireEvent("rowupdated", this, index, record);
5910     },
5911     
5912     insertRow : function(dm, rowIndex, isUpdate){
5913         
5914         if(!isUpdate){
5915             this.fireEvent("beforerowsinserted", this, rowIndex);
5916         }
5917             //var s = this.getScrollState();
5918         var row = this.renderRow(this.cm, this.store, rowIndex);
5919         // insert before rowIndex..
5920         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5921         
5922         var _this = this;
5923                 
5924         if(row.cellObjects.length){
5925             Roo.each(row.cellObjects, function(r){
5926                 _this.renderCellObject(r);
5927             })
5928         }
5929             
5930         if(!isUpdate){
5931             this.fireEvent("rowsinserted", this, rowIndex);
5932             //this.syncRowHeights(firstRow, lastRow);
5933             //this.stripeRows(firstRow);
5934             //this.layout();
5935         }
5936         
5937     },
5938     
5939     
5940     getRowDom : function(rowIndex)
5941     {
5942         var rows = this.el.select('tbody > tr', true).elements;
5943         
5944         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5945         
5946     },
5947     // returns the object tree for a tr..
5948   
5949     
5950     renderRow : function(cm, ds, rowIndex) 
5951     {
5952         
5953         var d = ds.getAt(rowIndex);
5954         
5955         var row = {
5956             tag : 'tr',
5957             cn : []
5958         };
5959             
5960         var cellObjects = [];
5961         
5962         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5963             var config = cm.config[i];
5964             
5965             var renderer = cm.getRenderer(i);
5966             var value = '';
5967             var id = false;
5968             
5969             if(typeof(renderer) !== 'undefined'){
5970                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5971             }
5972             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5973             // and are rendered into the cells after the row is rendered - using the id for the element.
5974             
5975             if(typeof(value) === 'object'){
5976                 id = Roo.id();
5977                 cellObjects.push({
5978                     container : id,
5979                     cfg : value 
5980                 })
5981             }
5982             
5983             var rowcfg = {
5984                 record: d,
5985                 rowIndex : rowIndex,
5986                 colIndex : i,
5987                 rowClass : ''
5988             }
5989
5990             this.fireEvent('rowclass', this, rowcfg);
5991             
5992             var td = {
5993                 tag: 'td',
5994                 cls : rowcfg.rowClass,
5995                 style: '',
5996                 html: (typeof(value) === 'object') ? '' : value
5997             };
5998             
5999             if (id) {
6000                 td.id = id;
6001             }
6002             
6003             if(typeof(config.colspan) != 'undefined'){
6004                 td.colspan = config.colspan;
6005             }
6006             
6007             if(typeof(config.hidden) != 'undefined' && config.hidden){
6008                 td.style += ' display:none;';
6009             }
6010             
6011             if(typeof(config.align) != 'undefined' && config.align.length){
6012                 td.style += ' text-align:' + config.align + ';';
6013             }
6014             
6015             if(typeof(config.width) != 'undefined'){
6016                 td.style += ' width:' +  config.width + 'px;';
6017             }
6018             
6019             if(typeof(config.cursor) != 'undefined'){
6020                 td.style += ' cursor:' +  config.cursor + ';';
6021             }
6022             
6023             if(typeof(config.cls) != 'undefined'){
6024                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6025             }
6026              
6027             row.cn.push(td);
6028            
6029         }
6030         
6031         row.cellObjects = cellObjects;
6032         
6033         return row;
6034           
6035     },
6036     
6037     
6038     
6039     onBeforeLoad : function()
6040     {
6041         //Roo.log('ds onBeforeLoad');
6042         
6043         //this.clear();
6044         
6045         //if(this.loadMask){
6046         //    this.maskEl.show();
6047         //}
6048     },
6049      /**
6050      * Remove all rows
6051      */
6052     clear : function()
6053     {
6054         this.el.select('tbody', true).first().dom.innerHTML = '';
6055     },
6056     /**
6057      * Show or hide a row.
6058      * @param {Number} rowIndex to show or hide
6059      * @param {Boolean} state hide
6060      */
6061     setRowVisibility : function(rowIndex, state)
6062     {
6063         var bt = this.mainBody.dom;
6064         
6065         var rows = this.el.select('tbody > tr', true).elements;
6066         
6067         if(typeof(rows[rowIndex]) == 'undefined'){
6068             return;
6069         }
6070         rows[rowIndex].dom.style.display = state ? '' : 'none';
6071     },
6072     
6073     
6074     getSelectionModel : function(){
6075         if(!this.selModel){
6076             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6077         }
6078         return this.selModel;
6079     },
6080     /*
6081      * Render the Roo.bootstrap object from renderder
6082      */
6083     renderCellObject : function(r)
6084     {
6085         var _this = this;
6086         
6087         var t = r.cfg.render(r.container);
6088         
6089         if(r.cfg.cn){
6090             Roo.each(r.cfg.cn, function(c){
6091                 var child = {
6092                     container: t.getChildContainer(),
6093                     cfg: c
6094                 }
6095                 _this.renderCellObject(child);
6096             })
6097         }
6098     },
6099     
6100     getRowIndex : function(row)
6101     {
6102         var rowIndex = -1;
6103         
6104         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6105             if(el != row){
6106                 return;
6107             }
6108             
6109             rowIndex = index;
6110         });
6111         
6112         return rowIndex;
6113     }
6114    
6115 });
6116
6117  
6118
6119  /*
6120  * - LGPL
6121  *
6122  * table cell
6123  * 
6124  */
6125
6126 /**
6127  * @class Roo.bootstrap.TableCell
6128  * @extends Roo.bootstrap.Component
6129  * Bootstrap TableCell class
6130  * @cfg {String} html cell contain text
6131  * @cfg {String} cls cell class
6132  * @cfg {String} tag cell tag (td|th) default td
6133  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6134  * @cfg {String} align Aligns the content in a cell
6135  * @cfg {String} axis Categorizes cells
6136  * @cfg {String} bgcolor Specifies the background color of a cell
6137  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6138  * @cfg {Number} colspan Specifies the number of columns a cell should span
6139  * @cfg {String} headers Specifies one or more header cells a cell is related to
6140  * @cfg {Number} height Sets the height of a cell
6141  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6142  * @cfg {Number} rowspan Sets the number of rows a cell should span
6143  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6144  * @cfg {String} valign Vertical aligns the content in a cell
6145  * @cfg {Number} width Specifies the width of a cell
6146  * 
6147  * @constructor
6148  * Create a new TableCell
6149  * @param {Object} config The config object
6150  */
6151
6152 Roo.bootstrap.TableCell = function(config){
6153     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6154 };
6155
6156 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6157     
6158     html: false,
6159     cls: false,
6160     tag: false,
6161     abbr: false,
6162     align: false,
6163     axis: false,
6164     bgcolor: false,
6165     charoff: false,
6166     colspan: false,
6167     headers: false,
6168     height: false,
6169     nowrap: false,
6170     rowspan: false,
6171     scope: false,
6172     valign: false,
6173     width: false,
6174     
6175     
6176     getAutoCreate : function(){
6177         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'td'
6181         }
6182         
6183         if(this.tag){
6184             cfg.tag = this.tag;
6185         }
6186         
6187         if (this.html) {
6188             cfg.html=this.html
6189         }
6190         if (this.cls) {
6191             cfg.cls=this.cls
6192         }
6193         if (this.abbr) {
6194             cfg.abbr=this.abbr
6195         }
6196         if (this.align) {
6197             cfg.align=this.align
6198         }
6199         if (this.axis) {
6200             cfg.axis=this.axis
6201         }
6202         if (this.bgcolor) {
6203             cfg.bgcolor=this.bgcolor
6204         }
6205         if (this.charoff) {
6206             cfg.charoff=this.charoff
6207         }
6208         if (this.colspan) {
6209             cfg.colspan=this.colspan
6210         }
6211         if (this.headers) {
6212             cfg.headers=this.headers
6213         }
6214         if (this.height) {
6215             cfg.height=this.height
6216         }
6217         if (this.nowrap) {
6218             cfg.nowrap=this.nowrap
6219         }
6220         if (this.rowspan) {
6221             cfg.rowspan=this.rowspan
6222         }
6223         if (this.scope) {
6224             cfg.scope=this.scope
6225         }
6226         if (this.valign) {
6227             cfg.valign=this.valign
6228         }
6229         if (this.width) {
6230             cfg.width=this.width
6231         }
6232         
6233         
6234         return cfg;
6235     }
6236    
6237 });
6238
6239  
6240
6241  /*
6242  * - LGPL
6243  *
6244  * table row
6245  * 
6246  */
6247
6248 /**
6249  * @class Roo.bootstrap.TableRow
6250  * @extends Roo.bootstrap.Component
6251  * Bootstrap TableRow class
6252  * @cfg {String} cls row class
6253  * @cfg {String} align Aligns the content in a table row
6254  * @cfg {String} bgcolor Specifies a background color for a table row
6255  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6256  * @cfg {String} valign Vertical aligns the content in a table row
6257  * 
6258  * @constructor
6259  * Create a new TableRow
6260  * @param {Object} config The config object
6261  */
6262
6263 Roo.bootstrap.TableRow = function(config){
6264     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6265 };
6266
6267 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6268     
6269     cls: false,
6270     align: false,
6271     bgcolor: false,
6272     charoff: false,
6273     valign: false,
6274     
6275     getAutoCreate : function(){
6276         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6277         
6278         cfg = {
6279             tag: 'tr'
6280         }
6281             
6282         if(this.cls){
6283             cfg.cls = this.cls;
6284         }
6285         if(this.align){
6286             cfg.align = this.align;
6287         }
6288         if(this.bgcolor){
6289             cfg.bgcolor = this.bgcolor;
6290         }
6291         if(this.charoff){
6292             cfg.charoff = this.charoff;
6293         }
6294         if(this.valign){
6295             cfg.valign = this.valign;
6296         }
6297         
6298         return cfg;
6299     }
6300    
6301 });
6302
6303  
6304
6305  /*
6306  * - LGPL
6307  *
6308  * table body
6309  * 
6310  */
6311
6312 /**
6313  * @class Roo.bootstrap.TableBody
6314  * @extends Roo.bootstrap.Component
6315  * Bootstrap TableBody class
6316  * @cfg {String} cls element class
6317  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6318  * @cfg {String} align Aligns the content inside the element
6319  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6320  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6321  * 
6322  * @constructor
6323  * Create a new TableBody
6324  * @param {Object} config The config object
6325  */
6326
6327 Roo.bootstrap.TableBody = function(config){
6328     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6329 };
6330
6331 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6332     
6333     cls: false,
6334     tag: false,
6335     align: false,
6336     charoff: false,
6337     valign: false,
6338     
6339     getAutoCreate : function(){
6340         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6341         
6342         cfg = {
6343             tag: 'tbody'
6344         }
6345             
6346         if (this.cls) {
6347             cfg.cls=this.cls
6348         }
6349         if(this.tag){
6350             cfg.tag = this.tag;
6351         }
6352         
6353         if(this.align){
6354             cfg.align = this.align;
6355         }
6356         if(this.charoff){
6357             cfg.charoff = this.charoff;
6358         }
6359         if(this.valign){
6360             cfg.valign = this.valign;
6361         }
6362         
6363         return cfg;
6364     }
6365     
6366     
6367 //    initEvents : function()
6368 //    {
6369 //        
6370 //        if(!this.store){
6371 //            return;
6372 //        }
6373 //        
6374 //        this.store = Roo.factory(this.store, Roo.data);
6375 //        this.store.on('load', this.onLoad, this);
6376 //        
6377 //        this.store.load();
6378 //        
6379 //    },
6380 //    
6381 //    onLoad: function () 
6382 //    {   
6383 //        this.fireEvent('load', this);
6384 //    }
6385 //    
6386 //   
6387 });
6388
6389  
6390
6391  /*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402 // as we use this in bootstrap.
6403 Roo.namespace('Roo.form');
6404  /**
6405  * @class Roo.form.Action
6406  * Internal Class used to handle form actions
6407  * @constructor
6408  * @param {Roo.form.BasicForm} el The form element or its id
6409  * @param {Object} config Configuration options
6410  */
6411
6412  
6413  
6414 // define the action interface
6415 Roo.form.Action = function(form, options){
6416     this.form = form;
6417     this.options = options || {};
6418 };
6419 /**
6420  * Client Validation Failed
6421  * @const 
6422  */
6423 Roo.form.Action.CLIENT_INVALID = 'client';
6424 /**
6425  * Server Validation Failed
6426  * @const 
6427  */
6428 Roo.form.Action.SERVER_INVALID = 'server';
6429  /**
6430  * Connect to Server Failed
6431  * @const 
6432  */
6433 Roo.form.Action.CONNECT_FAILURE = 'connect';
6434 /**
6435  * Reading Data from Server Failed
6436  * @const 
6437  */
6438 Roo.form.Action.LOAD_FAILURE = 'load';
6439
6440 Roo.form.Action.prototype = {
6441     type : 'default',
6442     failureType : undefined,
6443     response : undefined,
6444     result : undefined,
6445
6446     // interface method
6447     run : function(options){
6448
6449     },
6450
6451     // interface method
6452     success : function(response){
6453
6454     },
6455
6456     // interface method
6457     handleResponse : function(response){
6458
6459     },
6460
6461     // default connection failure
6462     failure : function(response){
6463         
6464         this.response = response;
6465         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6466         this.form.afterAction(this, false);
6467     },
6468
6469     processResponse : function(response){
6470         this.response = response;
6471         if(!response.responseText){
6472             return true;
6473         }
6474         this.result = this.handleResponse(response);
6475         return this.result;
6476     },
6477
6478     // utility functions used internally
6479     getUrl : function(appendParams){
6480         var url = this.options.url || this.form.url || this.form.el.dom.action;
6481         if(appendParams){
6482             var p = this.getParams();
6483             if(p){
6484                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6485             }
6486         }
6487         return url;
6488     },
6489
6490     getMethod : function(){
6491         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6492     },
6493
6494     getParams : function(){
6495         var bp = this.form.baseParams;
6496         var p = this.options.params;
6497         if(p){
6498             if(typeof p == "object"){
6499                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6500             }else if(typeof p == 'string' && bp){
6501                 p += '&' + Roo.urlEncode(bp);
6502             }
6503         }else if(bp){
6504             p = Roo.urlEncode(bp);
6505         }
6506         return p;
6507     },
6508
6509     createCallback : function(){
6510         return {
6511             success: this.success,
6512             failure: this.failure,
6513             scope: this,
6514             timeout: (this.form.timeout*1000),
6515             upload: this.form.fileUpload ? this.success : undefined
6516         };
6517     }
6518 };
6519
6520 Roo.form.Action.Submit = function(form, options){
6521     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6522 };
6523
6524 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6525     type : 'submit',
6526
6527     haveProgress : false,
6528     uploadComplete : false,
6529     
6530     // uploadProgress indicator.
6531     uploadProgress : function()
6532     {
6533         if (!this.form.progressUrl) {
6534             return;
6535         }
6536         
6537         if (!this.haveProgress) {
6538             Roo.MessageBox.progress("Uploading", "Uploading");
6539         }
6540         if (this.uploadComplete) {
6541            Roo.MessageBox.hide();
6542            return;
6543         }
6544         
6545         this.haveProgress = true;
6546    
6547         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6548         
6549         var c = new Roo.data.Connection();
6550         c.request({
6551             url : this.form.progressUrl,
6552             params: {
6553                 id : uid
6554             },
6555             method: 'GET',
6556             success : function(req){
6557                //console.log(data);
6558                 var rdata = false;
6559                 var edata;
6560                 try  {
6561                    rdata = Roo.decode(req.responseText)
6562                 } catch (e) {
6563                     Roo.log("Invalid data from server..");
6564                     Roo.log(edata);
6565                     return;
6566                 }
6567                 if (!rdata || !rdata.success) {
6568                     Roo.log(rdata);
6569                     Roo.MessageBox.alert(Roo.encode(rdata));
6570                     return;
6571                 }
6572                 var data = rdata.data;
6573                 
6574                 if (this.uploadComplete) {
6575                    Roo.MessageBox.hide();
6576                    return;
6577                 }
6578                    
6579                 if (data){
6580                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6581                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6582                     );
6583                 }
6584                 this.uploadProgress.defer(2000,this);
6585             },
6586        
6587             failure: function(data) {
6588                 Roo.log('progress url failed ');
6589                 Roo.log(data);
6590             },
6591             scope : this
6592         });
6593            
6594     },
6595     
6596     
6597     run : function()
6598     {
6599         // run get Values on the form, so it syncs any secondary forms.
6600         this.form.getValues();
6601         
6602         var o = this.options;
6603         var method = this.getMethod();
6604         var isPost = method == 'POST';
6605         if(o.clientValidation === false || this.form.isValid()){
6606             
6607             if (this.form.progressUrl) {
6608                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6609                     (new Date() * 1) + '' + Math.random());
6610                     
6611             } 
6612             
6613             
6614             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6615                 form:this.form.el.dom,
6616                 url:this.getUrl(!isPost),
6617                 method: method,
6618                 params:isPost ? this.getParams() : null,
6619                 isUpload: this.form.fileUpload
6620             }));
6621             
6622             this.uploadProgress();
6623
6624         }else if (o.clientValidation !== false){ // client validation failed
6625             this.failureType = Roo.form.Action.CLIENT_INVALID;
6626             this.form.afterAction(this, false);
6627         }
6628     },
6629
6630     success : function(response)
6631     {
6632         this.uploadComplete= true;
6633         if (this.haveProgress) {
6634             Roo.MessageBox.hide();
6635         }
6636         
6637         
6638         var result = this.processResponse(response);
6639         if(result === true || result.success){
6640             this.form.afterAction(this, true);
6641             return;
6642         }
6643         if(result.errors){
6644             this.form.markInvalid(result.errors);
6645             this.failureType = Roo.form.Action.SERVER_INVALID;
6646         }
6647         this.form.afterAction(this, false);
6648     },
6649     failure : function(response)
6650     {
6651         this.uploadComplete= true;
6652         if (this.haveProgress) {
6653             Roo.MessageBox.hide();
6654         }
6655         
6656         this.response = response;
6657         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6658         this.form.afterAction(this, false);
6659     },
6660     
6661     handleResponse : function(response){
6662         if(this.form.errorReader){
6663             var rs = this.form.errorReader.read(response);
6664             var errors = [];
6665             if(rs.records){
6666                 for(var i = 0, len = rs.records.length; i < len; i++) {
6667                     var r = rs.records[i];
6668                     errors[i] = r.data;
6669                 }
6670             }
6671             if(errors.length < 1){
6672                 errors = null;
6673             }
6674             return {
6675                 success : rs.success,
6676                 errors : errors
6677             };
6678         }
6679         var ret = false;
6680         try {
6681             ret = Roo.decode(response.responseText);
6682         } catch (e) {
6683             ret = {
6684                 success: false,
6685                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6686                 errors : []
6687             };
6688         }
6689         return ret;
6690         
6691     }
6692 });
6693
6694
6695 Roo.form.Action.Load = function(form, options){
6696     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6697     this.reader = this.form.reader;
6698 };
6699
6700 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6701     type : 'load',
6702
6703     run : function(){
6704         
6705         Roo.Ajax.request(Roo.apply(
6706                 this.createCallback(), {
6707                     method:this.getMethod(),
6708                     url:this.getUrl(false),
6709                     params:this.getParams()
6710         }));
6711     },
6712
6713     success : function(response){
6714         
6715         var result = this.processResponse(response);
6716         if(result === true || !result.success || !result.data){
6717             this.failureType = Roo.form.Action.LOAD_FAILURE;
6718             this.form.afterAction(this, false);
6719             return;
6720         }
6721         this.form.clearInvalid();
6722         this.form.setValues(result.data);
6723         this.form.afterAction(this, true);
6724     },
6725
6726     handleResponse : function(response){
6727         if(this.form.reader){
6728             var rs = this.form.reader.read(response);
6729             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6730             return {
6731                 success : rs.success,
6732                 data : data
6733             };
6734         }
6735         return Roo.decode(response.responseText);
6736     }
6737 });
6738
6739 Roo.form.Action.ACTION_TYPES = {
6740     'load' : Roo.form.Action.Load,
6741     'submit' : Roo.form.Action.Submit
6742 };/*
6743  * - LGPL
6744  *
6745  * form
6746  * 
6747  */
6748
6749 /**
6750  * @class Roo.bootstrap.Form
6751  * @extends Roo.bootstrap.Component
6752  * Bootstrap Form class
6753  * @cfg {String} method  GET | POST (default POST)
6754  * @cfg {String} labelAlign top | left (default top)
6755  * @cfg {String} align left  | right - for navbars
6756  * @cfg {Boolean} loadMask load mask when submit (default true)
6757
6758  * 
6759  * @constructor
6760  * Create a new Form
6761  * @param {Object} config The config object
6762  */
6763
6764
6765 Roo.bootstrap.Form = function(config){
6766     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6767     this.addEvents({
6768         /**
6769          * @event clientvalidation
6770          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6771          * @param {Form} this
6772          * @param {Boolean} valid true if the form has passed client-side validation
6773          */
6774         clientvalidation: true,
6775         /**
6776          * @event beforeaction
6777          * Fires before any action is performed. Return false to cancel the action.
6778          * @param {Form} this
6779          * @param {Action} action The action to be performed
6780          */
6781         beforeaction: true,
6782         /**
6783          * @event actionfailed
6784          * Fires when an action fails.
6785          * @param {Form} this
6786          * @param {Action} action The action that failed
6787          */
6788         actionfailed : true,
6789         /**
6790          * @event actioncomplete
6791          * Fires when an action is completed.
6792          * @param {Form} this
6793          * @param {Action} action The action that completed
6794          */
6795         actioncomplete : true
6796     });
6797     
6798 };
6799
6800 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6801       
6802      /**
6803      * @cfg {String} method
6804      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6805      */
6806     method : 'POST',
6807     /**
6808      * @cfg {String} url
6809      * The URL to use for form actions if one isn't supplied in the action options.
6810      */
6811     /**
6812      * @cfg {Boolean} fileUpload
6813      * Set to true if this form is a file upload.
6814      */
6815      
6816     /**
6817      * @cfg {Object} baseParams
6818      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6819      */
6820       
6821     /**
6822      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6823      */
6824     timeout: 30,
6825     /**
6826      * @cfg {Sting} align (left|right) for navbar forms
6827      */
6828     align : 'left',
6829
6830     // private
6831     activeAction : null,
6832  
6833     /**
6834      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6835      * element by passing it or its id or mask the form itself by passing in true.
6836      * @type Mixed
6837      */
6838     waitMsgTarget : false,
6839     
6840     loadMask : true,
6841     
6842     getAutoCreate : function(){
6843         
6844         var cfg = {
6845             tag: 'form',
6846             method : this.method || 'POST',
6847             id : this.id || Roo.id(),
6848             cls : ''
6849         }
6850         if (this.parent().xtype.match(/^Nav/)) {
6851             cfg.cls = 'navbar-form navbar-' + this.align;
6852             
6853         }
6854         
6855         if (this.labelAlign == 'left' ) {
6856             cfg.cls += ' form-horizontal';
6857         }
6858         
6859         
6860         return cfg;
6861     },
6862     initEvents : function()
6863     {
6864         this.el.on('submit', this.onSubmit, this);
6865         // this was added as random key presses on the form where triggering form submit.
6866         this.el.on('keypress', function(e) {
6867             if (e.getCharCode() != 13) {
6868                 return true;
6869             }
6870             // we might need to allow it for textareas.. and some other items.
6871             // check e.getTarget().
6872             
6873             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6874                 return true;
6875             }
6876         
6877             Roo.log("keypress blocked");
6878             
6879             e.preventDefault();
6880             return false;
6881         });
6882         
6883     },
6884     // private
6885     onSubmit : function(e){
6886         e.stopEvent();
6887     },
6888     
6889      /**
6890      * Returns true if client-side validation on the form is successful.
6891      * @return Boolean
6892      */
6893     isValid : function(){
6894         var items = this.getItems();
6895         var valid = true;
6896         items.each(function(f){
6897            if(!f.validate()){
6898                valid = false;
6899                
6900            }
6901         });
6902         return valid;
6903     },
6904     /**
6905      * Returns true if any fields in this form have changed since their original load.
6906      * @return Boolean
6907      */
6908     isDirty : function(){
6909         var dirty = false;
6910         var items = this.getItems();
6911         items.each(function(f){
6912            if(f.isDirty()){
6913                dirty = true;
6914                return false;
6915            }
6916            return true;
6917         });
6918         return dirty;
6919     },
6920      /**
6921      * Performs a predefined action (submit or load) or custom actions you define on this form.
6922      * @param {String} actionName The name of the action type
6923      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6924      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6925      * accept other config options):
6926      * <pre>
6927 Property          Type             Description
6928 ----------------  ---------------  ----------------------------------------------------------------------------------
6929 url               String           The url for the action (defaults to the form's url)
6930 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6931 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6932 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6933                                    validate the form on the client (defaults to false)
6934      * </pre>
6935      * @return {BasicForm} this
6936      */
6937     doAction : function(action, options){
6938         if(typeof action == 'string'){
6939             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6940         }
6941         if(this.fireEvent('beforeaction', this, action) !== false){
6942             this.beforeAction(action);
6943             action.run.defer(100, action);
6944         }
6945         return this;
6946     },
6947     
6948     // private
6949     beforeAction : function(action){
6950         var o = action.options;
6951         
6952         if(this.loadMask){
6953             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6954         }
6955         // not really supported yet.. ??
6956         
6957         //if(this.waitMsgTarget === true){
6958         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6959         //}else if(this.waitMsgTarget){
6960         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6961         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6962         //}else {
6963         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6964        // }
6965          
6966     },
6967
6968     // private
6969     afterAction : function(action, success){
6970         this.activeAction = null;
6971         var o = action.options;
6972         
6973         //if(this.waitMsgTarget === true){
6974             this.el.unmask();
6975         //}else if(this.waitMsgTarget){
6976         //    this.waitMsgTarget.unmask();
6977         //}else{
6978         //    Roo.MessageBox.updateProgress(1);
6979         //    Roo.MessageBox.hide();
6980        // }
6981         // 
6982         if(success){
6983             if(o.reset){
6984                 this.reset();
6985             }
6986             Roo.callback(o.success, o.scope, [this, action]);
6987             this.fireEvent('actioncomplete', this, action);
6988             
6989         }else{
6990             
6991             // failure condition..
6992             // we have a scenario where updates need confirming.
6993             // eg. if a locking scenario exists..
6994             // we look for { errors : { needs_confirm : true }} in the response.
6995             if (
6996                 (typeof(action.result) != 'undefined')  &&
6997                 (typeof(action.result.errors) != 'undefined')  &&
6998                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6999            ){
7000                 var _t = this;
7001                 Roo.log("not supported yet");
7002                  /*
7003                 
7004                 Roo.MessageBox.confirm(
7005                     "Change requires confirmation",
7006                     action.result.errorMsg,
7007                     function(r) {
7008                         if (r != 'yes') {
7009                             return;
7010                         }
7011                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7012                     }
7013                     
7014                 );
7015                 */
7016                 
7017                 
7018                 return;
7019             }
7020             
7021             Roo.callback(o.failure, o.scope, [this, action]);
7022             // show an error message if no failed handler is set..
7023             if (!this.hasListener('actionfailed')) {
7024                 Roo.log("need to add dialog support");
7025                 /*
7026                 Roo.MessageBox.alert("Error",
7027                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7028                         action.result.errorMsg :
7029                         "Saving Failed, please check your entries or try again"
7030                 );
7031                 */
7032             }
7033             
7034             this.fireEvent('actionfailed', this, action);
7035         }
7036         
7037     },
7038     /**
7039      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7040      * @param {String} id The value to search for
7041      * @return Field
7042      */
7043     findField : function(id){
7044         var items = this.getItems();
7045         var field = items.get(id);
7046         if(!field){
7047              items.each(function(f){
7048                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7049                     field = f;
7050                     return false;
7051                 }
7052                 return true;
7053             });
7054         }
7055         return field || null;
7056     },
7057      /**
7058      * Mark fields in this form invalid in bulk.
7059      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7060      * @return {BasicForm} this
7061      */
7062     markInvalid : function(errors){
7063         if(errors instanceof Array){
7064             for(var i = 0, len = errors.length; i < len; i++){
7065                 var fieldError = errors[i];
7066                 var f = this.findField(fieldError.id);
7067                 if(f){
7068                     f.markInvalid(fieldError.msg);
7069                 }
7070             }
7071         }else{
7072             var field, id;
7073             for(id in errors){
7074                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7075                     field.markInvalid(errors[id]);
7076                 }
7077             }
7078         }
7079         //Roo.each(this.childForms || [], function (f) {
7080         //    f.markInvalid(errors);
7081         //});
7082         
7083         return this;
7084     },
7085
7086     /**
7087      * Set values for fields in this form in bulk.
7088      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7089      * @return {BasicForm} this
7090      */
7091     setValues : function(values){
7092         if(values instanceof Array){ // array of objects
7093             for(var i = 0, len = values.length; i < len; i++){
7094                 var v = values[i];
7095                 var f = this.findField(v.id);
7096                 if(f){
7097                     f.setValue(v.value);
7098                     if(this.trackResetOnLoad){
7099                         f.originalValue = f.getValue();
7100                     }
7101                 }
7102             }
7103         }else{ // object hash
7104             var field, id;
7105             for(id in values){
7106                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7107                     
7108                     if (field.setFromData && 
7109                         field.valueField && 
7110                         field.displayField &&
7111                         // combos' with local stores can 
7112                         // be queried via setValue()
7113                         // to set their value..
7114                         (field.store && !field.store.isLocal)
7115                         ) {
7116                         // it's a combo
7117                         var sd = { };
7118                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7119                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7120                         field.setFromData(sd);
7121                         
7122                     } else {
7123                         field.setValue(values[id]);
7124                     }
7125                     
7126                     
7127                     if(this.trackResetOnLoad){
7128                         field.originalValue = field.getValue();
7129                     }
7130                 }
7131             }
7132         }
7133          
7134         //Roo.each(this.childForms || [], function (f) {
7135         //    f.setValues(values);
7136         //});
7137                 
7138         return this;
7139     },
7140
7141     /**
7142      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7143      * they are returned as an array.
7144      * @param {Boolean} asString
7145      * @return {Object}
7146      */
7147     getValues : function(asString){
7148         //if (this.childForms) {
7149             // copy values from the child forms
7150         //    Roo.each(this.childForms, function (f) {
7151         //        this.setValues(f.getValues());
7152         //    }, this);
7153         //}
7154         
7155         
7156         
7157         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7158         if(asString === true){
7159             return fs;
7160         }
7161         return Roo.urlDecode(fs);
7162     },
7163     
7164     /**
7165      * Returns the fields in this form as an object with key/value pairs. 
7166      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7167      * @return {Object}
7168      */
7169     getFieldValues : function(with_hidden)
7170     {
7171         var items = this.getItems();
7172         var ret = {};
7173         items.each(function(f){
7174             if (!f.getName()) {
7175                 return;
7176             }
7177             var v = f.getValue();
7178             if (f.inputType =='radio') {
7179                 if (typeof(ret[f.getName()]) == 'undefined') {
7180                     ret[f.getName()] = ''; // empty..
7181                 }
7182                 
7183                 if (!f.el.dom.checked) {
7184                     return;
7185                     
7186                 }
7187                 v = f.el.dom.value;
7188                 
7189             }
7190             
7191             // not sure if this supported any more..
7192             if ((typeof(v) == 'object') && f.getRawValue) {
7193                 v = f.getRawValue() ; // dates..
7194             }
7195             // combo boxes where name != hiddenName...
7196             if (f.name != f.getName()) {
7197                 ret[f.name] = f.getRawValue();
7198             }
7199             ret[f.getName()] = v;
7200         });
7201         
7202         return ret;
7203     },
7204
7205     /**
7206      * Clears all invalid messages in this form.
7207      * @return {BasicForm} this
7208      */
7209     clearInvalid : function(){
7210         var items = this.getItems();
7211         
7212         items.each(function(f){
7213            f.clearInvalid();
7214         });
7215         
7216         
7217         
7218         return this;
7219     },
7220
7221     /**
7222      * Resets this form.
7223      * @return {BasicForm} this
7224      */
7225     reset : function(){
7226         var items = this.getItems();
7227         items.each(function(f){
7228             f.reset();
7229         });
7230         
7231         Roo.each(this.childForms || [], function (f) {
7232             f.reset();
7233         });
7234        
7235         
7236         return this;
7237     },
7238     getItems : function()
7239     {
7240         var r=new Roo.util.MixedCollection(false, function(o){
7241             return o.id || (o.id = Roo.id());
7242         });
7243         var iter = function(el) {
7244             if (el.inputEl) {
7245                 r.add(el);
7246             }
7247             if (!el.items) {
7248                 return;
7249             }
7250             Roo.each(el.items,function(e) {
7251                 iter(e);
7252             });
7253             
7254             
7255         };
7256         
7257         iter(this);
7258         return r;
7259         
7260         
7261         
7262         
7263     }
7264     
7265 });
7266
7267  
7268 /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278 /**
7279  * @class Roo.form.VTypes
7280  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7281  * @singleton
7282  */
7283 Roo.form.VTypes = function(){
7284     // closure these in so they are only created once.
7285     var alpha = /^[a-zA-Z_]+$/;
7286     var alphanum = /^[a-zA-Z0-9_]+$/;
7287     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7288     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7289
7290     // All these messages and functions are configurable
7291     return {
7292         /**
7293          * The function used to validate email addresses
7294          * @param {String} value The email address
7295          */
7296         'email' : function(v){
7297             return email.test(v);
7298         },
7299         /**
7300          * The error text to display when the email validation function returns false
7301          * @type String
7302          */
7303         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7304         /**
7305          * The keystroke filter mask to be applied on email input
7306          * @type RegExp
7307          */
7308         'emailMask' : /[a-z0-9_\.\-@]/i,
7309
7310         /**
7311          * The function used to validate URLs
7312          * @param {String} value The URL
7313          */
7314         'url' : function(v){
7315             return url.test(v);
7316         },
7317         /**
7318          * The error text to display when the url validation function returns false
7319          * @type String
7320          */
7321         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7322         
7323         /**
7324          * The function used to validate alpha values
7325          * @param {String} value The value
7326          */
7327         'alpha' : function(v){
7328             return alpha.test(v);
7329         },
7330         /**
7331          * The error text to display when the alpha validation function returns false
7332          * @type String
7333          */
7334         'alphaText' : 'This field should only contain letters and _',
7335         /**
7336          * The keystroke filter mask to be applied on alpha input
7337          * @type RegExp
7338          */
7339         'alphaMask' : /[a-z_]/i,
7340
7341         /**
7342          * The function used to validate alphanumeric values
7343          * @param {String} value The value
7344          */
7345         'alphanum' : function(v){
7346             return alphanum.test(v);
7347         },
7348         /**
7349          * The error text to display when the alphanumeric validation function returns false
7350          * @type String
7351          */
7352         'alphanumText' : 'This field should only contain letters, numbers and _',
7353         /**
7354          * The keystroke filter mask to be applied on alphanumeric input
7355          * @type RegExp
7356          */
7357         'alphanumMask' : /[a-z0-9_]/i
7358     };
7359 }();/*
7360  * - LGPL
7361  *
7362  * Input
7363  * 
7364  */
7365
7366 /**
7367  * @class Roo.bootstrap.Input
7368  * @extends Roo.bootstrap.Component
7369  * Bootstrap Input class
7370  * @cfg {Boolean} disabled is it disabled
7371  * @cfg {String} fieldLabel - the label associated
7372  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7373  * @cfg {String} name name of the input
7374  * @cfg {string} fieldLabel - the label associated
7375  * @cfg {string}  inputType - input / file submit ...
7376  * @cfg {string} placeholder - placeholder to put in text.
7377  * @cfg {string}  before - input group add on before
7378  * @cfg {string} after - input group add on after
7379  * @cfg {string} size - (lg|sm) or leave empty..
7380  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7381  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7382  * @cfg {Number} md colspan out of 12 for computer-sized screens
7383  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7384  * @cfg {string} value default value of the input
7385  * @cfg {Number} labelWidth set the width of label (0-12)
7386  * @cfg {String} labelAlign (top|left)
7387  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7388  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7389
7390  * @cfg {String} align (left|center|right) Default left
7391  * 
7392  * 
7393  * 
7394  * @constructor
7395  * Create a new Input
7396  * @param {Object} config The config object
7397  */
7398
7399 Roo.bootstrap.Input = function(config){
7400     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7401    
7402         this.addEvents({
7403             /**
7404              * @event focus
7405              * Fires when this field receives input focus.
7406              * @param {Roo.form.Field} this
7407              */
7408             focus : true,
7409             /**
7410              * @event blur
7411              * Fires when this field loses input focus.
7412              * @param {Roo.form.Field} this
7413              */
7414             blur : true,
7415             /**
7416              * @event specialkey
7417              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7418              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7419              * @param {Roo.form.Field} this
7420              * @param {Roo.EventObject} e The event object
7421              */
7422             specialkey : true,
7423             /**
7424              * @event change
7425              * Fires just before the field blurs if the field value has changed.
7426              * @param {Roo.form.Field} this
7427              * @param {Mixed} newValue The new value
7428              * @param {Mixed} oldValue The original value
7429              */
7430             change : true,
7431             /**
7432              * @event invalid
7433              * Fires after the field has been marked as invalid.
7434              * @param {Roo.form.Field} this
7435              * @param {String} msg The validation message
7436              */
7437             invalid : true,
7438             /**
7439              * @event valid
7440              * Fires after the field has been validated with no errors.
7441              * @param {Roo.form.Field} this
7442              */
7443             valid : true,
7444              /**
7445              * @event keyup
7446              * Fires after the key up
7447              * @param {Roo.form.Field} this
7448              * @param {Roo.EventObject}  e The event Object
7449              */
7450             keyup : true
7451         });
7452 };
7453
7454 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7455      /**
7456      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7457       automatic validation (defaults to "keyup").
7458      */
7459     validationEvent : "keyup",
7460      /**
7461      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7462      */
7463     validateOnBlur : true,
7464     /**
7465      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7466      */
7467     validationDelay : 250,
7468      /**
7469      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7470      */
7471     focusClass : "x-form-focus",  // not needed???
7472     
7473        
7474     /**
7475      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7476      */
7477     invalidClass : "has-warning",
7478     
7479     /**
7480      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7481      */
7482     validClass : "has-success",
7483     
7484     /**
7485      * @cfg {Boolean} hasFeedback (true|false) default true
7486      */
7487     hasFeedback : true,
7488     
7489     /**
7490      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7491      */
7492     invalidFeedbackClass : "glyphicon-warning-sign",
7493     
7494     /**
7495      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7496      */
7497     validFeedbackClass : "glyphicon-ok",
7498     
7499     /**
7500      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7501      */
7502     selectOnFocus : false,
7503     
7504      /**
7505      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7506      */
7507     maskRe : null,
7508        /**
7509      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7510      */
7511     vtype : null,
7512     
7513       /**
7514      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7515      */
7516     disableKeyFilter : false,
7517     
7518        /**
7519      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7520      */
7521     disabled : false,
7522      /**
7523      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7524      */
7525     allowBlank : true,
7526     /**
7527      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7528      */
7529     blankText : "This field is required",
7530     
7531      /**
7532      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7533      */
7534     minLength : 0,
7535     /**
7536      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7537      */
7538     maxLength : Number.MAX_VALUE,
7539     /**
7540      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7541      */
7542     minLengthText : "The minimum length for this field is {0}",
7543     /**
7544      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7545      */
7546     maxLengthText : "The maximum length for this field is {0}",
7547   
7548     
7549     /**
7550      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7551      * If available, this function will be called only after the basic validators all return true, and will be passed the
7552      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7553      */
7554     validator : null,
7555     /**
7556      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7557      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7558      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7559      */
7560     regex : null,
7561     /**
7562      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7563      */
7564     regexText : "",
7565     
7566     autocomplete: false,
7567     
7568     
7569     fieldLabel : '',
7570     inputType : 'text',
7571     
7572     name : false,
7573     placeholder: false,
7574     before : false,
7575     after : false,
7576     size : false,
7577     hasFocus : false,
7578     preventMark: false,
7579     isFormField : true,
7580     value : '',
7581     labelWidth : 2,
7582     labelAlign : false,
7583     readOnly : false,
7584     align : false,
7585     formatedValue : false,
7586     
7587     parentLabelAlign : function()
7588     {
7589         var parent = this;
7590         while (parent.parent()) {
7591             parent = parent.parent();
7592             if (typeof(parent.labelAlign) !='undefined') {
7593                 return parent.labelAlign;
7594             }
7595         }
7596         return 'left';
7597         
7598     },
7599     
7600     getAutoCreate : function(){
7601         
7602         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7603         
7604         var id = Roo.id();
7605         
7606         var cfg = {};
7607         
7608         if(this.inputType != 'hidden'){
7609             cfg.cls = 'form-group' //input-group
7610         }
7611         
7612         var input =  {
7613             tag: 'input',
7614             id : id,
7615             type : this.inputType,
7616             value : this.value,
7617             cls : 'form-control',
7618             placeholder : this.placeholder || '',
7619             autocomplete : this.autocomplete || 'new-password'
7620         };
7621         
7622         
7623         if(this.align){
7624             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7625         }
7626         
7627         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7628             input.maxLength = this.maxLength;
7629         }
7630         
7631         if (this.disabled) {
7632             input.disabled=true;
7633         }
7634         
7635         if (this.readOnly) {
7636             input.readonly=true;
7637         }
7638         
7639         if (this.name) {
7640             input.name = this.name;
7641         }
7642         if (this.size) {
7643             input.cls += ' input-' + this.size;
7644         }
7645         var settings=this;
7646         ['xs','sm','md','lg'].map(function(size){
7647             if (settings[size]) {
7648                 cfg.cls += ' col-' + size + '-' + settings[size];
7649             }
7650         });
7651         
7652         var inputblock = input;
7653         
7654         var feedback = {
7655             tag: 'span',
7656             cls: 'glyphicon form-control-feedback'
7657         };
7658             
7659         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7660             
7661             inputblock = {
7662                 cls : 'has-feedback',
7663                 cn :  [
7664                     input,
7665                     feedback
7666                 ] 
7667             };  
7668         }
7669         
7670         if (this.before || this.after) {
7671             
7672             inputblock = {
7673                 cls : 'input-group',
7674                 cn :  [] 
7675             };
7676             
7677             if (this.before && typeof(this.before) == 'string') {
7678                 
7679                 inputblock.cn.push({
7680                     tag :'span',
7681                     cls : 'roo-input-before input-group-addon',
7682                     html : this.before
7683                 });
7684             }
7685             if (this.before && typeof(this.before) == 'object') {
7686                 this.before = Roo.factory(this.before);
7687                 Roo.log(this.before);
7688                 inputblock.cn.push({
7689                     tag :'span',
7690                     cls : 'roo-input-before input-group-' +
7691                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7692                 });
7693             }
7694             
7695             inputblock.cn.push(input);
7696             
7697             if (this.after && typeof(this.after) == 'string') {
7698                 inputblock.cn.push({
7699                     tag :'span',
7700                     cls : 'roo-input-after input-group-addon',
7701                     html : this.after
7702                 });
7703             }
7704             if (this.after && typeof(this.after) == 'object') {
7705                 this.after = Roo.factory(this.after);
7706                 Roo.log(this.after);
7707                 inputblock.cn.push({
7708                     tag :'span',
7709                     cls : 'roo-input-after input-group-' +
7710                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7711                 });
7712             }
7713             
7714             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7715                 inputblock.cls += ' has-feedback';
7716                 inputblock.cn.push(feedback);
7717             }
7718         };
7719         
7720         if (align ==='left' && this.fieldLabel.length) {
7721                 Roo.log("left and has label");
7722                 cfg.cn = [
7723                     
7724                     {
7725                         tag: 'label',
7726                         'for' :  id,
7727                         cls : 'control-label col-sm-' + this.labelWidth,
7728                         html : this.fieldLabel
7729                         
7730                     },
7731                     {
7732                         cls : "col-sm-" + (12 - this.labelWidth), 
7733                         cn: [
7734                             inputblock
7735                         ]
7736                     }
7737                     
7738                 ];
7739         } else if ( this.fieldLabel.length) {
7740                 Roo.log(" label");
7741                  cfg.cn = [
7742                    
7743                     {
7744                         tag: 'label',
7745                         //cls : 'input-group-addon',
7746                         html : this.fieldLabel
7747                         
7748                     },
7749                     
7750                     inputblock
7751                     
7752                 ];
7753
7754         } else {
7755             
7756                 Roo.log(" no label && no align");
7757                 cfg.cn = [
7758                     
7759                         inputblock
7760                     
7761                 ];
7762                 
7763                 
7764         };
7765         Roo.log('input-parentType: ' + this.parentType);
7766         
7767         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7768            cfg.cls += ' navbar-form';
7769            Roo.log(cfg);
7770         }
7771         
7772         return cfg;
7773         
7774     },
7775     /**
7776      * return the real input element.
7777      */
7778     inputEl: function ()
7779     {
7780         return this.el.select('input.form-control',true).first();
7781     },
7782     
7783     tooltipEl : function()
7784     {
7785         return this.inputEl();
7786     },
7787     
7788     setDisabled : function(v)
7789     {
7790         var i  = this.inputEl().dom;
7791         if (!v) {
7792             i.removeAttribute('disabled');
7793             return;
7794             
7795         }
7796         i.setAttribute('disabled','true');
7797     },
7798     initEvents : function()
7799     {
7800           
7801         this.inputEl().on("keydown" , this.fireKey,  this);
7802         this.inputEl().on("focus", this.onFocus,  this);
7803         this.inputEl().on("blur", this.onBlur,  this);
7804         
7805         this.inputEl().relayEvent('keyup', this);
7806
7807         // reference to original value for reset
7808         this.originalValue = this.getValue();
7809         //Roo.form.TextField.superclass.initEvents.call(this);
7810         if(this.validationEvent == 'keyup'){
7811             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7812             this.inputEl().on('keyup', this.filterValidation, this);
7813         }
7814         else if(this.validationEvent !== false){
7815             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7816         }
7817         
7818         if(this.selectOnFocus){
7819             this.on("focus", this.preFocus, this);
7820             
7821         }
7822         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7823             this.inputEl().on("keypress", this.filterKeys, this);
7824         }
7825        /* if(this.grow){
7826             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7827             this.el.on("click", this.autoSize,  this);
7828         }
7829         */
7830         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7831             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7832         }
7833         
7834         if (typeof(this.before) == 'object') {
7835             this.before.render(this.el.select('.roo-input-before',true).first());
7836         }
7837         if (typeof(this.after) == 'object') {
7838             this.after.render(this.el.select('.roo-input-after',true).first());
7839         }
7840         
7841         
7842     },
7843     filterValidation : function(e){
7844         if(!e.isNavKeyPress()){
7845             this.validationTask.delay(this.validationDelay);
7846         }
7847     },
7848      /**
7849      * Validates the field value
7850      * @return {Boolean} True if the value is valid, else false
7851      */
7852     validate : function(){
7853         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7854         if(this.disabled || this.validateValue(this.getRawValue())){
7855             this.markValid();
7856             return true;
7857         }
7858         
7859         this.markInvalid();
7860         return false;
7861     },
7862     
7863     
7864     /**
7865      * Validates a value according to the field's validation rules and marks the field as invalid
7866      * if the validation fails
7867      * @param {Mixed} value The value to validate
7868      * @return {Boolean} True if the value is valid, else false
7869      */
7870     validateValue : function(value){
7871         if(value.length < 1)  { // if it's blank
7872             if(this.allowBlank){
7873                 return true;
7874             }
7875             return false;
7876         }
7877         
7878         if(value.length < this.minLength){
7879             return false;
7880         }
7881         if(value.length > this.maxLength){
7882             return false;
7883         }
7884         if(this.vtype){
7885             var vt = Roo.form.VTypes;
7886             if(!vt[this.vtype](value, this)){
7887                 return false;
7888             }
7889         }
7890         if(typeof this.validator == "function"){
7891             var msg = this.validator(value);
7892             if(msg !== true){
7893                 return false;
7894             }
7895         }
7896         
7897         if(this.regex && !this.regex.test(value)){
7898             return false;
7899         }
7900         
7901         return true;
7902     },
7903
7904     
7905     
7906      // private
7907     fireKey : function(e){
7908         //Roo.log('field ' + e.getKey());
7909         if(e.isNavKeyPress()){
7910             this.fireEvent("specialkey", this, e);
7911         }
7912     },
7913     focus : function (selectText){
7914         if(this.rendered){
7915             this.inputEl().focus();
7916             if(selectText === true){
7917                 this.inputEl().dom.select();
7918             }
7919         }
7920         return this;
7921     } ,
7922     
7923     onFocus : function(){
7924         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7925            // this.el.addClass(this.focusClass);
7926         }
7927         if(!this.hasFocus){
7928             this.hasFocus = true;
7929             this.startValue = this.getValue();
7930             this.fireEvent("focus", this);
7931         }
7932     },
7933     
7934     beforeBlur : Roo.emptyFn,
7935
7936     
7937     // private
7938     onBlur : function(){
7939         this.beforeBlur();
7940         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7941             //this.el.removeClass(this.focusClass);
7942         }
7943         this.hasFocus = false;
7944         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7945             this.validate();
7946         }
7947         var v = this.getValue();
7948         if(String(v) !== String(this.startValue)){
7949             this.fireEvent('change', this, v, this.startValue);
7950         }
7951         this.fireEvent("blur", this);
7952     },
7953     
7954     /**
7955      * Resets the current field value to the originally loaded value and clears any validation messages
7956      */
7957     reset : function(){
7958         this.setValue(this.originalValue);
7959         this.validate();
7960     },
7961      /**
7962      * Returns the name of the field
7963      * @return {Mixed} name The name field
7964      */
7965     getName: function(){
7966         return this.name;
7967     },
7968      /**
7969      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7970      * @return {Mixed} value The field value
7971      */
7972     getValue : function(){
7973         
7974         var v = this.inputEl().getValue();
7975         
7976         return v;
7977     },
7978     /**
7979      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7980      * @return {Mixed} value The field value
7981      */
7982     getRawValue : function(){
7983         var v = this.inputEl().getValue();
7984         
7985         return v;
7986     },
7987     
7988     /**
7989      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7990      * @param {Mixed} value The value to set
7991      */
7992     setRawValue : function(v){
7993         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7994     },
7995     
7996     selectText : function(start, end){
7997         var v = this.getRawValue();
7998         if(v.length > 0){
7999             start = start === undefined ? 0 : start;
8000             end = end === undefined ? v.length : end;
8001             var d = this.inputEl().dom;
8002             if(d.setSelectionRange){
8003                 d.setSelectionRange(start, end);
8004             }else if(d.createTextRange){
8005                 var range = d.createTextRange();
8006                 range.moveStart("character", start);
8007                 range.moveEnd("character", v.length-end);
8008                 range.select();
8009             }
8010         }
8011     },
8012     
8013     /**
8014      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8015      * @param {Mixed} value The value to set
8016      */
8017     setValue : function(v){
8018         this.value = v;
8019         if(this.rendered){
8020             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8021             this.validate();
8022         }
8023     },
8024     
8025     /*
8026     processValue : function(value){
8027         if(this.stripCharsRe){
8028             var newValue = value.replace(this.stripCharsRe, '');
8029             if(newValue !== value){
8030                 this.setRawValue(newValue);
8031                 return newValue;
8032             }
8033         }
8034         return value;
8035     },
8036   */
8037     preFocus : function(){
8038         
8039         if(this.selectOnFocus){
8040             this.inputEl().dom.select();
8041         }
8042     },
8043     filterKeys : function(e){
8044         var k = e.getKey();
8045         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8046             return;
8047         }
8048         var c = e.getCharCode(), cc = String.fromCharCode(c);
8049         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8050             return;
8051         }
8052         if(!this.maskRe.test(cc)){
8053             e.stopEvent();
8054         }
8055     },
8056      /**
8057      * Clear any invalid styles/messages for this field
8058      */
8059     clearInvalid : function(){
8060         
8061         if(!this.el || this.preventMark){ // not rendered
8062             return;
8063         }
8064         this.el.removeClass(this.invalidClass);
8065         
8066         this.fireEvent('valid', this);
8067     },
8068     
8069      /**
8070      * Mark this field as valid
8071      */
8072     markValid : function(){
8073         if(!this.el  || this.preventMark){ // not rendered
8074             return;
8075         }
8076         
8077         this.el.removeClass([this.invalidClass, this.validClass]);
8078         
8079         if(this.disabled || this.allowBlank){
8080             return;
8081         }
8082         
8083         this.el.addClass(this.validClass);
8084         
8085         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8086             
8087             var feedback = this.el.select('.form-control-feedback', true).first();
8088             
8089             if(feedback){
8090                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8091                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8092             }
8093             
8094         }
8095         
8096         this.fireEvent('valid', this);
8097     },
8098     
8099      /**
8100      * Mark this field as invalid
8101      * @param {String} msg The validation message
8102      */
8103     markInvalid : function(msg){
8104         if(!this.el  || this.preventMark){ // not rendered
8105             return;
8106         }
8107         
8108         this.el.removeClass([this.invalidClass, this.validClass]);
8109         
8110         if(this.disabled || this.allowBlank){
8111             return;
8112         }
8113         
8114         this.el.addClass(this.invalidClass);
8115         
8116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8117             
8118             var feedback = this.el.select('.form-control-feedback', true).first();
8119             
8120             if(feedback){
8121                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8122                 
8123                 if(this.getValue().length){
8124                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8125                 }
8126                 
8127             }
8128             
8129         }
8130         
8131         this.fireEvent('invalid', this, msg);
8132     },
8133     // private
8134     SafariOnKeyDown : function(event)
8135     {
8136         // this is a workaround for a password hang bug on chrome/ webkit.
8137         
8138         var isSelectAll = false;
8139         
8140         if(this.inputEl().dom.selectionEnd > 0){
8141             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8142         }
8143         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8144             event.preventDefault();
8145             this.setValue('');
8146             return;
8147         }
8148         
8149         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8150             
8151             event.preventDefault();
8152             // this is very hacky as keydown always get's upper case.
8153             //
8154             var cc = String.fromCharCode(event.getCharCode());
8155             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8156             
8157         }
8158     },
8159     adjustWidth : function(tag, w){
8160         tag = tag.toLowerCase();
8161         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8162             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8163                 if(tag == 'input'){
8164                     return w + 2;
8165                 }
8166                 if(tag == 'textarea'){
8167                     return w-2;
8168                 }
8169             }else if(Roo.isOpera){
8170                 if(tag == 'input'){
8171                     return w + 2;
8172                 }
8173                 if(tag == 'textarea'){
8174                     return w-2;
8175                 }
8176             }
8177         }
8178         return w;
8179     }
8180     
8181 });
8182
8183  
8184 /*
8185  * - LGPL
8186  *
8187  * Input
8188  * 
8189  */
8190
8191 /**
8192  * @class Roo.bootstrap.TextArea
8193  * @extends Roo.bootstrap.Input
8194  * Bootstrap TextArea class
8195  * @cfg {Number} cols Specifies the visible width of a text area
8196  * @cfg {Number} rows Specifies the visible number of lines in a text area
8197  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8198  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8199  * @cfg {string} html text
8200  * 
8201  * @constructor
8202  * Create a new TextArea
8203  * @param {Object} config The config object
8204  */
8205
8206 Roo.bootstrap.TextArea = function(config){
8207     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8208    
8209 };
8210
8211 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8212      
8213     cols : false,
8214     rows : 5,
8215     readOnly : false,
8216     warp : 'soft',
8217     resize : false,
8218     value: false,
8219     html: false,
8220     
8221     getAutoCreate : function(){
8222         
8223         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8224         
8225         var id = Roo.id();
8226         
8227         var cfg = {};
8228         
8229         var input =  {
8230             tag: 'textarea',
8231             id : id,
8232             warp : this.warp,
8233             rows : this.rows,
8234             value : this.value || '',
8235             html: this.html || '',
8236             cls : 'form-control',
8237             placeholder : this.placeholder || '' 
8238             
8239         };
8240         
8241         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8242             input.maxLength = this.maxLength;
8243         }
8244         
8245         if(this.resize){
8246             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8247         }
8248         
8249         if(this.cols){
8250             input.cols = this.cols;
8251         }
8252         
8253         if (this.readOnly) {
8254             input.readonly = true;
8255         }
8256         
8257         if (this.name) {
8258             input.name = this.name;
8259         }
8260         
8261         if (this.size) {
8262             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8263         }
8264         
8265         var settings=this;
8266         ['xs','sm','md','lg'].map(function(size){
8267             if (settings[size]) {
8268                 cfg.cls += ' col-' + size + '-' + settings[size];
8269             }
8270         });
8271         
8272         var inputblock = input;
8273         
8274         if(this.hasFeedback && !this.allowBlank){
8275             
8276             var feedback = {
8277                 tag: 'span',
8278                 cls: 'glyphicon form-control-feedback'
8279             };
8280
8281             inputblock = {
8282                 cls : 'has-feedback',
8283                 cn :  [
8284                     input,
8285                     feedback
8286                 ] 
8287             };  
8288         }
8289         
8290         
8291         if (this.before || this.after) {
8292             
8293             inputblock = {
8294                 cls : 'input-group',
8295                 cn :  [] 
8296             };
8297             if (this.before) {
8298                 inputblock.cn.push({
8299                     tag :'span',
8300                     cls : 'input-group-addon',
8301                     html : this.before
8302                 });
8303             }
8304             
8305             inputblock.cn.push(input);
8306             
8307             if(this.hasFeedback && !this.allowBlank){
8308                 inputblock.cls += ' has-feedback';
8309                 inputblock.cn.push(feedback);
8310             }
8311             
8312             if (this.after) {
8313                 inputblock.cn.push({
8314                     tag :'span',
8315                     cls : 'input-group-addon',
8316                     html : this.after
8317                 });
8318             }
8319             
8320         }
8321         
8322         if (align ==='left' && this.fieldLabel.length) {
8323                 Roo.log("left and has label");
8324                 cfg.cn = [
8325                     
8326                     {
8327                         tag: 'label',
8328                         'for' :  id,
8329                         cls : 'control-label col-sm-' + this.labelWidth,
8330                         html : this.fieldLabel
8331                         
8332                     },
8333                     {
8334                         cls : "col-sm-" + (12 - this.labelWidth), 
8335                         cn: [
8336                             inputblock
8337                         ]
8338                     }
8339                     
8340                 ];
8341         } else if ( this.fieldLabel.length) {
8342                 Roo.log(" label");
8343                  cfg.cn = [
8344                    
8345                     {
8346                         tag: 'label',
8347                         //cls : 'input-group-addon',
8348                         html : this.fieldLabel
8349                         
8350                     },
8351                     
8352                     inputblock
8353                     
8354                 ];
8355
8356         } else {
8357             
8358                    Roo.log(" no label && no align");
8359                 cfg.cn = [
8360                     
8361                         inputblock
8362                     
8363                 ];
8364                 
8365                 
8366         }
8367         
8368         if (this.disabled) {
8369             input.disabled=true;
8370         }
8371         
8372         return cfg;
8373         
8374     },
8375     /**
8376      * return the real textarea element.
8377      */
8378     inputEl: function ()
8379     {
8380         return this.el.select('textarea.form-control',true).first();
8381     }
8382 });
8383
8384  
8385 /*
8386  * - LGPL
8387  *
8388  * trigger field - base class for combo..
8389  * 
8390  */
8391  
8392 /**
8393  * @class Roo.bootstrap.TriggerField
8394  * @extends Roo.bootstrap.Input
8395  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8396  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8397  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8398  * for which you can provide a custom implementation.  For example:
8399  * <pre><code>
8400 var trigger = new Roo.bootstrap.TriggerField();
8401 trigger.onTriggerClick = myTriggerFn;
8402 trigger.applyTo('my-field');
8403 </code></pre>
8404  *
8405  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8406  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8407  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8408  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8409  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8410
8411  * @constructor
8412  * Create a new TriggerField.
8413  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8414  * to the base TextField)
8415  */
8416 Roo.bootstrap.TriggerField = function(config){
8417     this.mimicing = false;
8418     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8419 };
8420
8421 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8422     /**
8423      * @cfg {String} triggerClass A CSS class to apply to the trigger
8424      */
8425      /**
8426      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8427      */
8428     hideTrigger:false,
8429
8430     /** @cfg {Boolean} grow @hide */
8431     /** @cfg {Number} growMin @hide */
8432     /** @cfg {Number} growMax @hide */
8433
8434     /**
8435      * @hide 
8436      * @method
8437      */
8438     autoSize: Roo.emptyFn,
8439     // private
8440     monitorTab : true,
8441     // private
8442     deferHeight : true,
8443
8444     
8445     actionMode : 'wrap',
8446     
8447     caret : false,
8448     
8449     
8450     getAutoCreate : function(){
8451        
8452         var align = this.labelAlign || this.parentLabelAlign();
8453         
8454         var id = Roo.id();
8455         
8456         var cfg = {
8457             cls: 'form-group' //input-group
8458         };
8459         
8460         
8461         var input =  {
8462             tag: 'input',
8463             id : id,
8464             type : this.inputType,
8465             cls : 'form-control',
8466             autocomplete: 'new-password',
8467             placeholder : this.placeholder || '' 
8468             
8469         };
8470         if (this.name) {
8471             input.name = this.name;
8472         }
8473         if (this.size) {
8474             input.cls += ' input-' + this.size;
8475         }
8476         
8477         if (this.disabled) {
8478             input.disabled=true;
8479         }
8480         
8481         var inputblock = input;
8482         
8483         if(this.hasFeedback && !this.allowBlank){
8484             
8485             var feedback = {
8486                 tag: 'span',
8487                 cls: 'glyphicon form-control-feedback'
8488             };
8489
8490             inputblock = {
8491                 cls : 'has-feedback',
8492                 cn :  [
8493                     input,
8494                     feedback
8495                 ] 
8496             };  
8497         }
8498         
8499         if (this.before || this.after) {
8500             
8501             inputblock = {
8502                 cls : 'input-group',
8503                 cn :  [] 
8504             };
8505             if (this.before) {
8506                 inputblock.cn.push({
8507                     tag :'span',
8508                     cls : 'input-group-addon',
8509                     html : this.before
8510                 });
8511             }
8512             
8513             inputblock.cn.push(input);
8514             
8515             if(this.hasFeedback && !this.allowBlank){
8516                 inputblock.cls += ' has-feedback';
8517                 inputblock.cn.push(feedback);
8518             }
8519             
8520             if (this.after) {
8521                 inputblock.cn.push({
8522                     tag :'span',
8523                     cls : 'input-group-addon',
8524                     html : this.after
8525                 });
8526             }
8527             
8528         };
8529         
8530         var box = {
8531             tag: 'div',
8532             cn: [
8533                 {
8534                     tag: 'input',
8535                     type : 'hidden',
8536                     cls: 'form-hidden-field'
8537                 },
8538                 inputblock
8539             ]
8540             
8541         };
8542         
8543         if(this.multiple){
8544             Roo.log('multiple');
8545             
8546             box = {
8547                 tag: 'div',
8548                 cn: [
8549                     {
8550                         tag: 'input',
8551                         type : 'hidden',
8552                         cls: 'form-hidden-field'
8553                     },
8554                     {
8555                         tag: 'ul',
8556                         cls: 'select2-choices',
8557                         cn:[
8558                             {
8559                                 tag: 'li',
8560                                 cls: 'select2-search-field',
8561                                 cn: [
8562
8563                                     inputblock
8564                                 ]
8565                             }
8566                         ]
8567                     }
8568                 ]
8569             }
8570         };
8571         
8572         var combobox = {
8573             cls: 'select2-container input-group',
8574             cn: [
8575                 box
8576 //                {
8577 //                    tag: 'ul',
8578 //                    cls: 'typeahead typeahead-long dropdown-menu',
8579 //                    style: 'display:none'
8580 //                }
8581             ]
8582         };
8583         
8584         if(!this.multiple && this.showToggleBtn){
8585             
8586             var caret = {
8587                         tag: 'span',
8588                         cls: 'caret'
8589              };
8590             if (this.caret != false) {
8591                 caret = {
8592                      tag: 'i',
8593                      cls: 'fa fa-' + this.caret
8594                 };
8595                 
8596             }
8597             
8598             combobox.cn.push({
8599                 tag :'span',
8600                 cls : 'input-group-addon btn dropdown-toggle',
8601                 cn : [
8602                     caret,
8603                     {
8604                         tag: 'span',
8605                         cls: 'combobox-clear',
8606                         cn  : [
8607                             {
8608                                 tag : 'i',
8609                                 cls: 'icon-remove'
8610                             }
8611                         ]
8612                     }
8613                 ]
8614
8615             })
8616         }
8617         
8618         if(this.multiple){
8619             combobox.cls += ' select2-container-multi';
8620         }
8621         
8622         if (align ==='left' && this.fieldLabel.length) {
8623             
8624                 Roo.log("left and has label");
8625                 cfg.cn = [
8626                     
8627                     {
8628                         tag: 'label',
8629                         'for' :  id,
8630                         cls : 'control-label col-sm-' + this.labelWidth,
8631                         html : this.fieldLabel
8632                         
8633                     },
8634                     {
8635                         cls : "col-sm-" + (12 - this.labelWidth), 
8636                         cn: [
8637                             combobox
8638                         ]
8639                     }
8640                     
8641                 ];
8642         } else if ( this.fieldLabel.length) {
8643                 Roo.log(" label");
8644                  cfg.cn = [
8645                    
8646                     {
8647                         tag: 'label',
8648                         //cls : 'input-group-addon',
8649                         html : this.fieldLabel
8650                         
8651                     },
8652                     
8653                     combobox
8654                     
8655                 ];
8656
8657         } else {
8658             
8659                 Roo.log(" no label && no align");
8660                 cfg = combobox
8661                      
8662                 
8663         }
8664          
8665         var settings=this;
8666         ['xs','sm','md','lg'].map(function(size){
8667             if (settings[size]) {
8668                 cfg.cls += ' col-' + size + '-' + settings[size];
8669             }
8670         });
8671         
8672         return cfg;
8673         
8674     },
8675     
8676     
8677     
8678     // private
8679     onResize : function(w, h){
8680 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8681 //        if(typeof w == 'number'){
8682 //            var x = w - this.trigger.getWidth();
8683 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8684 //            this.trigger.setStyle('left', x+'px');
8685 //        }
8686     },
8687
8688     // private
8689     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8690
8691     // private
8692     getResizeEl : function(){
8693         return this.inputEl();
8694     },
8695
8696     // private
8697     getPositionEl : function(){
8698         return this.inputEl();
8699     },
8700
8701     // private
8702     alignErrorIcon : function(){
8703         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8704     },
8705
8706     // private
8707     initEvents : function(){
8708         
8709         this.createList();
8710         
8711         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8712         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8713         if(!this.multiple && this.showToggleBtn){
8714             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8715             if(this.hideTrigger){
8716                 this.trigger.setDisplayed(false);
8717             }
8718             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8719         }
8720         
8721         if(this.multiple){
8722             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8723         }
8724         
8725         //this.trigger.addClassOnOver('x-form-trigger-over');
8726         //this.trigger.addClassOnClick('x-form-trigger-click');
8727         
8728         //if(!this.width){
8729         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8730         //}
8731     },
8732     
8733     createList : function()
8734     {
8735         this.list = Roo.get(document.body).createChild({
8736             tag: 'ul',
8737             cls: 'typeahead typeahead-long dropdown-menu',
8738             style: 'display:none'
8739         });
8740         
8741         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8742         
8743     },
8744
8745     // private
8746     initTrigger : function(){
8747        
8748     },
8749
8750     // private
8751     onDestroy : function(){
8752         if(this.trigger){
8753             this.trigger.removeAllListeners();
8754           //  this.trigger.remove();
8755         }
8756         //if(this.wrap){
8757         //    this.wrap.remove();
8758         //}
8759         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8760     },
8761
8762     // private
8763     onFocus : function(){
8764         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8765         /*
8766         if(!this.mimicing){
8767             this.wrap.addClass('x-trigger-wrap-focus');
8768             this.mimicing = true;
8769             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8770             if(this.monitorTab){
8771                 this.el.on("keydown", this.checkTab, this);
8772             }
8773         }
8774         */
8775     },
8776
8777     // private
8778     checkTab : function(e){
8779         if(e.getKey() == e.TAB){
8780             this.triggerBlur();
8781         }
8782     },
8783
8784     // private
8785     onBlur : function(){
8786         // do nothing
8787     },
8788
8789     // private
8790     mimicBlur : function(e, t){
8791         /*
8792         if(!this.wrap.contains(t) && this.validateBlur()){
8793             this.triggerBlur();
8794         }
8795         */
8796     },
8797
8798     // private
8799     triggerBlur : function(){
8800         this.mimicing = false;
8801         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8802         if(this.monitorTab){
8803             this.el.un("keydown", this.checkTab, this);
8804         }
8805         //this.wrap.removeClass('x-trigger-wrap-focus');
8806         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8807     },
8808
8809     // private
8810     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8811     validateBlur : function(e, t){
8812         return true;
8813     },
8814
8815     // private
8816     onDisable : function(){
8817         this.inputEl().dom.disabled = true;
8818         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8819         //if(this.wrap){
8820         //    this.wrap.addClass('x-item-disabled');
8821         //}
8822     },
8823
8824     // private
8825     onEnable : function(){
8826         this.inputEl().dom.disabled = false;
8827         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8828         //if(this.wrap){
8829         //    this.el.removeClass('x-item-disabled');
8830         //}
8831     },
8832
8833     // private
8834     onShow : function(){
8835         var ae = this.getActionEl();
8836         
8837         if(ae){
8838             ae.dom.style.display = '';
8839             ae.dom.style.visibility = 'visible';
8840         }
8841     },
8842
8843     // private
8844     
8845     onHide : function(){
8846         var ae = this.getActionEl();
8847         ae.dom.style.display = 'none';
8848     },
8849
8850     /**
8851      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8852      * by an implementing function.
8853      * @method
8854      * @param {EventObject} e
8855      */
8856     onTriggerClick : Roo.emptyFn
8857 });
8858  /*
8859  * Based on:
8860  * Ext JS Library 1.1.1
8861  * Copyright(c) 2006-2007, Ext JS, LLC.
8862  *
8863  * Originally Released Under LGPL - original licence link has changed is not relivant.
8864  *
8865  * Fork - LGPL
8866  * <script type="text/javascript">
8867  */
8868
8869
8870 /**
8871  * @class Roo.data.SortTypes
8872  * @singleton
8873  * Defines the default sorting (casting?) comparison functions used when sorting data.
8874  */
8875 Roo.data.SortTypes = {
8876     /**
8877      * Default sort that does nothing
8878      * @param {Mixed} s The value being converted
8879      * @return {Mixed} The comparison value
8880      */
8881     none : function(s){
8882         return s;
8883     },
8884     
8885     /**
8886      * The regular expression used to strip tags
8887      * @type {RegExp}
8888      * @property
8889      */
8890     stripTagsRE : /<\/?[^>]+>/gi,
8891     
8892     /**
8893      * Strips all HTML tags to sort on text only
8894      * @param {Mixed} s The value being converted
8895      * @return {String} The comparison value
8896      */
8897     asText : function(s){
8898         return String(s).replace(this.stripTagsRE, "");
8899     },
8900     
8901     /**
8902      * Strips all HTML tags to sort on text only - Case insensitive
8903      * @param {Mixed} s The value being converted
8904      * @return {String} The comparison value
8905      */
8906     asUCText : function(s){
8907         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8908     },
8909     
8910     /**
8911      * Case insensitive string
8912      * @param {Mixed} s The value being converted
8913      * @return {String} The comparison value
8914      */
8915     asUCString : function(s) {
8916         return String(s).toUpperCase();
8917     },
8918     
8919     /**
8920      * Date sorting
8921      * @param {Mixed} s The value being converted
8922      * @return {Number} The comparison value
8923      */
8924     asDate : function(s) {
8925         if(!s){
8926             return 0;
8927         }
8928         if(s instanceof Date){
8929             return s.getTime();
8930         }
8931         return Date.parse(String(s));
8932     },
8933     
8934     /**
8935      * Float sorting
8936      * @param {Mixed} s The value being converted
8937      * @return {Float} The comparison value
8938      */
8939     asFloat : function(s) {
8940         var val = parseFloat(String(s).replace(/,/g, ""));
8941         if(isNaN(val)) val = 0;
8942         return val;
8943     },
8944     
8945     /**
8946      * Integer sorting
8947      * @param {Mixed} s The value being converted
8948      * @return {Number} The comparison value
8949      */
8950     asInt : function(s) {
8951         var val = parseInt(String(s).replace(/,/g, ""));
8952         if(isNaN(val)) val = 0;
8953         return val;
8954     }
8955 };/*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965
8966 /**
8967 * @class Roo.data.Record
8968  * Instances of this class encapsulate both record <em>definition</em> information, and record
8969  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8970  * to access Records cached in an {@link Roo.data.Store} object.<br>
8971  * <p>
8972  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8973  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8974  * objects.<br>
8975  * <p>
8976  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8977  * @constructor
8978  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8979  * {@link #create}. The parameters are the same.
8980  * @param {Array} data An associative Array of data values keyed by the field name.
8981  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8982  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8983  * not specified an integer id is generated.
8984  */
8985 Roo.data.Record = function(data, id){
8986     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8987     this.data = data;
8988 };
8989
8990 /**
8991  * Generate a constructor for a specific record layout.
8992  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8993  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8994  * Each field definition object may contain the following properties: <ul>
8995  * <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,
8996  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8997  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8998  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8999  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9000  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9001  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9002  * this may be omitted.</p></li>
9003  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9004  * <ul><li>auto (Default, implies no conversion)</li>
9005  * <li>string</li>
9006  * <li>int</li>
9007  * <li>float</li>
9008  * <li>boolean</li>
9009  * <li>date</li></ul></p></li>
9010  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9011  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9012  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9013  * by the Reader into an object that will be stored in the Record. It is passed the
9014  * following parameters:<ul>
9015  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9016  * </ul></p></li>
9017  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9018  * </ul>
9019  * <br>usage:<br><pre><code>
9020 var TopicRecord = Roo.data.Record.create(
9021     {name: 'title', mapping: 'topic_title'},
9022     {name: 'author', mapping: 'username'},
9023     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9024     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9025     {name: 'lastPoster', mapping: 'user2'},
9026     {name: 'excerpt', mapping: 'post_text'}
9027 );
9028
9029 var myNewRecord = new TopicRecord({
9030     title: 'Do my job please',
9031     author: 'noobie',
9032     totalPosts: 1,
9033     lastPost: new Date(),
9034     lastPoster: 'Animal',
9035     excerpt: 'No way dude!'
9036 });
9037 myStore.add(myNewRecord);
9038 </code></pre>
9039  * @method create
9040  * @static
9041  */
9042 Roo.data.Record.create = function(o){
9043     var f = function(){
9044         f.superclass.constructor.apply(this, arguments);
9045     };
9046     Roo.extend(f, Roo.data.Record);
9047     var p = f.prototype;
9048     p.fields = new Roo.util.MixedCollection(false, function(field){
9049         return field.name;
9050     });
9051     for(var i = 0, len = o.length; i < len; i++){
9052         p.fields.add(new Roo.data.Field(o[i]));
9053     }
9054     f.getField = function(name){
9055         return p.fields.get(name);  
9056     };
9057     return f;
9058 };
9059
9060 Roo.data.Record.AUTO_ID = 1000;
9061 Roo.data.Record.EDIT = 'edit';
9062 Roo.data.Record.REJECT = 'reject';
9063 Roo.data.Record.COMMIT = 'commit';
9064
9065 Roo.data.Record.prototype = {
9066     /**
9067      * Readonly flag - true if this record has been modified.
9068      * @type Boolean
9069      */
9070     dirty : false,
9071     editing : false,
9072     error: null,
9073     modified: null,
9074
9075     // private
9076     join : function(store){
9077         this.store = store;
9078     },
9079
9080     /**
9081      * Set the named field to the specified value.
9082      * @param {String} name The name of the field to set.
9083      * @param {Object} value The value to set the field to.
9084      */
9085     set : function(name, value){
9086         if(this.data[name] == value){
9087             return;
9088         }
9089         this.dirty = true;
9090         if(!this.modified){
9091             this.modified = {};
9092         }
9093         if(typeof this.modified[name] == 'undefined'){
9094             this.modified[name] = this.data[name];
9095         }
9096         this.data[name] = value;
9097         if(!this.editing && this.store){
9098             this.store.afterEdit(this);
9099         }       
9100     },
9101
9102     /**
9103      * Get the value of the named field.
9104      * @param {String} name The name of the field to get the value of.
9105      * @return {Object} The value of the field.
9106      */
9107     get : function(name){
9108         return this.data[name]; 
9109     },
9110
9111     // private
9112     beginEdit : function(){
9113         this.editing = true;
9114         this.modified = {}; 
9115     },
9116
9117     // private
9118     cancelEdit : function(){
9119         this.editing = false;
9120         delete this.modified;
9121     },
9122
9123     // private
9124     endEdit : function(){
9125         this.editing = false;
9126         if(this.dirty && this.store){
9127             this.store.afterEdit(this);
9128         }
9129     },
9130
9131     /**
9132      * Usually called by the {@link Roo.data.Store} which owns the Record.
9133      * Rejects all changes made to the Record since either creation, or the last commit operation.
9134      * Modified fields are reverted to their original values.
9135      * <p>
9136      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9137      * of reject operations.
9138      */
9139     reject : function(){
9140         var m = this.modified;
9141         for(var n in m){
9142             if(typeof m[n] != "function"){
9143                 this.data[n] = m[n];
9144             }
9145         }
9146         this.dirty = false;
9147         delete this.modified;
9148         this.editing = false;
9149         if(this.store){
9150             this.store.afterReject(this);
9151         }
9152     },
9153
9154     /**
9155      * Usually called by the {@link Roo.data.Store} which owns the Record.
9156      * Commits all changes made to the Record since either creation, or the last commit operation.
9157      * <p>
9158      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9159      * of commit operations.
9160      */
9161     commit : function(){
9162         this.dirty = false;
9163         delete this.modified;
9164         this.editing = false;
9165         if(this.store){
9166             this.store.afterCommit(this);
9167         }
9168     },
9169
9170     // private
9171     hasError : function(){
9172         return this.error != null;
9173     },
9174
9175     // private
9176     clearError : function(){
9177         this.error = null;
9178     },
9179
9180     /**
9181      * Creates a copy of this record.
9182      * @param {String} id (optional) A new record id if you don't want to use this record's id
9183      * @return {Record}
9184      */
9185     copy : function(newId) {
9186         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9187     }
9188 };/*
9189  * Based on:
9190  * Ext JS Library 1.1.1
9191  * Copyright(c) 2006-2007, Ext JS, LLC.
9192  *
9193  * Originally Released Under LGPL - original licence link has changed is not relivant.
9194  *
9195  * Fork - LGPL
9196  * <script type="text/javascript">
9197  */
9198
9199
9200
9201 /**
9202  * @class Roo.data.Store
9203  * @extends Roo.util.Observable
9204  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9205  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9206  * <p>
9207  * 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
9208  * has no knowledge of the format of the data returned by the Proxy.<br>
9209  * <p>
9210  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9211  * instances from the data object. These records are cached and made available through accessor functions.
9212  * @constructor
9213  * Creates a new Store.
9214  * @param {Object} config A config object containing the objects needed for the Store to access data,
9215  * and read the data into Records.
9216  */
9217 Roo.data.Store = function(config){
9218     this.data = new Roo.util.MixedCollection(false);
9219     this.data.getKey = function(o){
9220         return o.id;
9221     };
9222     this.baseParams = {};
9223     // private
9224     this.paramNames = {
9225         "start" : "start",
9226         "limit" : "limit",
9227         "sort" : "sort",
9228         "dir" : "dir",
9229         "multisort" : "_multisort"
9230     };
9231
9232     if(config && config.data){
9233         this.inlineData = config.data;
9234         delete config.data;
9235     }
9236
9237     Roo.apply(this, config);
9238     
9239     if(this.reader){ // reader passed
9240         this.reader = Roo.factory(this.reader, Roo.data);
9241         this.reader.xmodule = this.xmodule || false;
9242         if(!this.recordType){
9243             this.recordType = this.reader.recordType;
9244         }
9245         if(this.reader.onMetaChange){
9246             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9247         }
9248     }
9249
9250     if(this.recordType){
9251         this.fields = this.recordType.prototype.fields;
9252     }
9253     this.modified = [];
9254
9255     this.addEvents({
9256         /**
9257          * @event datachanged
9258          * Fires when the data cache has changed, and a widget which is using this Store
9259          * as a Record cache should refresh its view.
9260          * @param {Store} this
9261          */
9262         datachanged : true,
9263         /**
9264          * @event metachange
9265          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9266          * @param {Store} this
9267          * @param {Object} meta The JSON metadata
9268          */
9269         metachange : true,
9270         /**
9271          * @event add
9272          * Fires when Records have been added to the Store
9273          * @param {Store} this
9274          * @param {Roo.data.Record[]} records The array of Records added
9275          * @param {Number} index The index at which the record(s) were added
9276          */
9277         add : true,
9278         /**
9279          * @event remove
9280          * Fires when a Record has been removed from the Store
9281          * @param {Store} this
9282          * @param {Roo.data.Record} record The Record that was removed
9283          * @param {Number} index The index at which the record was removed
9284          */
9285         remove : true,
9286         /**
9287          * @event update
9288          * Fires when a Record has been updated
9289          * @param {Store} this
9290          * @param {Roo.data.Record} record The Record that was updated
9291          * @param {String} operation The update operation being performed.  Value may be one of:
9292          * <pre><code>
9293  Roo.data.Record.EDIT
9294  Roo.data.Record.REJECT
9295  Roo.data.Record.COMMIT
9296          * </code></pre>
9297          */
9298         update : true,
9299         /**
9300          * @event clear
9301          * Fires when the data cache has been cleared.
9302          * @param {Store} this
9303          */
9304         clear : true,
9305         /**
9306          * @event beforeload
9307          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9308          * the load action will be canceled.
9309          * @param {Store} this
9310          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9311          */
9312         beforeload : true,
9313         /**
9314          * @event beforeloadadd
9315          * Fires after a new set of Records has been loaded.
9316          * @param {Store} this
9317          * @param {Roo.data.Record[]} records The Records that were loaded
9318          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9319          */
9320         beforeloadadd : true,
9321         /**
9322          * @event load
9323          * Fires after a new set of Records has been loaded, before they are added to the store.
9324          * @param {Store} this
9325          * @param {Roo.data.Record[]} records The Records that were loaded
9326          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9327          * @params {Object} return from reader
9328          */
9329         load : true,
9330         /**
9331          * @event loadexception
9332          * Fires if an exception occurs in the Proxy during loading.
9333          * Called with the signature of the Proxy's "loadexception" event.
9334          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9335          * 
9336          * @param {Proxy} 
9337          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9338          * @param {Object} load options 
9339          * @param {Object} jsonData from your request (normally this contains the Exception)
9340          */
9341         loadexception : true
9342     });
9343     
9344     if(this.proxy){
9345         this.proxy = Roo.factory(this.proxy, Roo.data);
9346         this.proxy.xmodule = this.xmodule || false;
9347         this.relayEvents(this.proxy,  ["loadexception"]);
9348     }
9349     this.sortToggle = {};
9350     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9351
9352     Roo.data.Store.superclass.constructor.call(this);
9353
9354     if(this.inlineData){
9355         this.loadData(this.inlineData);
9356         delete this.inlineData;
9357     }
9358 };
9359
9360 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9361      /**
9362     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9363     * without a remote query - used by combo/forms at present.
9364     */
9365     
9366     /**
9367     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9368     */
9369     /**
9370     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9371     */
9372     /**
9373     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9374     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9375     */
9376     /**
9377     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9378     * on any HTTP request
9379     */
9380     /**
9381     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9382     */
9383     /**
9384     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9385     */
9386     multiSort: false,
9387     /**
9388     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9389     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9390     */
9391     remoteSort : false,
9392
9393     /**
9394     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9395      * loaded or when a record is removed. (defaults to false).
9396     */
9397     pruneModifiedRecords : false,
9398
9399     // private
9400     lastOptions : null,
9401
9402     /**
9403      * Add Records to the Store and fires the add event.
9404      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9405      */
9406     add : function(records){
9407         records = [].concat(records);
9408         for(var i = 0, len = records.length; i < len; i++){
9409             records[i].join(this);
9410         }
9411         var index = this.data.length;
9412         this.data.addAll(records);
9413         this.fireEvent("add", this, records, index);
9414     },
9415
9416     /**
9417      * Remove a Record from the Store and fires the remove event.
9418      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9419      */
9420     remove : function(record){
9421         var index = this.data.indexOf(record);
9422         this.data.removeAt(index);
9423         if(this.pruneModifiedRecords){
9424             this.modified.remove(record);
9425         }
9426         this.fireEvent("remove", this, record, index);
9427     },
9428
9429     /**
9430      * Remove all Records from the Store and fires the clear event.
9431      */
9432     removeAll : function(){
9433         this.data.clear();
9434         if(this.pruneModifiedRecords){
9435             this.modified = [];
9436         }
9437         this.fireEvent("clear", this);
9438     },
9439
9440     /**
9441      * Inserts Records to the Store at the given index and fires the add event.
9442      * @param {Number} index The start index at which to insert the passed Records.
9443      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9444      */
9445     insert : function(index, records){
9446         records = [].concat(records);
9447         for(var i = 0, len = records.length; i < len; i++){
9448             this.data.insert(index, records[i]);
9449             records[i].join(this);
9450         }
9451         this.fireEvent("add", this, records, index);
9452     },
9453
9454     /**
9455      * Get the index within the cache of the passed Record.
9456      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9457      * @return {Number} The index of the passed Record. Returns -1 if not found.
9458      */
9459     indexOf : function(record){
9460         return this.data.indexOf(record);
9461     },
9462
9463     /**
9464      * Get the index within the cache of the Record with the passed id.
9465      * @param {String} id The id of the Record to find.
9466      * @return {Number} The index of the Record. Returns -1 if not found.
9467      */
9468     indexOfId : function(id){
9469         return this.data.indexOfKey(id);
9470     },
9471
9472     /**
9473      * Get the Record with the specified id.
9474      * @param {String} id The id of the Record to find.
9475      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9476      */
9477     getById : function(id){
9478         return this.data.key(id);
9479     },
9480
9481     /**
9482      * Get the Record at the specified index.
9483      * @param {Number} index The index of the Record to find.
9484      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9485      */
9486     getAt : function(index){
9487         return this.data.itemAt(index);
9488     },
9489
9490     /**
9491      * Returns a range of Records between specified indices.
9492      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9493      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9494      * @return {Roo.data.Record[]} An array of Records
9495      */
9496     getRange : function(start, end){
9497         return this.data.getRange(start, end);
9498     },
9499
9500     // private
9501     storeOptions : function(o){
9502         o = Roo.apply({}, o);
9503         delete o.callback;
9504         delete o.scope;
9505         this.lastOptions = o;
9506     },
9507
9508     /**
9509      * Loads the Record cache from the configured Proxy using the configured Reader.
9510      * <p>
9511      * If using remote paging, then the first load call must specify the <em>start</em>
9512      * and <em>limit</em> properties in the options.params property to establish the initial
9513      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9514      * <p>
9515      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9516      * and this call will return before the new data has been loaded. Perform any post-processing
9517      * in a callback function, or in a "load" event handler.</strong>
9518      * <p>
9519      * @param {Object} options An object containing properties which control loading options:<ul>
9520      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9521      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9522      * passed the following arguments:<ul>
9523      * <li>r : Roo.data.Record[]</li>
9524      * <li>options: Options object from the load call</li>
9525      * <li>success: Boolean success indicator</li></ul></li>
9526      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9527      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9528      * </ul>
9529      */
9530     load : function(options){
9531         options = options || {};
9532         if(this.fireEvent("beforeload", this, options) !== false){
9533             this.storeOptions(options);
9534             var p = Roo.apply(options.params || {}, this.baseParams);
9535             // if meta was not loaded from remote source.. try requesting it.
9536             if (!this.reader.metaFromRemote) {
9537                 p._requestMeta = 1;
9538             }
9539             if(this.sortInfo && this.remoteSort){
9540                 var pn = this.paramNames;
9541                 p[pn["sort"]] = this.sortInfo.field;
9542                 p[pn["dir"]] = this.sortInfo.direction;
9543             }
9544             if (this.multiSort) {
9545                 var pn = this.paramNames;
9546                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9547             }
9548             
9549             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9550         }
9551     },
9552
9553     /**
9554      * Reloads the Record cache from the configured Proxy using the configured Reader and
9555      * the options from the last load operation performed.
9556      * @param {Object} options (optional) An object containing properties which may override the options
9557      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9558      * the most recently used options are reused).
9559      */
9560     reload : function(options){
9561         this.load(Roo.applyIf(options||{}, this.lastOptions));
9562     },
9563
9564     // private
9565     // Called as a callback by the Reader during a load operation.
9566     loadRecords : function(o, options, success){
9567         if(!o || success === false){
9568             if(success !== false){
9569                 this.fireEvent("load", this, [], options, o);
9570             }
9571             if(options.callback){
9572                 options.callback.call(options.scope || this, [], options, false);
9573             }
9574             return;
9575         }
9576         // if data returned failure - throw an exception.
9577         if (o.success === false) {
9578             // show a message if no listener is registered.
9579             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9580                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9581             }
9582             // loadmask wil be hooked into this..
9583             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9584             return;
9585         }
9586         var r = o.records, t = o.totalRecords || r.length;
9587         
9588         this.fireEvent("beforeloadadd", this, r, options, o);
9589         
9590         if(!options || options.add !== true){
9591             if(this.pruneModifiedRecords){
9592                 this.modified = [];
9593             }
9594             for(var i = 0, len = r.length; i < len; i++){
9595                 r[i].join(this);
9596             }
9597             if(this.snapshot){
9598                 this.data = this.snapshot;
9599                 delete this.snapshot;
9600             }
9601             this.data.clear();
9602             this.data.addAll(r);
9603             this.totalLength = t;
9604             this.applySort();
9605             this.fireEvent("datachanged", this);
9606         }else{
9607             this.totalLength = Math.max(t, this.data.length+r.length);
9608             this.add(r);
9609         }
9610         this.fireEvent("load", this, r, options, o);
9611         if(options.callback){
9612             options.callback.call(options.scope || this, r, options, true);
9613         }
9614     },
9615
9616
9617     /**
9618      * Loads data from a passed data block. A Reader which understands the format of the data
9619      * must have been configured in the constructor.
9620      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9621      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9622      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9623      */
9624     loadData : function(o, append){
9625         var r = this.reader.readRecords(o);
9626         this.loadRecords(r, {add: append}, true);
9627     },
9628
9629     /**
9630      * Gets the number of cached records.
9631      * <p>
9632      * <em>If using paging, this may not be the total size of the dataset. If the data object
9633      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9634      * the data set size</em>
9635      */
9636     getCount : function(){
9637         return this.data.length || 0;
9638     },
9639
9640     /**
9641      * Gets the total number of records in the dataset as returned by the server.
9642      * <p>
9643      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9644      * the dataset size</em>
9645      */
9646     getTotalCount : function(){
9647         return this.totalLength || 0;
9648     },
9649
9650     /**
9651      * Returns the sort state of the Store as an object with two properties:
9652      * <pre><code>
9653  field {String} The name of the field by which the Records are sorted
9654  direction {String} The sort order, "ASC" or "DESC"
9655      * </code></pre>
9656      */
9657     getSortState : function(){
9658         return this.sortInfo;
9659     },
9660
9661     // private
9662     applySort : function(){
9663         if(this.sortInfo && !this.remoteSort){
9664             var s = this.sortInfo, f = s.field;
9665             var st = this.fields.get(f).sortType;
9666             var fn = function(r1, r2){
9667                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9668                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9669             };
9670             this.data.sort(s.direction, fn);
9671             if(this.snapshot && this.snapshot != this.data){
9672                 this.snapshot.sort(s.direction, fn);
9673             }
9674         }
9675     },
9676
9677     /**
9678      * Sets the default sort column and order to be used by the next load operation.
9679      * @param {String} fieldName The name of the field to sort by.
9680      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9681      */
9682     setDefaultSort : function(field, dir){
9683         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9684     },
9685
9686     /**
9687      * Sort the Records.
9688      * If remote sorting is used, the sort is performed on the server, and the cache is
9689      * reloaded. If local sorting is used, the cache is sorted internally.
9690      * @param {String} fieldName The name of the field to sort by.
9691      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9692      */
9693     sort : function(fieldName, dir){
9694         var f = this.fields.get(fieldName);
9695         if(!dir){
9696             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9697             
9698             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9699                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9700             }else{
9701                 dir = f.sortDir;
9702             }
9703         }
9704         this.sortToggle[f.name] = dir;
9705         this.sortInfo = {field: f.name, direction: dir};
9706         if(!this.remoteSort){
9707             this.applySort();
9708             this.fireEvent("datachanged", this);
9709         }else{
9710             this.load(this.lastOptions);
9711         }
9712     },
9713
9714     /**
9715      * Calls the specified function for each of the Records in the cache.
9716      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9717      * Returning <em>false</em> aborts and exits the iteration.
9718      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9719      */
9720     each : function(fn, scope){
9721         this.data.each(fn, scope);
9722     },
9723
9724     /**
9725      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9726      * (e.g., during paging).
9727      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9728      */
9729     getModifiedRecords : function(){
9730         return this.modified;
9731     },
9732
9733     // private
9734     createFilterFn : function(property, value, anyMatch){
9735         if(!value.exec){ // not a regex
9736             value = String(value);
9737             if(value.length == 0){
9738                 return false;
9739             }
9740             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9741         }
9742         return function(r){
9743             return value.test(r.data[property]);
9744         };
9745     },
9746
9747     /**
9748      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9749      * @param {String} property A field on your records
9750      * @param {Number} start The record index to start at (defaults to 0)
9751      * @param {Number} end The last record index to include (defaults to length - 1)
9752      * @return {Number} The sum
9753      */
9754     sum : function(property, start, end){
9755         var rs = this.data.items, v = 0;
9756         start = start || 0;
9757         end = (end || end === 0) ? end : rs.length-1;
9758
9759         for(var i = start; i <= end; i++){
9760             v += (rs[i].data[property] || 0);
9761         }
9762         return v;
9763     },
9764
9765     /**
9766      * Filter 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      */
9772     filter : function(property, value, anyMatch){
9773         var fn = this.createFilterFn(property, value, anyMatch);
9774         return fn ? this.filterBy(fn) : this.clearFilter();
9775     },
9776
9777     /**
9778      * Filter by a function. The specified function will be called with each
9779      * record in this data source. If the function returns true the record is included,
9780      * otherwise it is filtered.
9781      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9782      * @param {Object} scope (optional) The scope of the function (defaults to this)
9783      */
9784     filterBy : function(fn, scope){
9785         this.snapshot = this.snapshot || this.data;
9786         this.data = this.queryBy(fn, scope||this);
9787         this.fireEvent("datachanged", this);
9788     },
9789
9790     /**
9791      * Query the records by a specified property.
9792      * @param {String} field A field on your records
9793      * @param {String/RegExp} value Either a string that the field
9794      * should start with or a RegExp to test against the field
9795      * @param {Boolean} anyMatch True to match any part not just the beginning
9796      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9797      */
9798     query : function(property, value, anyMatch){
9799         var fn = this.createFilterFn(property, value, anyMatch);
9800         return fn ? this.queryBy(fn) : this.data.clone();
9801     },
9802
9803     /**
9804      * Query by a function. The specified function will be called with each
9805      * record in this data source. If the function returns true the record is included
9806      * in the results.
9807      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9808      * @param {Object} scope (optional) The scope of the function (defaults to this)
9809       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9810      **/
9811     queryBy : function(fn, scope){
9812         var data = this.snapshot || this.data;
9813         return data.filterBy(fn, scope||this);
9814     },
9815
9816     /**
9817      * Collects unique values for a particular dataIndex from this store.
9818      * @param {String} dataIndex The property to collect
9819      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9820      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9821      * @return {Array} An array of the unique values
9822      **/
9823     collect : function(dataIndex, allowNull, bypassFilter){
9824         var d = (bypassFilter === true && this.snapshot) ?
9825                 this.snapshot.items : this.data.items;
9826         var v, sv, r = [], l = {};
9827         for(var i = 0, len = d.length; i < len; i++){
9828             v = d[i].data[dataIndex];
9829             sv = String(v);
9830             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9831                 l[sv] = true;
9832                 r[r.length] = v;
9833             }
9834         }
9835         return r;
9836     },
9837
9838     /**
9839      * Revert to a view of the Record cache with no filtering applied.
9840      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9841      */
9842     clearFilter : function(suppressEvent){
9843         if(this.snapshot && this.snapshot != this.data){
9844             this.data = this.snapshot;
9845             delete this.snapshot;
9846             if(suppressEvent !== true){
9847                 this.fireEvent("datachanged", this);
9848             }
9849         }
9850     },
9851
9852     // private
9853     afterEdit : function(record){
9854         if(this.modified.indexOf(record) == -1){
9855             this.modified.push(record);
9856         }
9857         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9858     },
9859     
9860     // private
9861     afterReject : function(record){
9862         this.modified.remove(record);
9863         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9864     },
9865
9866     // private
9867     afterCommit : function(record){
9868         this.modified.remove(record);
9869         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9870     },
9871
9872     /**
9873      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9874      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9875      */
9876     commitChanges : function(){
9877         var m = this.modified.slice(0);
9878         this.modified = [];
9879         for(var i = 0, len = m.length; i < len; i++){
9880             m[i].commit();
9881         }
9882     },
9883
9884     /**
9885      * Cancel outstanding changes on all changed records.
9886      */
9887     rejectChanges : function(){
9888         var m = this.modified.slice(0);
9889         this.modified = [];
9890         for(var i = 0, len = m.length; i < len; i++){
9891             m[i].reject();
9892         }
9893     },
9894
9895     onMetaChange : function(meta, rtype, o){
9896         this.recordType = rtype;
9897         this.fields = rtype.prototype.fields;
9898         delete this.snapshot;
9899         this.sortInfo = meta.sortInfo || this.sortInfo;
9900         this.modified = [];
9901         this.fireEvent('metachange', this, this.reader.meta);
9902     },
9903     
9904     moveIndex : function(data, type)
9905     {
9906         var index = this.indexOf(data);
9907         
9908         var newIndex = index + type;
9909         
9910         this.remove(data);
9911         
9912         this.insert(newIndex, data);
9913         
9914     }
9915 });/*
9916  * Based on:
9917  * Ext JS Library 1.1.1
9918  * Copyright(c) 2006-2007, Ext JS, LLC.
9919  *
9920  * Originally Released Under LGPL - original licence link has changed is not relivant.
9921  *
9922  * Fork - LGPL
9923  * <script type="text/javascript">
9924  */
9925
9926 /**
9927  * @class Roo.data.SimpleStore
9928  * @extends Roo.data.Store
9929  * Small helper class to make creating Stores from Array data easier.
9930  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9931  * @cfg {Array} fields An array of field definition objects, or field name strings.
9932  * @cfg {Array} data The multi-dimensional array of data
9933  * @constructor
9934  * @param {Object} config
9935  */
9936 Roo.data.SimpleStore = function(config){
9937     Roo.data.SimpleStore.superclass.constructor.call(this, {
9938         isLocal : true,
9939         reader: new Roo.data.ArrayReader({
9940                 id: config.id
9941             },
9942             Roo.data.Record.create(config.fields)
9943         ),
9944         proxy : new Roo.data.MemoryProxy(config.data)
9945     });
9946     this.load();
9947 };
9948 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9949  * Based on:
9950  * Ext JS Library 1.1.1
9951  * Copyright(c) 2006-2007, Ext JS, LLC.
9952  *
9953  * Originally Released Under LGPL - original licence link has changed is not relivant.
9954  *
9955  * Fork - LGPL
9956  * <script type="text/javascript">
9957  */
9958
9959 /**
9960 /**
9961  * @extends Roo.data.Store
9962  * @class Roo.data.JsonStore
9963  * Small helper class to make creating Stores for JSON data easier. <br/>
9964 <pre><code>
9965 var store = new Roo.data.JsonStore({
9966     url: 'get-images.php',
9967     root: 'images',
9968     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9969 });
9970 </code></pre>
9971  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9972  * JsonReader and HttpProxy (unless inline data is provided).</b>
9973  * @cfg {Array} fields An array of field definition objects, or field name strings.
9974  * @constructor
9975  * @param {Object} config
9976  */
9977 Roo.data.JsonStore = function(c){
9978     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9979         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9980         reader: new Roo.data.JsonReader(c, c.fields)
9981     }));
9982 };
9983 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9984  * Based on:
9985  * Ext JS Library 1.1.1
9986  * Copyright(c) 2006-2007, Ext JS, LLC.
9987  *
9988  * Originally Released Under LGPL - original licence link has changed is not relivant.
9989  *
9990  * Fork - LGPL
9991  * <script type="text/javascript">
9992  */
9993
9994  
9995 Roo.data.Field = function(config){
9996     if(typeof config == "string"){
9997         config = {name: config};
9998     }
9999     Roo.apply(this, config);
10000     
10001     if(!this.type){
10002         this.type = "auto";
10003     }
10004     
10005     var st = Roo.data.SortTypes;
10006     // named sortTypes are supported, here we look them up
10007     if(typeof this.sortType == "string"){
10008         this.sortType = st[this.sortType];
10009     }
10010     
10011     // set default sortType for strings and dates
10012     if(!this.sortType){
10013         switch(this.type){
10014             case "string":
10015                 this.sortType = st.asUCString;
10016                 break;
10017             case "date":
10018                 this.sortType = st.asDate;
10019                 break;
10020             default:
10021                 this.sortType = st.none;
10022         }
10023     }
10024
10025     // define once
10026     var stripRe = /[\$,%]/g;
10027
10028     // prebuilt conversion function for this field, instead of
10029     // switching every time we're reading a value
10030     if(!this.convert){
10031         var cv, dateFormat = this.dateFormat;
10032         switch(this.type){
10033             case "":
10034             case "auto":
10035             case undefined:
10036                 cv = function(v){ return v; };
10037                 break;
10038             case "string":
10039                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10040                 break;
10041             case "int":
10042                 cv = function(v){
10043                     return v !== undefined && v !== null && v !== '' ?
10044                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10045                     };
10046                 break;
10047             case "float":
10048                 cv = function(v){
10049                     return v !== undefined && v !== null && v !== '' ?
10050                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10051                     };
10052                 break;
10053             case "bool":
10054             case "boolean":
10055                 cv = function(v){ return v === true || v === "true" || v == 1; };
10056                 break;
10057             case "date":
10058                 cv = function(v){
10059                     if(!v){
10060                         return '';
10061                     }
10062                     if(v instanceof Date){
10063                         return v;
10064                     }
10065                     if(dateFormat){
10066                         if(dateFormat == "timestamp"){
10067                             return new Date(v*1000);
10068                         }
10069                         return Date.parseDate(v, dateFormat);
10070                     }
10071                     var parsed = Date.parse(v);
10072                     return parsed ? new Date(parsed) : null;
10073                 };
10074              break;
10075             
10076         }
10077         this.convert = cv;
10078     }
10079 };
10080
10081 Roo.data.Field.prototype = {
10082     dateFormat: null,
10083     defaultValue: "",
10084     mapping: null,
10085     sortType : null,
10086     sortDir : "ASC"
10087 };/*
10088  * Based on:
10089  * Ext JS Library 1.1.1
10090  * Copyright(c) 2006-2007, Ext JS, LLC.
10091  *
10092  * Originally Released Under LGPL - original licence link has changed is not relivant.
10093  *
10094  * Fork - LGPL
10095  * <script type="text/javascript">
10096  */
10097  
10098 // Base class for reading structured data from a data source.  This class is intended to be
10099 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10100
10101 /**
10102  * @class Roo.data.DataReader
10103  * Base class for reading structured data from a data source.  This class is intended to be
10104  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10105  */
10106
10107 Roo.data.DataReader = function(meta, recordType){
10108     
10109     this.meta = meta;
10110     
10111     this.recordType = recordType instanceof Array ? 
10112         Roo.data.Record.create(recordType) : recordType;
10113 };
10114
10115 Roo.data.DataReader.prototype = {
10116      /**
10117      * Create an empty record
10118      * @param {Object} data (optional) - overlay some values
10119      * @return {Roo.data.Record} record created.
10120      */
10121     newRow :  function(d) {
10122         var da =  {};
10123         this.recordType.prototype.fields.each(function(c) {
10124             switch( c.type) {
10125                 case 'int' : da[c.name] = 0; break;
10126                 case 'date' : da[c.name] = new Date(); break;
10127                 case 'float' : da[c.name] = 0.0; break;
10128                 case 'boolean' : da[c.name] = false; break;
10129                 default : da[c.name] = ""; break;
10130             }
10131             
10132         });
10133         return new this.recordType(Roo.apply(da, d));
10134     }
10135     
10136 };/*
10137  * Based on:
10138  * Ext JS Library 1.1.1
10139  * Copyright(c) 2006-2007, Ext JS, LLC.
10140  *
10141  * Originally Released Under LGPL - original licence link has changed is not relivant.
10142  *
10143  * Fork - LGPL
10144  * <script type="text/javascript">
10145  */
10146
10147 /**
10148  * @class Roo.data.DataProxy
10149  * @extends Roo.data.Observable
10150  * This class is an abstract base class for implementations which provide retrieval of
10151  * unformatted data objects.<br>
10152  * <p>
10153  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10154  * (of the appropriate type which knows how to parse the data object) to provide a block of
10155  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10156  * <p>
10157  * Custom implementations must implement the load method as described in
10158  * {@link Roo.data.HttpProxy#load}.
10159  */
10160 Roo.data.DataProxy = function(){
10161     this.addEvents({
10162         /**
10163          * @event beforeload
10164          * Fires before a network request is made to retrieve a data object.
10165          * @param {Object} This DataProxy object.
10166          * @param {Object} params The params parameter to the load function.
10167          */
10168         beforeload : true,
10169         /**
10170          * @event load
10171          * Fires before the load method's callback is called.
10172          * @param {Object} This DataProxy object.
10173          * @param {Object} o The data object.
10174          * @param {Object} arg The callback argument object passed to the load function.
10175          */
10176         load : true,
10177         /**
10178          * @event loadexception
10179          * Fires if an Exception occurs during data retrieval.
10180          * @param {Object} This DataProxy object.
10181          * @param {Object} o The data object.
10182          * @param {Object} arg The callback argument object passed to the load function.
10183          * @param {Object} e The Exception.
10184          */
10185         loadexception : true
10186     });
10187     Roo.data.DataProxy.superclass.constructor.call(this);
10188 };
10189
10190 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10191
10192     /**
10193      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10194      */
10195 /*
10196  * Based on:
10197  * Ext JS Library 1.1.1
10198  * Copyright(c) 2006-2007, Ext JS, LLC.
10199  *
10200  * Originally Released Under LGPL - original licence link has changed is not relivant.
10201  *
10202  * Fork - LGPL
10203  * <script type="text/javascript">
10204  */
10205 /**
10206  * @class Roo.data.MemoryProxy
10207  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10208  * to the Reader when its load method is called.
10209  * @constructor
10210  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10211  */
10212 Roo.data.MemoryProxy = function(data){
10213     if (data.data) {
10214         data = data.data;
10215     }
10216     Roo.data.MemoryProxy.superclass.constructor.call(this);
10217     this.data = data;
10218 };
10219
10220 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10221     /**
10222      * Load data from the requested source (in this case an in-memory
10223      * data object passed to the constructor), read the data object into
10224      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10225      * process that block using the passed callback.
10226      * @param {Object} params This parameter is not used by the MemoryProxy class.
10227      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10228      * object into a block of Roo.data.Records.
10229      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10230      * The function must be passed <ul>
10231      * <li>The Record block object</li>
10232      * <li>The "arg" argument from the load function</li>
10233      * <li>A boolean success indicator</li>
10234      * </ul>
10235      * @param {Object} scope The scope in which to call the callback
10236      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10237      */
10238     load : function(params, reader, callback, scope, arg){
10239         params = params || {};
10240         var result;
10241         try {
10242             result = reader.readRecords(this.data);
10243         }catch(e){
10244             this.fireEvent("loadexception", this, arg, null, e);
10245             callback.call(scope, null, arg, false);
10246             return;
10247         }
10248         callback.call(scope, result, arg, true);
10249     },
10250     
10251     // private
10252     update : function(params, records){
10253         
10254     }
10255 });/*
10256  * Based on:
10257  * Ext JS Library 1.1.1
10258  * Copyright(c) 2006-2007, Ext JS, LLC.
10259  *
10260  * Originally Released Under LGPL - original licence link has changed is not relivant.
10261  *
10262  * Fork - LGPL
10263  * <script type="text/javascript">
10264  */
10265 /**
10266  * @class Roo.data.HttpProxy
10267  * @extends Roo.data.DataProxy
10268  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10269  * configured to reference a certain URL.<br><br>
10270  * <p>
10271  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10272  * from which the running page was served.<br><br>
10273  * <p>
10274  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10275  * <p>
10276  * Be aware that to enable the browser to parse an XML document, the server must set
10277  * the Content-Type header in the HTTP response to "text/xml".
10278  * @constructor
10279  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10280  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10281  * will be used to make the request.
10282  */
10283 Roo.data.HttpProxy = function(conn){
10284     Roo.data.HttpProxy.superclass.constructor.call(this);
10285     // is conn a conn config or a real conn?
10286     this.conn = conn;
10287     this.useAjax = !conn || !conn.events;
10288   
10289 };
10290
10291 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10292     // thse are take from connection...
10293     
10294     /**
10295      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10296      */
10297     /**
10298      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10299      * extra parameters to each request made by this object. (defaults to undefined)
10300      */
10301     /**
10302      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10303      *  to each request made by this object. (defaults to undefined)
10304      */
10305     /**
10306      * @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)
10307      */
10308     /**
10309      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10310      */
10311      /**
10312      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10313      * @type Boolean
10314      */
10315   
10316
10317     /**
10318      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10319      * @type Boolean
10320      */
10321     /**
10322      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10323      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10324      * a finer-grained basis than the DataProxy events.
10325      */
10326     getConnection : function(){
10327         return this.useAjax ? Roo.Ajax : this.conn;
10328     },
10329
10330     /**
10331      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10332      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10333      * process that block using the passed callback.
10334      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10335      * for the request to the remote server.
10336      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10337      * object into a block of Roo.data.Records.
10338      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10339      * The function must be passed <ul>
10340      * <li>The Record block object</li>
10341      * <li>The "arg" argument from the load function</li>
10342      * <li>A boolean success indicator</li>
10343      * </ul>
10344      * @param {Object} scope The scope in which to call the callback
10345      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10346      */
10347     load : function(params, reader, callback, scope, arg){
10348         if(this.fireEvent("beforeload", this, params) !== false){
10349             var  o = {
10350                 params : params || {},
10351                 request: {
10352                     callback : callback,
10353                     scope : scope,
10354                     arg : arg
10355                 },
10356                 reader: reader,
10357                 callback : this.loadResponse,
10358                 scope: this
10359             };
10360             if(this.useAjax){
10361                 Roo.applyIf(o, this.conn);
10362                 if(this.activeRequest){
10363                     Roo.Ajax.abort(this.activeRequest);
10364                 }
10365                 this.activeRequest = Roo.Ajax.request(o);
10366             }else{
10367                 this.conn.request(o);
10368             }
10369         }else{
10370             callback.call(scope||this, null, arg, false);
10371         }
10372     },
10373
10374     // private
10375     loadResponse : function(o, success, response){
10376         delete this.activeRequest;
10377         if(!success){
10378             this.fireEvent("loadexception", this, o, response);
10379             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10380             return;
10381         }
10382         var result;
10383         try {
10384             result = o.reader.read(response);
10385         }catch(e){
10386             this.fireEvent("loadexception", this, o, response, e);
10387             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10388             return;
10389         }
10390         
10391         this.fireEvent("load", this, o, o.request.arg);
10392         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10393     },
10394
10395     // private
10396     update : function(dataSet){
10397
10398     },
10399
10400     // private
10401     updateResponse : function(dataSet){
10402
10403     }
10404 });/*
10405  * Based on:
10406  * Ext JS Library 1.1.1
10407  * Copyright(c) 2006-2007, Ext JS, LLC.
10408  *
10409  * Originally Released Under LGPL - original licence link has changed is not relivant.
10410  *
10411  * Fork - LGPL
10412  * <script type="text/javascript">
10413  */
10414
10415 /**
10416  * @class Roo.data.ScriptTagProxy
10417  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10418  * other than the originating domain of the running page.<br><br>
10419  * <p>
10420  * <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
10421  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10422  * <p>
10423  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10424  * source code that is used as the source inside a &lt;script> tag.<br><br>
10425  * <p>
10426  * In order for the browser to process the returned data, the server must wrap the data object
10427  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10428  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10429  * depending on whether the callback name was passed:
10430  * <p>
10431  * <pre><code>
10432 boolean scriptTag = false;
10433 String cb = request.getParameter("callback");
10434 if (cb != null) {
10435     scriptTag = true;
10436     response.setContentType("text/javascript");
10437 } else {
10438     response.setContentType("application/x-json");
10439 }
10440 Writer out = response.getWriter();
10441 if (scriptTag) {
10442     out.write(cb + "(");
10443 }
10444 out.print(dataBlock.toJsonString());
10445 if (scriptTag) {
10446     out.write(");");
10447 }
10448 </pre></code>
10449  *
10450  * @constructor
10451  * @param {Object} config A configuration object.
10452  */
10453 Roo.data.ScriptTagProxy = function(config){
10454     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10455     Roo.apply(this, config);
10456     this.head = document.getElementsByTagName("head")[0];
10457 };
10458
10459 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10460
10461 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10462     /**
10463      * @cfg {String} url The URL from which to request the data object.
10464      */
10465     /**
10466      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10467      */
10468     timeout : 30000,
10469     /**
10470      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10471      * the server the name of the callback function set up by the load call to process the returned data object.
10472      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10473      * javascript output which calls this named function passing the data object as its only parameter.
10474      */
10475     callbackParam : "callback",
10476     /**
10477      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10478      * name to the request.
10479      */
10480     nocache : true,
10481
10482     /**
10483      * Load data from the configured URL, read the data object into
10484      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10485      * process that block using the passed callback.
10486      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10487      * for the request to the remote server.
10488      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10489      * object into a block of Roo.data.Records.
10490      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10491      * The function must be passed <ul>
10492      * <li>The Record block object</li>
10493      * <li>The "arg" argument from the load function</li>
10494      * <li>A boolean success indicator</li>
10495      * </ul>
10496      * @param {Object} scope The scope in which to call the callback
10497      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10498      */
10499     load : function(params, reader, callback, scope, arg){
10500         if(this.fireEvent("beforeload", this, params) !== false){
10501
10502             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10503
10504             var url = this.url;
10505             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10506             if(this.nocache){
10507                 url += "&_dc=" + (new Date().getTime());
10508             }
10509             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10510             var trans = {
10511                 id : transId,
10512                 cb : "stcCallback"+transId,
10513                 scriptId : "stcScript"+transId,
10514                 params : params,
10515                 arg : arg,
10516                 url : url,
10517                 callback : callback,
10518                 scope : scope,
10519                 reader : reader
10520             };
10521             var conn = this;
10522
10523             window[trans.cb] = function(o){
10524                 conn.handleResponse(o, trans);
10525             };
10526
10527             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10528
10529             if(this.autoAbort !== false){
10530                 this.abort();
10531             }
10532
10533             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10534
10535             var script = document.createElement("script");
10536             script.setAttribute("src", url);
10537             script.setAttribute("type", "text/javascript");
10538             script.setAttribute("id", trans.scriptId);
10539             this.head.appendChild(script);
10540
10541             this.trans = trans;
10542         }else{
10543             callback.call(scope||this, null, arg, false);
10544         }
10545     },
10546
10547     // private
10548     isLoading : function(){
10549         return this.trans ? true : false;
10550     },
10551
10552     /**
10553      * Abort the current server request.
10554      */
10555     abort : function(){
10556         if(this.isLoading()){
10557             this.destroyTrans(this.trans);
10558         }
10559     },
10560
10561     // private
10562     destroyTrans : function(trans, isLoaded){
10563         this.head.removeChild(document.getElementById(trans.scriptId));
10564         clearTimeout(trans.timeoutId);
10565         if(isLoaded){
10566             window[trans.cb] = undefined;
10567             try{
10568                 delete window[trans.cb];
10569             }catch(e){}
10570         }else{
10571             // if hasn't been loaded, wait for load to remove it to prevent script error
10572             window[trans.cb] = function(){
10573                 window[trans.cb] = undefined;
10574                 try{
10575                     delete window[trans.cb];
10576                 }catch(e){}
10577             };
10578         }
10579     },
10580
10581     // private
10582     handleResponse : function(o, trans){
10583         this.trans = false;
10584         this.destroyTrans(trans, true);
10585         var result;
10586         try {
10587             result = trans.reader.readRecords(o);
10588         }catch(e){
10589             this.fireEvent("loadexception", this, o, trans.arg, e);
10590             trans.callback.call(trans.scope||window, null, trans.arg, false);
10591             return;
10592         }
10593         this.fireEvent("load", this, o, trans.arg);
10594         trans.callback.call(trans.scope||window, result, trans.arg, true);
10595     },
10596
10597     // private
10598     handleFailure : function(trans){
10599         this.trans = false;
10600         this.destroyTrans(trans, false);
10601         this.fireEvent("loadexception", this, null, trans.arg);
10602         trans.callback.call(trans.scope||window, null, trans.arg, false);
10603     }
10604 });/*
10605  * Based on:
10606  * Ext JS Library 1.1.1
10607  * Copyright(c) 2006-2007, Ext JS, LLC.
10608  *
10609  * Originally Released Under LGPL - original licence link has changed is not relivant.
10610  *
10611  * Fork - LGPL
10612  * <script type="text/javascript">
10613  */
10614
10615 /**
10616  * @class Roo.data.JsonReader
10617  * @extends Roo.data.DataReader
10618  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10619  * based on mappings in a provided Roo.data.Record constructor.
10620  * 
10621  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10622  * in the reply previously. 
10623  * 
10624  * <p>
10625  * Example code:
10626  * <pre><code>
10627 var RecordDef = Roo.data.Record.create([
10628     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10629     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10630 ]);
10631 var myReader = new Roo.data.JsonReader({
10632     totalProperty: "results",    // The property which contains the total dataset size (optional)
10633     root: "rows",                // The property which contains an Array of row objects
10634     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10635 }, RecordDef);
10636 </code></pre>
10637  * <p>
10638  * This would consume a JSON file like this:
10639  * <pre><code>
10640 { 'results': 2, 'rows': [
10641     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10642     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10643 }
10644 </code></pre>
10645  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10646  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10647  * paged from the remote server.
10648  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10649  * @cfg {String} root name of the property which contains the Array of row objects.
10650  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10651  * @constructor
10652  * Create a new JsonReader
10653  * @param {Object} meta Metadata configuration options
10654  * @param {Object} recordType Either an Array of field definition objects,
10655  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10656  */
10657 Roo.data.JsonReader = function(meta, recordType){
10658     
10659     meta = meta || {};
10660     // set some defaults:
10661     Roo.applyIf(meta, {
10662         totalProperty: 'total',
10663         successProperty : 'success',
10664         root : 'data',
10665         id : 'id'
10666     });
10667     
10668     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10669 };
10670 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10671     
10672     /**
10673      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10674      * Used by Store query builder to append _requestMeta to params.
10675      * 
10676      */
10677     metaFromRemote : false,
10678     /**
10679      * This method is only used by a DataProxy which has retrieved data from a remote server.
10680      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10681      * @return {Object} data A data block which is used by an Roo.data.Store object as
10682      * a cache of Roo.data.Records.
10683      */
10684     read : function(response){
10685         var json = response.responseText;
10686        
10687         var o = /* eval:var:o */ eval("("+json+")");
10688         if(!o) {
10689             throw {message: "JsonReader.read: Json object not found"};
10690         }
10691         
10692         if(o.metaData){
10693             
10694             delete this.ef;
10695             this.metaFromRemote = true;
10696             this.meta = o.metaData;
10697             this.recordType = Roo.data.Record.create(o.metaData.fields);
10698             this.onMetaChange(this.meta, this.recordType, o);
10699         }
10700         return this.readRecords(o);
10701     },
10702
10703     // private function a store will implement
10704     onMetaChange : function(meta, recordType, o){
10705
10706     },
10707
10708     /**
10709          * @ignore
10710          */
10711     simpleAccess: function(obj, subsc) {
10712         return obj[subsc];
10713     },
10714
10715         /**
10716          * @ignore
10717          */
10718     getJsonAccessor: function(){
10719         var re = /[\[\.]/;
10720         return function(expr) {
10721             try {
10722                 return(re.test(expr))
10723                     ? new Function("obj", "return obj." + expr)
10724                     : function(obj){
10725                         return obj[expr];
10726                     };
10727             } catch(e){}
10728             return Roo.emptyFn;
10729         };
10730     }(),
10731
10732     /**
10733      * Create a data block containing Roo.data.Records from an XML document.
10734      * @param {Object} o An object which contains an Array of row objects in the property specified
10735      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10736      * which contains the total size of the dataset.
10737      * @return {Object} data A data block which is used by an Roo.data.Store object as
10738      * a cache of Roo.data.Records.
10739      */
10740     readRecords : function(o){
10741         /**
10742          * After any data loads, the raw JSON data is available for further custom processing.
10743          * @type Object
10744          */
10745         this.o = o;
10746         var s = this.meta, Record = this.recordType,
10747             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10748
10749 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10750         if (!this.ef) {
10751             if(s.totalProperty) {
10752                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10753                 }
10754                 if(s.successProperty) {
10755                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10756                 }
10757                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10758                 if (s.id) {
10759                         var g = this.getJsonAccessor(s.id);
10760                         this.getId = function(rec) {
10761                                 var r = g(rec);  
10762                                 return (r === undefined || r === "") ? null : r;
10763                         };
10764                 } else {
10765                         this.getId = function(){return null;};
10766                 }
10767             this.ef = [];
10768             for(var jj = 0; jj < fl; jj++){
10769                 f = fi[jj];
10770                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10771                 this.ef[jj] = this.getJsonAccessor(map);
10772             }
10773         }
10774
10775         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10776         if(s.totalProperty){
10777             var vt = parseInt(this.getTotal(o), 10);
10778             if(!isNaN(vt)){
10779                 totalRecords = vt;
10780             }
10781         }
10782         if(s.successProperty){
10783             var vs = this.getSuccess(o);
10784             if(vs === false || vs === 'false'){
10785                 success = false;
10786             }
10787         }
10788         var records = [];
10789         for(var i = 0; i < c; i++){
10790                 var n = root[i];
10791             var values = {};
10792             var id = this.getId(n);
10793             for(var j = 0; j < fl; j++){
10794                 f = fi[j];
10795             var v = this.ef[j](n);
10796             if (!f.convert) {
10797                 Roo.log('missing convert for ' + f.name);
10798                 Roo.log(f);
10799                 continue;
10800             }
10801             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10802             }
10803             var record = new Record(values, id);
10804             record.json = n;
10805             records[i] = record;
10806         }
10807         return {
10808             raw : o,
10809             success : success,
10810             records : records,
10811             totalRecords : totalRecords
10812         };
10813     }
10814 });/*
10815  * Based on:
10816  * Ext JS Library 1.1.1
10817  * Copyright(c) 2006-2007, Ext JS, LLC.
10818  *
10819  * Originally Released Under LGPL - original licence link has changed is not relivant.
10820  *
10821  * Fork - LGPL
10822  * <script type="text/javascript">
10823  */
10824
10825 /**
10826  * @class Roo.data.ArrayReader
10827  * @extends Roo.data.DataReader
10828  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10829  * Each element of that Array represents a row of data fields. The
10830  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10831  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10832  * <p>
10833  * Example code:.
10834  * <pre><code>
10835 var RecordDef = Roo.data.Record.create([
10836     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10837     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10838 ]);
10839 var myReader = new Roo.data.ArrayReader({
10840     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10841 }, RecordDef);
10842 </code></pre>
10843  * <p>
10844  * This would consume an Array like this:
10845  * <pre><code>
10846 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10847   </code></pre>
10848  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10849  * @constructor
10850  * Create a new JsonReader
10851  * @param {Object} meta Metadata configuration options.
10852  * @param {Object} recordType Either an Array of field definition objects
10853  * as specified to {@link Roo.data.Record#create},
10854  * or an {@link Roo.data.Record} object
10855  * created using {@link Roo.data.Record#create}.
10856  */
10857 Roo.data.ArrayReader = function(meta, recordType){
10858     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10859 };
10860
10861 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10862     /**
10863      * Create a data block containing Roo.data.Records from an XML document.
10864      * @param {Object} o An Array of row objects which represents the dataset.
10865      * @return {Object} data A data block which is used by an Roo.data.Store object as
10866      * a cache of Roo.data.Records.
10867      */
10868     readRecords : function(o){
10869         var sid = this.meta ? this.meta.id : null;
10870         var recordType = this.recordType, fields = recordType.prototype.fields;
10871         var records = [];
10872         var root = o;
10873             for(var i = 0; i < root.length; i++){
10874                     var n = root[i];
10875                 var values = {};
10876                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10877                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10878                 var f = fields.items[j];
10879                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10880                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10881                 v = f.convert(v);
10882                 values[f.name] = v;
10883             }
10884                 var record = new recordType(values, id);
10885                 record.json = n;
10886                 records[records.length] = record;
10887             }
10888             return {
10889                 records : records,
10890                 totalRecords : records.length
10891             };
10892     }
10893 });/*
10894  * - LGPL
10895  * * 
10896  */
10897
10898 /**
10899  * @class Roo.bootstrap.ComboBox
10900  * @extends Roo.bootstrap.TriggerField
10901  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10902  * @cfg {Boolean} append (true|false) default false
10903  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10904  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10905  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10906  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10907  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10908  * @constructor
10909  * Create a new ComboBox.
10910  * @param {Object} config Configuration options
10911  */
10912 Roo.bootstrap.ComboBox = function(config){
10913     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10914     this.addEvents({
10915         /**
10916          * @event expand
10917          * Fires when the dropdown list is expanded
10918              * @param {Roo.bootstrap.ComboBox} combo This combo box
10919              */
10920         'expand' : true,
10921         /**
10922          * @event collapse
10923          * Fires when the dropdown list is collapsed
10924              * @param {Roo.bootstrap.ComboBox} combo This combo box
10925              */
10926         'collapse' : true,
10927         /**
10928          * @event beforeselect
10929          * Fires before a list item is selected. Return false to cancel the selection.
10930              * @param {Roo.bootstrap.ComboBox} combo This combo box
10931              * @param {Roo.data.Record} record The data record returned from the underlying store
10932              * @param {Number} index The index of the selected item in the dropdown list
10933              */
10934         'beforeselect' : true,
10935         /**
10936          * @event select
10937          * Fires when a list item is selected
10938              * @param {Roo.bootstrap.ComboBox} combo This combo box
10939              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10940              * @param {Number} index The index of the selected item in the dropdown list
10941              */
10942         'select' : true,
10943         /**
10944          * @event beforequery
10945          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10946          * The event object passed has these properties:
10947              * @param {Roo.bootstrap.ComboBox} combo This combo box
10948              * @param {String} query The query
10949              * @param {Boolean} forceAll true to force "all" query
10950              * @param {Boolean} cancel true to cancel the query
10951              * @param {Object} e The query event object
10952              */
10953         'beforequery': true,
10954          /**
10955          * @event add
10956          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10957              * @param {Roo.bootstrap.ComboBox} combo This combo box
10958              */
10959         'add' : true,
10960         /**
10961          * @event edit
10962          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10963              * @param {Roo.bootstrap.ComboBox} combo This combo box
10964              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10965              */
10966         'edit' : true,
10967         /**
10968          * @event remove
10969          * Fires when the remove value from the combobox array
10970              * @param {Roo.bootstrap.ComboBox} combo This combo box
10971              */
10972         'remove' : true,
10973         /**
10974          * @event specialfilter
10975          * Fires when specialfilter
10976             * @param {Roo.bootstrap.ComboBox} combo This combo box
10977             */
10978         'specialfilter' : true
10979         
10980     });
10981     
10982     this.item = [];
10983     this.tickItems = [];
10984     
10985     this.selectedIndex = -1;
10986     if(this.mode == 'local'){
10987         if(config.queryDelay === undefined){
10988             this.queryDelay = 10;
10989         }
10990         if(config.minChars === undefined){
10991             this.minChars = 0;
10992         }
10993     }
10994 };
10995
10996 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10997      
10998     /**
10999      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11000      * rendering into an Roo.Editor, defaults to false)
11001      */
11002     /**
11003      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11004      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11005      */
11006     /**
11007      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11008      */
11009     /**
11010      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11011      * the dropdown list (defaults to undefined, with no header element)
11012      */
11013
11014      /**
11015      * @cfg {String/Roo.Template} tpl The template to use to render the output
11016      */
11017      
11018      /**
11019      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11020      */
11021     listWidth: undefined,
11022     /**
11023      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11024      * mode = 'remote' or 'text' if mode = 'local')
11025      */
11026     displayField: undefined,
11027     
11028     /**
11029      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11030      * mode = 'remote' or 'value' if mode = 'local'). 
11031      * Note: use of a valueField requires the user make a selection
11032      * in order for a value to be mapped.
11033      */
11034     valueField: undefined,
11035     
11036     
11037     /**
11038      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11039      * field's data value (defaults to the underlying DOM element's name)
11040      */
11041     hiddenName: undefined,
11042     /**
11043      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11044      */
11045     listClass: '',
11046     /**
11047      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11048      */
11049     selectedClass: 'active',
11050     
11051     /**
11052      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11053      */
11054     shadow:'sides',
11055     /**
11056      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11057      * anchor positions (defaults to 'tl-bl')
11058      */
11059     listAlign: 'tl-bl?',
11060     /**
11061      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11062      */
11063     maxHeight: 300,
11064     /**
11065      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11066      * query specified by the allQuery config option (defaults to 'query')
11067      */
11068     triggerAction: 'query',
11069     /**
11070      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11071      * (defaults to 4, does not apply if editable = false)
11072      */
11073     minChars : 4,
11074     /**
11075      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11076      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11077      */
11078     typeAhead: false,
11079     /**
11080      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11081      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11082      */
11083     queryDelay: 500,
11084     /**
11085      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11086      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11087      */
11088     pageSize: 0,
11089     /**
11090      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11091      * when editable = true (defaults to false)
11092      */
11093     selectOnFocus:false,
11094     /**
11095      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11096      */
11097     queryParam: 'query',
11098     /**
11099      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11100      * when mode = 'remote' (defaults to 'Loading...')
11101      */
11102     loadingText: 'Loading...',
11103     /**
11104      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11105      */
11106     resizable: false,
11107     /**
11108      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11109      */
11110     handleHeight : 8,
11111     /**
11112      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11113      * traditional select (defaults to true)
11114      */
11115     editable: true,
11116     /**
11117      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11118      */
11119     allQuery: '',
11120     /**
11121      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11122      */
11123     mode: 'remote',
11124     /**
11125      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11126      * listWidth has a higher value)
11127      */
11128     minListWidth : 70,
11129     /**
11130      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11131      * allow the user to set arbitrary text into the field (defaults to false)
11132      */
11133     forceSelection:false,
11134     /**
11135      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11136      * if typeAhead = true (defaults to 250)
11137      */
11138     typeAheadDelay : 250,
11139     /**
11140      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11141      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11142      */
11143     valueNotFoundText : undefined,
11144     /**
11145      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11146      */
11147     blockFocus : false,
11148     
11149     /**
11150      * @cfg {Boolean} disableClear Disable showing of clear button.
11151      */
11152     disableClear : false,
11153     /**
11154      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11155      */
11156     alwaysQuery : false,
11157     
11158     /**
11159      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11160      */
11161     multiple : false,
11162     
11163     /**
11164      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11165      */
11166     invalidClass : "has-warning",
11167     
11168     /**
11169      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11170      */
11171     validClass : "has-success",
11172     
11173     /**
11174      * @cfg {Boolean} specialFilter (true|false) special filter default false
11175      */
11176     specialFilter : false,
11177     
11178     //private
11179     addicon : false,
11180     editicon: false,
11181     
11182     page: 0,
11183     hasQuery: false,
11184     append: false,
11185     loadNext: false,
11186     autoFocus : true,
11187     tickable : false,
11188     btnPosition : 'right',
11189     triggerList : true,
11190     showToggleBtn : true,
11191     // element that contains real text value.. (when hidden is used..)
11192     
11193     getAutoCreate : function()
11194     {
11195         var cfg = false;
11196         
11197         /*
11198          *  Normal ComboBox
11199          */
11200         if(!this.tickable){
11201             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11202             return cfg;
11203         }
11204         
11205         /*
11206          *  ComboBox with tickable selections
11207          */
11208              
11209         var align = this.labelAlign || this.parentLabelAlign();
11210         
11211         cfg = {
11212             cls : 'form-group roo-combobox-tickable' //input-group
11213         };
11214         
11215         var buttons = {
11216             tag : 'div',
11217             cls : 'tickable-buttons',
11218             cn : [
11219                 {
11220                     tag : 'button',
11221                     type : 'button',
11222                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11223                     html : 'Edit'
11224                 },
11225                 {
11226                     tag : 'button',
11227                     type : 'button',
11228                     name : 'ok',
11229                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11230                     html : 'Done'
11231                 },
11232                 {
11233                     tag : 'button',
11234                     type : 'button',
11235                     name : 'cancel',
11236                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11237                     html : 'Cancel'
11238                 }
11239             ]
11240         };
11241         
11242         if(this.editable){
11243             buttons.cn.unshift({
11244                 tag: 'input',
11245                 cls: 'select2-search-field-input'
11246             });
11247         }
11248         
11249         var _this = this;
11250         
11251         Roo.each(buttons.cn, function(c){
11252             if (_this.size) {
11253                 c.cls += ' btn-' + _this.size;
11254             }
11255
11256             if (_this.disabled) {
11257                 c.disabled = true;
11258             }
11259         });
11260         
11261         var box = {
11262             tag: 'div',
11263             cn: [
11264                 {
11265                     tag: 'input',
11266                     type : 'hidden',
11267                     cls: 'form-hidden-field'
11268                 },
11269                 {
11270                     tag: 'ul',
11271                     cls: 'select2-choices',
11272                     cn:[
11273                         {
11274                             tag: 'li',
11275                             cls: 'select2-search-field',
11276                             cn: [
11277
11278                                 buttons
11279                             ]
11280                         }
11281                     ]
11282                 }
11283             ]
11284         }
11285         
11286         var combobox = {
11287             cls: 'select2-container input-group select2-container-multi',
11288             cn: [
11289                 box
11290 //                {
11291 //                    tag: 'ul',
11292 //                    cls: 'typeahead typeahead-long dropdown-menu',
11293 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11294 //                }
11295             ]
11296         };
11297         
11298         if(this.hasFeedback && !this.allowBlank){
11299             
11300             var feedback = {
11301                 tag: 'span',
11302                 cls: 'glyphicon form-control-feedback'
11303             };
11304
11305             combobox.cn.push(feedback);
11306         }
11307         
11308         if (align ==='left' && this.fieldLabel.length) {
11309             
11310                 Roo.log("left and has label");
11311                 cfg.cn = [
11312                     
11313                     {
11314                         tag: 'label',
11315                         'for' :  id,
11316                         cls : 'control-label col-sm-' + this.labelWidth,
11317                         html : this.fieldLabel
11318                         
11319                     },
11320                     {
11321                         cls : "col-sm-" + (12 - this.labelWidth), 
11322                         cn: [
11323                             combobox
11324                         ]
11325                     }
11326                     
11327                 ];
11328         } else if ( this.fieldLabel.length) {
11329                 Roo.log(" label");
11330                  cfg.cn = [
11331                    
11332                     {
11333                         tag: 'label',
11334                         //cls : 'input-group-addon',
11335                         html : this.fieldLabel
11336                         
11337                     },
11338                     
11339                     combobox
11340                     
11341                 ];
11342
11343         } else {
11344             
11345                 Roo.log(" no label && no align");
11346                 cfg = combobox
11347                      
11348                 
11349         }
11350          
11351         var settings=this;
11352         ['xs','sm','md','lg'].map(function(size){
11353             if (settings[size]) {
11354                 cfg.cls += ' col-' + size + '-' + settings[size];
11355             }
11356         });
11357         
11358         return cfg;
11359         
11360     },
11361     
11362     // private
11363     initEvents: function()
11364     {
11365         
11366         if (!this.store) {
11367             throw "can not find store for combo";
11368         }
11369         this.store = Roo.factory(this.store, Roo.data);
11370         
11371         if(this.tickable){
11372             this.initTickableEvents();
11373             return;
11374         }
11375         
11376         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11377         
11378         if(this.hiddenName){
11379             
11380             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11381             
11382             this.hiddenField.dom.value =
11383                 this.hiddenValue !== undefined ? this.hiddenValue :
11384                 this.value !== undefined ? this.value : '';
11385
11386             // prevent input submission
11387             this.el.dom.removeAttribute('name');
11388             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11389              
11390              
11391         }
11392         //if(Roo.isGecko){
11393         //    this.el.dom.setAttribute('autocomplete', 'off');
11394         //}
11395         
11396         var cls = 'x-combo-list';
11397         
11398         //this.list = new Roo.Layer({
11399         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11400         //});
11401         
11402         var _this = this;
11403         
11404         (function(){
11405             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11406             _this.list.setWidth(lw);
11407         }).defer(100);
11408         
11409         this.list.on('mouseover', this.onViewOver, this);
11410         this.list.on('mousemove', this.onViewMove, this);
11411         
11412         this.list.on('scroll', this.onViewScroll, this);
11413         
11414         /*
11415         this.list.swallowEvent('mousewheel');
11416         this.assetHeight = 0;
11417
11418         if(this.title){
11419             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11420             this.assetHeight += this.header.getHeight();
11421         }
11422
11423         this.innerList = this.list.createChild({cls:cls+'-inner'});
11424         this.innerList.on('mouseover', this.onViewOver, this);
11425         this.innerList.on('mousemove', this.onViewMove, this);
11426         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11427         
11428         if(this.allowBlank && !this.pageSize && !this.disableClear){
11429             this.footer = this.list.createChild({cls:cls+'-ft'});
11430             this.pageTb = new Roo.Toolbar(this.footer);
11431            
11432         }
11433         if(this.pageSize){
11434             this.footer = this.list.createChild({cls:cls+'-ft'});
11435             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11436                     {pageSize: this.pageSize});
11437             
11438         }
11439         
11440         if (this.pageTb && this.allowBlank && !this.disableClear) {
11441             var _this = this;
11442             this.pageTb.add(new Roo.Toolbar.Fill(), {
11443                 cls: 'x-btn-icon x-btn-clear',
11444                 text: '&#160;',
11445                 handler: function()
11446                 {
11447                     _this.collapse();
11448                     _this.clearValue();
11449                     _this.onSelect(false, -1);
11450                 }
11451             });
11452         }
11453         if (this.footer) {
11454             this.assetHeight += this.footer.getHeight();
11455         }
11456         */
11457             
11458         if(!this.tpl){
11459             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11460         }
11461
11462         this.view = new Roo.View(this.list, this.tpl, {
11463             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11464         });
11465         //this.view.wrapEl.setDisplayed(false);
11466         this.view.on('click', this.onViewClick, this);
11467         
11468         
11469         
11470         this.store.on('beforeload', this.onBeforeLoad, this);
11471         this.store.on('load', this.onLoad, this);
11472         this.store.on('loadexception', this.onLoadException, this);
11473         /*
11474         if(this.resizable){
11475             this.resizer = new Roo.Resizable(this.list,  {
11476                pinned:true, handles:'se'
11477             });
11478             this.resizer.on('resize', function(r, w, h){
11479                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11480                 this.listWidth = w;
11481                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11482                 this.restrictHeight();
11483             }, this);
11484             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11485         }
11486         */
11487         if(!this.editable){
11488             this.editable = true;
11489             this.setEditable(false);
11490         }
11491         
11492         /*
11493         
11494         if (typeof(this.events.add.listeners) != 'undefined') {
11495             
11496             this.addicon = this.wrap.createChild(
11497                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11498        
11499             this.addicon.on('click', function(e) {
11500                 this.fireEvent('add', this);
11501             }, this);
11502         }
11503         if (typeof(this.events.edit.listeners) != 'undefined') {
11504             
11505             this.editicon = this.wrap.createChild(
11506                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11507             if (this.addicon) {
11508                 this.editicon.setStyle('margin-left', '40px');
11509             }
11510             this.editicon.on('click', function(e) {
11511                 
11512                 // we fire even  if inothing is selected..
11513                 this.fireEvent('edit', this, this.lastData );
11514                 
11515             }, this);
11516         }
11517         */
11518         
11519         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11520             "up" : function(e){
11521                 this.inKeyMode = true;
11522                 this.selectPrev();
11523             },
11524
11525             "down" : function(e){
11526                 if(!this.isExpanded()){
11527                     this.onTriggerClick();
11528                 }else{
11529                     this.inKeyMode = true;
11530                     this.selectNext();
11531                 }
11532             },
11533
11534             "enter" : function(e){
11535 //                this.onViewClick();
11536                 //return true;
11537                 this.collapse();
11538                 
11539                 if(this.fireEvent("specialkey", this, e)){
11540                     this.onViewClick(false);
11541                 }
11542                 
11543                 return true;
11544             },
11545
11546             "esc" : function(e){
11547                 this.collapse();
11548             },
11549
11550             "tab" : function(e){
11551                 this.collapse();
11552                 
11553                 if(this.fireEvent("specialkey", this, e)){
11554                     this.onViewClick(false);
11555                 }
11556                 
11557                 return true;
11558             },
11559
11560             scope : this,
11561
11562             doRelay : function(foo, bar, hname){
11563                 if(hname == 'down' || this.scope.isExpanded()){
11564                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11565                 }
11566                 return true;
11567             },
11568
11569             forceKeyDown: true
11570         });
11571         
11572         
11573         this.queryDelay = Math.max(this.queryDelay || 10,
11574                 this.mode == 'local' ? 10 : 250);
11575         
11576         
11577         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11578         
11579         if(this.typeAhead){
11580             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11581         }
11582         if(this.editable !== false){
11583             this.inputEl().on("keyup", this.onKeyUp, this);
11584         }
11585         if(this.forceSelection){
11586             this.inputEl().on('blur', this.doForce, this);
11587         }
11588         
11589         if(this.multiple){
11590             this.choices = this.el.select('ul.select2-choices', true).first();
11591             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11592         }
11593     },
11594     
11595     initTickableEvents: function()
11596     {   
11597         this.createList();
11598         
11599         if(this.hiddenName){
11600             
11601             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11602             
11603             this.hiddenField.dom.value =
11604                 this.hiddenValue !== undefined ? this.hiddenValue :
11605                 this.value !== undefined ? this.value : '';
11606
11607             // prevent input submission
11608             this.el.dom.removeAttribute('name');
11609             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11610              
11611              
11612         }
11613         
11614 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11615         
11616         this.choices = this.el.select('ul.select2-choices', true).first();
11617         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11618         if(this.triggerList){
11619             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11620         }
11621          
11622         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11623         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11624         
11625         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11626         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11627         
11628         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11629         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11630         
11631         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11632         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11633         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11634         
11635         this.okBtn.hide();
11636         this.cancelBtn.hide();
11637         
11638         var _this = this;
11639         
11640         (function(){
11641             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11642             _this.list.setWidth(lw);
11643         }).defer(100);
11644         
11645         this.list.on('mouseover', this.onViewOver, this);
11646         this.list.on('mousemove', this.onViewMove, this);
11647         
11648         this.list.on('scroll', this.onViewScroll, this);
11649         
11650         if(!this.tpl){
11651             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>';
11652         }
11653
11654         this.view = new Roo.View(this.list, this.tpl, {
11655             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11656         });
11657         
11658         //this.view.wrapEl.setDisplayed(false);
11659         this.view.on('click', this.onViewClick, this);
11660         
11661         
11662         
11663         this.store.on('beforeload', this.onBeforeLoad, this);
11664         this.store.on('load', this.onLoad, this);
11665         this.store.on('loadexception', this.onLoadException, this);
11666         
11667         if(this.editable){
11668             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11669                 "up" : function(e){
11670                     this.inKeyMode = true;
11671                     this.selectPrev();
11672                 },
11673
11674                 "down" : function(e){
11675                     this.inKeyMode = true;
11676                     this.selectNext();
11677                 },
11678
11679                 "enter" : function(e){
11680                     if(this.fireEvent("specialkey", this, e)){
11681                         this.onViewClick(false);
11682                     }
11683                     
11684                     return true;
11685                 },
11686
11687                 "esc" : function(e){
11688                     this.onTickableFooterButtonClick(e, false, false);
11689                 },
11690
11691                 "tab" : function(e){
11692                     this.fireEvent("specialkey", this, e);
11693                     
11694                     this.onTickableFooterButtonClick(e, false, false);
11695                     
11696                     return true;
11697                 },
11698
11699                 scope : this,
11700
11701                 doRelay : function(e, fn, key){
11702                     if(this.scope.isExpanded()){
11703                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11704                     }
11705                     return true;
11706                 },
11707
11708                 forceKeyDown: true
11709             });
11710         }
11711         
11712         this.queryDelay = Math.max(this.queryDelay || 10,
11713                 this.mode == 'local' ? 10 : 250);
11714         
11715         
11716         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11717         
11718         if(this.typeAhead){
11719             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11720         }
11721         
11722         if(this.editable !== false){
11723             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11724         }
11725         
11726     },
11727
11728     onDestroy : function(){
11729         if(this.view){
11730             this.view.setStore(null);
11731             this.view.el.removeAllListeners();
11732             this.view.el.remove();
11733             this.view.purgeListeners();
11734         }
11735         if(this.list){
11736             this.list.dom.innerHTML  = '';
11737         }
11738         
11739         if(this.store){
11740             this.store.un('beforeload', this.onBeforeLoad, this);
11741             this.store.un('load', this.onLoad, this);
11742             this.store.un('loadexception', this.onLoadException, this);
11743         }
11744         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11745     },
11746
11747     // private
11748     fireKey : function(e){
11749         if(e.isNavKeyPress() && !this.list.isVisible()){
11750             this.fireEvent("specialkey", this, e);
11751         }
11752     },
11753
11754     // private
11755     onResize: function(w, h){
11756 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11757 //        
11758 //        if(typeof w != 'number'){
11759 //            // we do not handle it!?!?
11760 //            return;
11761 //        }
11762 //        var tw = this.trigger.getWidth();
11763 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11764 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11765 //        var x = w - tw;
11766 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11767 //            
11768 //        //this.trigger.setStyle('left', x+'px');
11769 //        
11770 //        if(this.list && this.listWidth === undefined){
11771 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11772 //            this.list.setWidth(lw);
11773 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11774 //        }
11775         
11776     
11777         
11778     },
11779
11780     /**
11781      * Allow or prevent the user from directly editing the field text.  If false is passed,
11782      * the user will only be able to select from the items defined in the dropdown list.  This method
11783      * is the runtime equivalent of setting the 'editable' config option at config time.
11784      * @param {Boolean} value True to allow the user to directly edit the field text
11785      */
11786     setEditable : function(value){
11787         if(value == this.editable){
11788             return;
11789         }
11790         this.editable = value;
11791         if(!value){
11792             this.inputEl().dom.setAttribute('readOnly', true);
11793             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11794             this.inputEl().addClass('x-combo-noedit');
11795         }else{
11796             this.inputEl().dom.setAttribute('readOnly', false);
11797             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11798             this.inputEl().removeClass('x-combo-noedit');
11799         }
11800     },
11801
11802     // private
11803     
11804     onBeforeLoad : function(combo,opts){
11805         if(!this.hasFocus){
11806             return;
11807         }
11808          if (!opts.add) {
11809             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11810          }
11811         this.restrictHeight();
11812         this.selectedIndex = -1;
11813     },
11814
11815     // private
11816     onLoad : function(){
11817         
11818         this.hasQuery = false;
11819         
11820         if(!this.hasFocus){
11821             return;
11822         }
11823         
11824         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11825             this.loading.hide();
11826         }
11827              
11828         if(this.store.getCount() > 0){
11829             this.expand();
11830             this.restrictHeight();
11831             if(this.lastQuery == this.allQuery){
11832                 if(this.editable && !this.tickable){
11833                     this.inputEl().dom.select();
11834                 }
11835                 
11836                 if(
11837                     !this.selectByValue(this.value, true) &&
11838                     this.autoFocus && 
11839                     (
11840                         !this.store.lastOptions ||
11841                         typeof(this.store.lastOptions.add) == 'undefined' || 
11842                         this.store.lastOptions.add != true
11843                     )
11844                 ){
11845                     this.select(0, true);
11846                 }
11847             }else{
11848                 if(this.autoFocus){
11849                     this.selectNext();
11850                 }
11851                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11852                     this.taTask.delay(this.typeAheadDelay);
11853                 }
11854             }
11855         }else{
11856             this.onEmptyResults();
11857         }
11858         
11859         //this.el.focus();
11860     },
11861     // private
11862     onLoadException : function()
11863     {
11864         this.hasQuery = false;
11865         
11866         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11867             this.loading.hide();
11868         }
11869         
11870         if(this.tickable && this.editable){
11871             return;
11872         }
11873         
11874         this.collapse();
11875         
11876         Roo.log(this.store.reader.jsonData);
11877         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11878             // fixme
11879             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11880         }
11881         
11882         
11883     },
11884     // private
11885     onTypeAhead : function(){
11886         if(this.store.getCount() > 0){
11887             var r = this.store.getAt(0);
11888             var newValue = r.data[this.displayField];
11889             var len = newValue.length;
11890             var selStart = this.getRawValue().length;
11891             
11892             if(selStart != len){
11893                 this.setRawValue(newValue);
11894                 this.selectText(selStart, newValue.length);
11895             }
11896         }
11897     },
11898
11899     // private
11900     onSelect : function(record, index){
11901         
11902         if(this.fireEvent('beforeselect', this, record, index) !== false){
11903         
11904             this.setFromData(index > -1 ? record.data : false);
11905             
11906             this.collapse();
11907             this.fireEvent('select', this, record, index);
11908         }
11909     },
11910
11911     /**
11912      * Returns the currently selected field value or empty string if no value is set.
11913      * @return {String} value The selected value
11914      */
11915     getValue : function(){
11916         
11917         if(this.multiple){
11918             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11919         }
11920         
11921         if(this.valueField){
11922             return typeof this.value != 'undefined' ? this.value : '';
11923         }else{
11924             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11925         }
11926     },
11927
11928     /**
11929      * Clears any text/value currently set in the field
11930      */
11931     clearValue : function(){
11932         if(this.hiddenField){
11933             this.hiddenField.dom.value = '';
11934         }
11935         this.value = '';
11936         this.setRawValue('');
11937         this.lastSelectionText = '';
11938         this.lastData = false;
11939         
11940     },
11941
11942     /**
11943      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11944      * will be displayed in the field.  If the value does not match the data value of an existing item,
11945      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11946      * Otherwise the field will be blank (although the value will still be set).
11947      * @param {String} value The value to match
11948      */
11949     setValue : function(v){
11950         if(this.multiple){
11951             this.syncValue();
11952             return;
11953         }
11954         
11955         var text = v;
11956         if(this.valueField){
11957             var r = this.findRecord(this.valueField, v);
11958             if(r){
11959                 text = r.data[this.displayField];
11960             }else if(this.valueNotFoundText !== undefined){
11961                 text = this.valueNotFoundText;
11962             }
11963         }
11964         this.lastSelectionText = text;
11965         if(this.hiddenField){
11966             this.hiddenField.dom.value = v;
11967         }
11968         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11969         this.value = v;
11970     },
11971     /**
11972      * @property {Object} the last set data for the element
11973      */
11974     
11975     lastData : false,
11976     /**
11977      * Sets the value of the field based on a object which is related to the record format for the store.
11978      * @param {Object} value the value to set as. or false on reset?
11979      */
11980     setFromData : function(o){
11981         
11982         if(this.multiple){
11983             this.addItem(o);
11984             return;
11985         }
11986             
11987         var dv = ''; // display value
11988         var vv = ''; // value value..
11989         this.lastData = o;
11990         if (this.displayField) {
11991             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11992         } else {
11993             // this is an error condition!!!
11994             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11995         }
11996         
11997         if(this.valueField){
11998             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11999         }
12000         
12001         if(this.hiddenField){
12002             this.hiddenField.dom.value = vv;
12003             
12004             this.lastSelectionText = dv;
12005             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12006             this.value = vv;
12007             return;
12008         }
12009         // no hidden field.. - we store the value in 'value', but still display
12010         // display field!!!!
12011         this.lastSelectionText = dv;
12012         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12013         this.value = vv;
12014         
12015         
12016     },
12017     // private
12018     reset : function(){
12019         // overridden so that last data is reset..
12020         
12021         if(this.multiple){
12022             this.clearItem();
12023             return;
12024         }
12025         
12026         this.setValue(this.originalValue);
12027         this.clearInvalid();
12028         this.lastData = false;
12029         if (this.view) {
12030             this.view.clearSelections();
12031         }
12032     },
12033     // private
12034     findRecord : function(prop, value){
12035         var record;
12036         if(this.store.getCount() > 0){
12037             this.store.each(function(r){
12038                 if(r.data[prop] == value){
12039                     record = r;
12040                     return false;
12041                 }
12042                 return true;
12043             });
12044         }
12045         return record;
12046     },
12047     
12048     getName: function()
12049     {
12050         // returns hidden if it's set..
12051         if (!this.rendered) {return ''};
12052         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12053         
12054     },
12055     // private
12056     onViewMove : function(e, t){
12057         this.inKeyMode = false;
12058     },
12059
12060     // private
12061     onViewOver : function(e, t){
12062         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12063             return;
12064         }
12065         var item = this.view.findItemFromChild(t);
12066         
12067         if(item){
12068             var index = this.view.indexOf(item);
12069             this.select(index, false);
12070         }
12071     },
12072
12073     // private
12074     onViewClick : function(view, doFocus, el, e)
12075     {
12076         var index = this.view.getSelectedIndexes()[0];
12077         
12078         var r = this.store.getAt(index);
12079         
12080         if(this.tickable){
12081             
12082             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12083                 return;
12084             }
12085             
12086             var rm = false;
12087             var _this = this;
12088             
12089             Roo.each(this.tickItems, function(v,k){
12090                 
12091                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12092                     _this.tickItems.splice(k, 1);
12093                     
12094                     if(typeof(e) == 'undefined' && view == false){
12095                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12096                     }
12097                     
12098                     rm = true;
12099                     return;
12100                 }
12101             });
12102             
12103             if(rm){
12104                 return;
12105             }
12106             
12107             this.tickItems.push(r.data);
12108             
12109             if(typeof(e) == 'undefined' && view == false){
12110                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12111             }
12112                     
12113             return;
12114         }
12115         
12116         if(r){
12117             this.onSelect(r, index);
12118         }
12119         if(doFocus !== false && !this.blockFocus){
12120             this.inputEl().focus();
12121         }
12122     },
12123
12124     // private
12125     restrictHeight : function(){
12126         //this.innerList.dom.style.height = '';
12127         //var inner = this.innerList.dom;
12128         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12129         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12130         //this.list.beginUpdate();
12131         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12132         this.list.alignTo(this.inputEl(), this.listAlign);
12133         this.list.alignTo(this.inputEl(), this.listAlign);
12134         //this.list.endUpdate();
12135     },
12136
12137     // private
12138     onEmptyResults : function(){
12139         
12140         if(this.tickable && this.editable){
12141             this.restrictHeight();
12142             return;
12143         }
12144         
12145         this.collapse();
12146     },
12147
12148     /**
12149      * Returns true if the dropdown list is expanded, else false.
12150      */
12151     isExpanded : function(){
12152         return this.list.isVisible();
12153     },
12154
12155     /**
12156      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12157      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12158      * @param {String} value The data value of the item to select
12159      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12160      * selected item if it is not currently in view (defaults to true)
12161      * @return {Boolean} True if the value matched an item in the list, else false
12162      */
12163     selectByValue : function(v, scrollIntoView){
12164         if(v !== undefined && v !== null){
12165             var r = this.findRecord(this.valueField || this.displayField, v);
12166             if(r){
12167                 this.select(this.store.indexOf(r), scrollIntoView);
12168                 return true;
12169             }
12170         }
12171         return false;
12172     },
12173
12174     /**
12175      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12176      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12177      * @param {Number} index The zero-based index of the list item to select
12178      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12179      * selected item if it is not currently in view (defaults to true)
12180      */
12181     select : function(index, scrollIntoView){
12182         this.selectedIndex = index;
12183         this.view.select(index);
12184         if(scrollIntoView !== false){
12185             var el = this.view.getNode(index);
12186             /*
12187              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12188              */
12189             if(el){
12190                 this.list.scrollChildIntoView(el, false);
12191             }
12192         }
12193     },
12194
12195     // private
12196     selectNext : function(){
12197         var ct = this.store.getCount();
12198         if(ct > 0){
12199             if(this.selectedIndex == -1){
12200                 this.select(0);
12201             }else if(this.selectedIndex < ct-1){
12202                 this.select(this.selectedIndex+1);
12203             }
12204         }
12205     },
12206
12207     // private
12208     selectPrev : function(){
12209         var ct = this.store.getCount();
12210         if(ct > 0){
12211             if(this.selectedIndex == -1){
12212                 this.select(0);
12213             }else if(this.selectedIndex != 0){
12214                 this.select(this.selectedIndex-1);
12215             }
12216         }
12217     },
12218
12219     // private
12220     onKeyUp : function(e){
12221         if(this.editable !== false && !e.isSpecialKey()){
12222             this.lastKey = e.getKey();
12223             this.dqTask.delay(this.queryDelay);
12224         }
12225     },
12226
12227     // private
12228     validateBlur : function(){
12229         return !this.list || !this.list.isVisible();   
12230     },
12231
12232     // private
12233     initQuery : function(){
12234         
12235         var v = this.getRawValue();
12236         
12237         if(this.tickable && this.editable){
12238             v = this.tickableInputEl().getValue();
12239         }
12240         
12241         this.doQuery(v);
12242     },
12243
12244     // private
12245     doForce : function(){
12246         if(this.inputEl().dom.value.length > 0){
12247             this.inputEl().dom.value =
12248                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12249              
12250         }
12251     },
12252
12253     /**
12254      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12255      * query allowing the query action to be canceled if needed.
12256      * @param {String} query The SQL query to execute
12257      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12258      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12259      * saved in the current store (defaults to false)
12260      */
12261     doQuery : function(q, forceAll){
12262         
12263         if(q === undefined || q === null){
12264             q = '';
12265         }
12266         var qe = {
12267             query: q,
12268             forceAll: forceAll,
12269             combo: this,
12270             cancel:false
12271         };
12272         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12273             return false;
12274         }
12275         q = qe.query;
12276         
12277         forceAll = qe.forceAll;
12278         if(forceAll === true || (q.length >= this.minChars)){
12279             
12280             this.hasQuery = true;
12281             
12282             if(this.lastQuery != q || this.alwaysQuery){
12283                 this.lastQuery = q;
12284                 if(this.mode == 'local'){
12285                     this.selectedIndex = -1;
12286                     if(forceAll){
12287                         this.store.clearFilter();
12288                     }else{
12289                         
12290                         if(this.specialFilter){
12291                             this.fireEvent('specialfilter', this);
12292                             this.onLoad();
12293                             return;
12294                         }
12295                         
12296                         this.store.filter(this.displayField, q);
12297                     }
12298                     
12299                     this.store.fireEvent("datachanged", this.store);
12300                     
12301                     this.onLoad();
12302                     
12303                     
12304                 }else{
12305                     
12306                     this.store.baseParams[this.queryParam] = q;
12307                     
12308                     var options = {params : this.getParams(q)};
12309                     
12310                     if(this.loadNext){
12311                         options.add = true;
12312                         options.params.start = this.page * this.pageSize;
12313                     }
12314                     
12315                     this.store.load(options);
12316                     
12317                     /*
12318                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12319                      *  we should expand the list on onLoad
12320                      *  so command out it
12321                      */
12322 //                    this.expand();
12323                 }
12324             }else{
12325                 this.selectedIndex = -1;
12326                 this.onLoad();   
12327             }
12328         }
12329         
12330         this.loadNext = false;
12331     },
12332     
12333     // private
12334     getParams : function(q){
12335         var p = {};
12336         //p[this.queryParam] = q;
12337         
12338         if(this.pageSize){
12339             p.start = 0;
12340             p.limit = this.pageSize;
12341         }
12342         return p;
12343     },
12344
12345     /**
12346      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12347      */
12348     collapse : function(){
12349         if(!this.isExpanded()){
12350             return;
12351         }
12352         
12353         this.list.hide();
12354         
12355         if(this.tickable){
12356             this.hasFocus = false;
12357             this.okBtn.hide();
12358             this.cancelBtn.hide();
12359             this.trigger.show();
12360             
12361             if(this.editable){
12362                 this.tickableInputEl().dom.value = '';
12363                 this.tickableInputEl().blur();
12364             }
12365             
12366         }
12367         
12368         Roo.get(document).un('mousedown', this.collapseIf, this);
12369         Roo.get(document).un('mousewheel', this.collapseIf, this);
12370         if (!this.editable) {
12371             Roo.get(document).un('keydown', this.listKeyPress, this);
12372         }
12373         this.fireEvent('collapse', this);
12374     },
12375
12376     // private
12377     collapseIf : function(e){
12378         var in_combo  = e.within(this.el);
12379         var in_list =  e.within(this.list);
12380         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12381         
12382         if (in_combo || in_list || is_list) {
12383             //e.stopPropagation();
12384             return;
12385         }
12386         
12387         if(this.tickable){
12388             this.onTickableFooterButtonClick(e, false, false);
12389         }
12390
12391         this.collapse();
12392         
12393     },
12394
12395     /**
12396      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12397      */
12398     expand : function(){
12399        
12400         if(this.isExpanded() || !this.hasFocus){
12401             return;
12402         }
12403         
12404         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12405         this.list.setWidth(lw);
12406         
12407         
12408          Roo.log('expand');
12409         
12410         this.list.show();
12411         
12412         this.restrictHeight();
12413         
12414         if(this.tickable){
12415             
12416             this.tickItems = Roo.apply([], this.item);
12417             
12418             this.okBtn.show();
12419             this.cancelBtn.show();
12420             this.trigger.hide();
12421             
12422             if(this.editable){
12423                 this.tickableInputEl().focus();
12424             }
12425             
12426         }
12427         
12428         Roo.get(document).on('mousedown', this.collapseIf, this);
12429         Roo.get(document).on('mousewheel', this.collapseIf, this);
12430         if (!this.editable) {
12431             Roo.get(document).on('keydown', this.listKeyPress, this);
12432         }
12433         
12434         this.fireEvent('expand', this);
12435     },
12436
12437     // private
12438     // Implements the default empty TriggerField.onTriggerClick function
12439     onTriggerClick : function(e)
12440     {
12441         Roo.log('trigger click');
12442         
12443         if(this.disabled || !this.triggerList){
12444             return;
12445         }
12446         
12447         this.page = 0;
12448         this.loadNext = false;
12449         
12450         if(this.isExpanded()){
12451             this.collapse();
12452             if (!this.blockFocus) {
12453                 this.inputEl().focus();
12454             }
12455             
12456         }else {
12457             this.hasFocus = true;
12458             if(this.triggerAction == 'all') {
12459                 this.doQuery(this.allQuery, true);
12460             } else {
12461                 this.doQuery(this.getRawValue());
12462             }
12463             if (!this.blockFocus) {
12464                 this.inputEl().focus();
12465             }
12466         }
12467     },
12468     
12469     onTickableTriggerClick : function(e)
12470     {
12471         if(this.disabled){
12472             return;
12473         }
12474         
12475         this.page = 0;
12476         this.loadNext = false;
12477         this.hasFocus = true;
12478         
12479         if(this.triggerAction == 'all') {
12480             this.doQuery(this.allQuery, true);
12481         } else {
12482             this.doQuery(this.getRawValue());
12483         }
12484     },
12485     
12486     onSearchFieldClick : function(e)
12487     {
12488         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12489             this.onTickableFooterButtonClick(e, false, false);
12490             return;
12491         }
12492         
12493         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12494             return;
12495         }
12496         
12497         this.page = 0;
12498         this.loadNext = false;
12499         this.hasFocus = true;
12500         
12501         if(this.triggerAction == 'all') {
12502             this.doQuery(this.allQuery, true);
12503         } else {
12504             this.doQuery(this.getRawValue());
12505         }
12506     },
12507     
12508     listKeyPress : function(e)
12509     {
12510         //Roo.log('listkeypress');
12511         // scroll to first matching element based on key pres..
12512         if (e.isSpecialKey()) {
12513             return false;
12514         }
12515         var k = String.fromCharCode(e.getKey()).toUpperCase();
12516         //Roo.log(k);
12517         var match  = false;
12518         var csel = this.view.getSelectedNodes();
12519         var cselitem = false;
12520         if (csel.length) {
12521             var ix = this.view.indexOf(csel[0]);
12522             cselitem  = this.store.getAt(ix);
12523             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12524                 cselitem = false;
12525             }
12526             
12527         }
12528         
12529         this.store.each(function(v) { 
12530             if (cselitem) {
12531                 // start at existing selection.
12532                 if (cselitem.id == v.id) {
12533                     cselitem = false;
12534                 }
12535                 return true;
12536             }
12537                 
12538             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12539                 match = this.store.indexOf(v);
12540                 return false;
12541             }
12542             return true;
12543         }, this);
12544         
12545         if (match === false) {
12546             return true; // no more action?
12547         }
12548         // scroll to?
12549         this.view.select(match);
12550         var sn = Roo.get(this.view.getSelectedNodes()[0])
12551         sn.scrollIntoView(sn.dom.parentNode, false);
12552     },
12553     
12554     onViewScroll : function(e, t){
12555         
12556         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){
12557             return;
12558         }
12559         
12560         this.hasQuery = true;
12561         
12562         this.loading = this.list.select('.loading', true).first();
12563         
12564         if(this.loading === null){
12565             this.list.createChild({
12566                 tag: 'div',
12567                 cls: 'loading select2-more-results select2-active',
12568                 html: 'Loading more results...'
12569             })
12570             
12571             this.loading = this.list.select('.loading', true).first();
12572             
12573             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12574             
12575             this.loading.hide();
12576         }
12577         
12578         this.loading.show();
12579         
12580         var _combo = this;
12581         
12582         this.page++;
12583         this.loadNext = true;
12584         
12585         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12586         
12587         return;
12588     },
12589     
12590     addItem : function(o)
12591     {   
12592         var dv = ''; // display value
12593         
12594         if (this.displayField) {
12595             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12596         } else {
12597             // this is an error condition!!!
12598             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12599         }
12600         
12601         if(!dv.length){
12602             return;
12603         }
12604         
12605         var choice = this.choices.createChild({
12606             tag: 'li',
12607             cls: 'select2-search-choice',
12608             cn: [
12609                 {
12610                     tag: 'div',
12611                     html: dv
12612                 },
12613                 {
12614                     tag: 'a',
12615                     href: '#',
12616                     cls: 'select2-search-choice-close',
12617                     tabindex: '-1'
12618                 }
12619             ]
12620             
12621         }, this.searchField);
12622         
12623         var close = choice.select('a.select2-search-choice-close', true).first()
12624         
12625         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12626         
12627         this.item.push(o);
12628         
12629         this.lastData = o;
12630         
12631         this.syncValue();
12632         
12633         this.inputEl().dom.value = '';
12634         
12635         this.validate();
12636     },
12637     
12638     onRemoveItem : function(e, _self, o)
12639     {
12640         e.preventDefault();
12641         
12642         this.lastItem = Roo.apply([], this.item);
12643         
12644         var index = this.item.indexOf(o.data) * 1;
12645         
12646         if( index < 0){
12647             Roo.log('not this item?!');
12648             return;
12649         }
12650         
12651         this.item.splice(index, 1);
12652         o.item.remove();
12653         
12654         this.syncValue();
12655         
12656         this.fireEvent('remove', this, e);
12657         
12658         this.validate();
12659         
12660     },
12661     
12662     syncValue : function()
12663     {
12664         if(!this.item.length){
12665             this.clearValue();
12666             return;
12667         }
12668             
12669         var value = [];
12670         var _this = this;
12671         Roo.each(this.item, function(i){
12672             if(_this.valueField){
12673                 value.push(i[_this.valueField]);
12674                 return;
12675             }
12676
12677             value.push(i);
12678         });
12679
12680         this.value = value.join(',');
12681
12682         if(this.hiddenField){
12683             this.hiddenField.dom.value = this.value;
12684         }
12685         
12686         this.store.fireEvent("datachanged", this.store);
12687     },
12688     
12689     clearItem : function()
12690     {
12691         if(!this.multiple){
12692             return;
12693         }
12694         
12695         this.item = [];
12696         
12697         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12698            c.remove();
12699         });
12700         
12701         this.syncValue();
12702         
12703         this.validate();
12704     },
12705     
12706     inputEl: function ()
12707     {
12708         if(this.tickable){
12709             return this.searchField;
12710         }
12711         return this.el.select('input.form-control',true).first();
12712     },
12713     
12714     
12715     onTickableFooterButtonClick : function(e, btn, el)
12716     {
12717         e.preventDefault();
12718         
12719         this.lastItem = Roo.apply([], this.item);
12720         
12721         if(btn && btn.name == 'cancel'){
12722             this.tickItems = Roo.apply([], this.item);
12723             this.collapse();
12724             return;
12725         }
12726         
12727         this.clearItem();
12728         
12729         var _this = this;
12730         
12731         Roo.each(this.tickItems, function(o){
12732             _this.addItem(o);
12733         });
12734         
12735         this.collapse();
12736         
12737     },
12738     
12739     validate : function()
12740     {
12741         var v = this.getRawValue();
12742         
12743         if(this.multiple){
12744             v = this.getValue();
12745         }
12746         
12747         if(this.disabled || this.allowBlank || v.length){
12748             this.markValid();
12749             return true;
12750         }
12751         
12752         this.markInvalid();
12753         return false;
12754     },
12755     
12756     tickableInputEl : function()
12757     {
12758         if(!this.tickable || !this.editable){
12759             return this.inputEl();
12760         }
12761         
12762         return this.inputEl().select('.select2-search-field-input', true).first();
12763     }
12764     
12765     
12766
12767     /** 
12768     * @cfg {Boolean} grow 
12769     * @hide 
12770     */
12771     /** 
12772     * @cfg {Number} growMin 
12773     * @hide 
12774     */
12775     /** 
12776     * @cfg {Number} growMax 
12777     * @hide 
12778     */
12779     /**
12780      * @hide
12781      * @method autoSize
12782      */
12783 });
12784 /*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794
12795 /**
12796  * @class Roo.View
12797  * @extends Roo.util.Observable
12798  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12799  * This class also supports single and multi selection modes. <br>
12800  * Create a data model bound view:
12801  <pre><code>
12802  var store = new Roo.data.Store(...);
12803
12804  var view = new Roo.View({
12805     el : "my-element",
12806     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12807  
12808     singleSelect: true,
12809     selectedClass: "ydataview-selected",
12810     store: store
12811  });
12812
12813  // listen for node click?
12814  view.on("click", function(vw, index, node, e){
12815  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12816  });
12817
12818  // load XML data
12819  dataModel.load("foobar.xml");
12820  </code></pre>
12821  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12822  * <br><br>
12823  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12824  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12825  * 
12826  * Note: old style constructor is still suported (container, template, config)
12827  * 
12828  * @constructor
12829  * Create a new View
12830  * @param {Object} config The config object
12831  * 
12832  */
12833 Roo.View = function(config, depreciated_tpl, depreciated_config){
12834     
12835     this.parent = false;
12836     
12837     if (typeof(depreciated_tpl) == 'undefined') {
12838         // new way.. - universal constructor.
12839         Roo.apply(this, config);
12840         this.el  = Roo.get(this.el);
12841     } else {
12842         // old format..
12843         this.el  = Roo.get(config);
12844         this.tpl = depreciated_tpl;
12845         Roo.apply(this, depreciated_config);
12846     }
12847     this.wrapEl  = this.el.wrap().wrap();
12848     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12849     
12850     
12851     if(typeof(this.tpl) == "string"){
12852         this.tpl = new Roo.Template(this.tpl);
12853     } else {
12854         // support xtype ctors..
12855         this.tpl = new Roo.factory(this.tpl, Roo);
12856     }
12857     
12858     
12859     this.tpl.compile();
12860     
12861     /** @private */
12862     this.addEvents({
12863         /**
12864          * @event beforeclick
12865          * Fires before a click is processed. Returns false to cancel the default action.
12866          * @param {Roo.View} this
12867          * @param {Number} index The index of the target node
12868          * @param {HTMLElement} node The target node
12869          * @param {Roo.EventObject} e The raw event object
12870          */
12871             "beforeclick" : true,
12872         /**
12873          * @event click
12874          * Fires when a template node is clicked.
12875          * @param {Roo.View} this
12876          * @param {Number} index The index of the target node
12877          * @param {HTMLElement} node The target node
12878          * @param {Roo.EventObject} e The raw event object
12879          */
12880             "click" : true,
12881         /**
12882          * @event dblclick
12883          * Fires when a template node is double clicked.
12884          * @param {Roo.View} this
12885          * @param {Number} index The index of the target node
12886          * @param {HTMLElement} node The target node
12887          * @param {Roo.EventObject} e The raw event object
12888          */
12889             "dblclick" : true,
12890         /**
12891          * @event contextmenu
12892          * Fires when a template node is right clicked.
12893          * @param {Roo.View} this
12894          * @param {Number} index The index of the target node
12895          * @param {HTMLElement} node The target node
12896          * @param {Roo.EventObject} e The raw event object
12897          */
12898             "contextmenu" : true,
12899         /**
12900          * @event selectionchange
12901          * Fires when the selected nodes change.
12902          * @param {Roo.View} this
12903          * @param {Array} selections Array of the selected nodes
12904          */
12905             "selectionchange" : true,
12906     
12907         /**
12908          * @event beforeselect
12909          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12910          * @param {Roo.View} this
12911          * @param {HTMLElement} node The node to be selected
12912          * @param {Array} selections Array of currently selected nodes
12913          */
12914             "beforeselect" : true,
12915         /**
12916          * @event preparedata
12917          * Fires on every row to render, to allow you to change the data.
12918          * @param {Roo.View} this
12919          * @param {Object} data to be rendered (change this)
12920          */
12921           "preparedata" : true
12922           
12923           
12924         });
12925
12926
12927
12928     this.el.on({
12929         "click": this.onClick,
12930         "dblclick": this.onDblClick,
12931         "contextmenu": this.onContextMenu,
12932         scope:this
12933     });
12934
12935     this.selections = [];
12936     this.nodes = [];
12937     this.cmp = new Roo.CompositeElementLite([]);
12938     if(this.store){
12939         this.store = Roo.factory(this.store, Roo.data);
12940         this.setStore(this.store, true);
12941     }
12942     
12943     if ( this.footer && this.footer.xtype) {
12944            
12945          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12946         
12947         this.footer.dataSource = this.store
12948         this.footer.container = fctr;
12949         this.footer = Roo.factory(this.footer, Roo);
12950         fctr.insertFirst(this.el);
12951         
12952         // this is a bit insane - as the paging toolbar seems to detach the el..
12953 //        dom.parentNode.parentNode.parentNode
12954          // they get detached?
12955     }
12956     
12957     
12958     Roo.View.superclass.constructor.call(this);
12959     
12960     
12961 };
12962
12963 Roo.extend(Roo.View, Roo.util.Observable, {
12964     
12965      /**
12966      * @cfg {Roo.data.Store} store Data store to load data from.
12967      */
12968     store : false,
12969     
12970     /**
12971      * @cfg {String|Roo.Element} el The container element.
12972      */
12973     el : '',
12974     
12975     /**
12976      * @cfg {String|Roo.Template} tpl The template used by this View 
12977      */
12978     tpl : false,
12979     /**
12980      * @cfg {String} dataName the named area of the template to use as the data area
12981      *                          Works with domtemplates roo-name="name"
12982      */
12983     dataName: false,
12984     /**
12985      * @cfg {String} selectedClass The css class to add to selected nodes
12986      */
12987     selectedClass : "x-view-selected",
12988      /**
12989      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12990      */
12991     emptyText : "",
12992     
12993     /**
12994      * @cfg {String} text to display on mask (default Loading)
12995      */
12996     mask : false,
12997     /**
12998      * @cfg {Boolean} multiSelect Allow multiple selection
12999      */
13000     multiSelect : false,
13001     /**
13002      * @cfg {Boolean} singleSelect Allow single selection
13003      */
13004     singleSelect:  false,
13005     
13006     /**
13007      * @cfg {Boolean} toggleSelect - selecting 
13008      */
13009     toggleSelect : false,
13010     
13011     /**
13012      * @cfg {Boolean} tickable - selecting 
13013      */
13014     tickable : false,
13015     
13016     /**
13017      * Returns the element this view is bound to.
13018      * @return {Roo.Element}
13019      */
13020     getEl : function(){
13021         return this.wrapEl;
13022     },
13023     
13024     
13025
13026     /**
13027      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13028      */
13029     refresh : function(){
13030         //Roo.log('refresh');
13031         var t = this.tpl;
13032         
13033         // if we are using something like 'domtemplate', then
13034         // the what gets used is:
13035         // t.applySubtemplate(NAME, data, wrapping data..)
13036         // the outer template then get' applied with
13037         //     the store 'extra data'
13038         // and the body get's added to the
13039         //      roo-name="data" node?
13040         //      <span class='roo-tpl-{name}'></span> ?????
13041         
13042         
13043         
13044         this.clearSelections();
13045         this.el.update("");
13046         var html = [];
13047         var records = this.store.getRange();
13048         if(records.length < 1) {
13049             
13050             // is this valid??  = should it render a template??
13051             
13052             this.el.update(this.emptyText);
13053             return;
13054         }
13055         var el = this.el;
13056         if (this.dataName) {
13057             this.el.update(t.apply(this.store.meta)); //????
13058             el = this.el.child('.roo-tpl-' + this.dataName);
13059         }
13060         
13061         for(var i = 0, len = records.length; i < len; i++){
13062             var data = this.prepareData(records[i].data, i, records[i]);
13063             this.fireEvent("preparedata", this, data, i, records[i]);
13064             
13065             var d = Roo.apply({}, data);
13066             
13067             if(this.tickable){
13068                 Roo.apply(d, {'roo-id' : Roo.id()});
13069                 
13070                 var _this = this;
13071             
13072                 Roo.each(this.parent.item, function(item){
13073                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13074                         return;
13075                     }
13076                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13077                 });
13078             }
13079             
13080             html[html.length] = Roo.util.Format.trim(
13081                 this.dataName ?
13082                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13083                     t.apply(d)
13084             );
13085         }
13086         
13087         
13088         
13089         el.update(html.join(""));
13090         this.nodes = el.dom.childNodes;
13091         this.updateIndexes(0);
13092     },
13093     
13094
13095     /**
13096      * Function to override to reformat the data that is sent to
13097      * the template for each node.
13098      * DEPRICATED - use the preparedata event handler.
13099      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13100      * a JSON object for an UpdateManager bound view).
13101      */
13102     prepareData : function(data, index, record)
13103     {
13104         this.fireEvent("preparedata", this, data, index, record);
13105         return data;
13106     },
13107
13108     onUpdate : function(ds, record){
13109         // Roo.log('on update');   
13110         this.clearSelections();
13111         var index = this.store.indexOf(record);
13112         var n = this.nodes[index];
13113         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13114         n.parentNode.removeChild(n);
13115         this.updateIndexes(index, index);
13116     },
13117
13118     
13119     
13120 // --------- FIXME     
13121     onAdd : function(ds, records, index)
13122     {
13123         //Roo.log(['on Add', ds, records, index] );        
13124         this.clearSelections();
13125         if(this.nodes.length == 0){
13126             this.refresh();
13127             return;
13128         }
13129         var n = this.nodes[index];
13130         for(var i = 0, len = records.length; i < len; i++){
13131             var d = this.prepareData(records[i].data, i, records[i]);
13132             if(n){
13133                 this.tpl.insertBefore(n, d);
13134             }else{
13135                 
13136                 this.tpl.append(this.el, d);
13137             }
13138         }
13139         this.updateIndexes(index);
13140     },
13141
13142     onRemove : function(ds, record, index){
13143        // Roo.log('onRemove');
13144         this.clearSelections();
13145         var el = this.dataName  ?
13146             this.el.child('.roo-tpl-' + this.dataName) :
13147             this.el; 
13148         
13149         el.dom.removeChild(this.nodes[index]);
13150         this.updateIndexes(index);
13151     },
13152
13153     /**
13154      * Refresh an individual node.
13155      * @param {Number} index
13156      */
13157     refreshNode : function(index){
13158         this.onUpdate(this.store, this.store.getAt(index));
13159     },
13160
13161     updateIndexes : function(startIndex, endIndex){
13162         var ns = this.nodes;
13163         startIndex = startIndex || 0;
13164         endIndex = endIndex || ns.length - 1;
13165         for(var i = startIndex; i <= endIndex; i++){
13166             ns[i].nodeIndex = i;
13167         }
13168     },
13169
13170     /**
13171      * Changes the data store this view uses and refresh the view.
13172      * @param {Store} store
13173      */
13174     setStore : function(store, initial){
13175         if(!initial && this.store){
13176             this.store.un("datachanged", this.refresh);
13177             this.store.un("add", this.onAdd);
13178             this.store.un("remove", this.onRemove);
13179             this.store.un("update", this.onUpdate);
13180             this.store.un("clear", this.refresh);
13181             this.store.un("beforeload", this.onBeforeLoad);
13182             this.store.un("load", this.onLoad);
13183             this.store.un("loadexception", this.onLoad);
13184         }
13185         if(store){
13186           
13187             store.on("datachanged", this.refresh, this);
13188             store.on("add", this.onAdd, this);
13189             store.on("remove", this.onRemove, this);
13190             store.on("update", this.onUpdate, this);
13191             store.on("clear", this.refresh, this);
13192             store.on("beforeload", this.onBeforeLoad, this);
13193             store.on("load", this.onLoad, this);
13194             store.on("loadexception", this.onLoad, this);
13195         }
13196         
13197         if(store){
13198             this.refresh();
13199         }
13200     },
13201     /**
13202      * onbeforeLoad - masks the loading area.
13203      *
13204      */
13205     onBeforeLoad : function(store,opts)
13206     {
13207          //Roo.log('onBeforeLoad');   
13208         if (!opts.add) {
13209             this.el.update("");
13210         }
13211         this.el.mask(this.mask ? this.mask : "Loading" ); 
13212     },
13213     onLoad : function ()
13214     {
13215         this.el.unmask();
13216     },
13217     
13218
13219     /**
13220      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13221      * @param {HTMLElement} node
13222      * @return {HTMLElement} The template node
13223      */
13224     findItemFromChild : function(node){
13225         var el = this.dataName  ?
13226             this.el.child('.roo-tpl-' + this.dataName,true) :
13227             this.el.dom; 
13228         
13229         if(!node || node.parentNode == el){
13230                     return node;
13231             }
13232             var p = node.parentNode;
13233             while(p && p != el){
13234             if(p.parentNode == el){
13235                 return p;
13236             }
13237             p = p.parentNode;
13238         }
13239             return null;
13240     },
13241
13242     /** @ignore */
13243     onClick : function(e){
13244         var item = this.findItemFromChild(e.getTarget());
13245         if(item){
13246             var index = this.indexOf(item);
13247             if(this.onItemClick(item, index, e) !== false){
13248                 this.fireEvent("click", this, index, item, e);
13249             }
13250         }else{
13251             this.clearSelections();
13252         }
13253     },
13254
13255     /** @ignore */
13256     onContextMenu : function(e){
13257         var item = this.findItemFromChild(e.getTarget());
13258         if(item){
13259             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13260         }
13261     },
13262
13263     /** @ignore */
13264     onDblClick : function(e){
13265         var item = this.findItemFromChild(e.getTarget());
13266         if(item){
13267             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13268         }
13269     },
13270
13271     onItemClick : function(item, index, e)
13272     {
13273         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13274             return false;
13275         }
13276         if (this.toggleSelect) {
13277             var m = this.isSelected(item) ? 'unselect' : 'select';
13278             //Roo.log(m);
13279             var _t = this;
13280             _t[m](item, true, false);
13281             return true;
13282         }
13283         if(this.multiSelect || this.singleSelect){
13284             if(this.multiSelect && e.shiftKey && this.lastSelection){
13285                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13286             }else{
13287                 this.select(item, this.multiSelect && e.ctrlKey);
13288                 this.lastSelection = item;
13289             }
13290             
13291             if(!this.tickable){
13292                 e.preventDefault();
13293             }
13294             
13295         }
13296         return true;
13297     },
13298
13299     /**
13300      * Get the number of selected nodes.
13301      * @return {Number}
13302      */
13303     getSelectionCount : function(){
13304         return this.selections.length;
13305     },
13306
13307     /**
13308      * Get the currently selected nodes.
13309      * @return {Array} An array of HTMLElements
13310      */
13311     getSelectedNodes : function(){
13312         return this.selections;
13313     },
13314
13315     /**
13316      * Get the indexes of the selected nodes.
13317      * @return {Array}
13318      */
13319     getSelectedIndexes : function(){
13320         var indexes = [], s = this.selections;
13321         for(var i = 0, len = s.length; i < len; i++){
13322             indexes.push(s[i].nodeIndex);
13323         }
13324         return indexes;
13325     },
13326
13327     /**
13328      * Clear all selections
13329      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13330      */
13331     clearSelections : function(suppressEvent){
13332         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13333             this.cmp.elements = this.selections;
13334             this.cmp.removeClass(this.selectedClass);
13335             this.selections = [];
13336             if(!suppressEvent){
13337                 this.fireEvent("selectionchange", this, this.selections);
13338             }
13339         }
13340     },
13341
13342     /**
13343      * Returns true if the passed node is selected
13344      * @param {HTMLElement/Number} node The node or node index
13345      * @return {Boolean}
13346      */
13347     isSelected : function(node){
13348         var s = this.selections;
13349         if(s.length < 1){
13350             return false;
13351         }
13352         node = this.getNode(node);
13353         return s.indexOf(node) !== -1;
13354     },
13355
13356     /**
13357      * Selects nodes.
13358      * @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
13359      * @param {Boolean} keepExisting (optional) true to keep existing selections
13360      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13361      */
13362     select : function(nodeInfo, keepExisting, suppressEvent){
13363         if(nodeInfo instanceof Array){
13364             if(!keepExisting){
13365                 this.clearSelections(true);
13366             }
13367             for(var i = 0, len = nodeInfo.length; i < len; i++){
13368                 this.select(nodeInfo[i], true, true);
13369             }
13370             return;
13371         } 
13372         var node = this.getNode(nodeInfo);
13373         if(!node || this.isSelected(node)){
13374             return; // already selected.
13375         }
13376         if(!keepExisting){
13377             this.clearSelections(true);
13378         }
13379         
13380         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13381             Roo.fly(node).addClass(this.selectedClass);
13382             this.selections.push(node);
13383             if(!suppressEvent){
13384                 this.fireEvent("selectionchange", this, this.selections);
13385             }
13386         }
13387         
13388         
13389     },
13390       /**
13391      * Unselects nodes.
13392      * @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
13393      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13394      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13395      */
13396     unselect : function(nodeInfo, keepExisting, suppressEvent)
13397     {
13398         if(nodeInfo instanceof Array){
13399             Roo.each(this.selections, function(s) {
13400                 this.unselect(s, nodeInfo);
13401             }, this);
13402             return;
13403         }
13404         var node = this.getNode(nodeInfo);
13405         if(!node || !this.isSelected(node)){
13406             //Roo.log("not selected");
13407             return; // not selected.
13408         }
13409         // fireevent???
13410         var ns = [];
13411         Roo.each(this.selections, function(s) {
13412             if (s == node ) {
13413                 Roo.fly(node).removeClass(this.selectedClass);
13414
13415                 return;
13416             }
13417             ns.push(s);
13418         },this);
13419         
13420         this.selections= ns;
13421         this.fireEvent("selectionchange", this, this.selections);
13422     },
13423
13424     /**
13425      * Gets a template node.
13426      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13427      * @return {HTMLElement} The node or null if it wasn't found
13428      */
13429     getNode : function(nodeInfo){
13430         if(typeof nodeInfo == "string"){
13431             return document.getElementById(nodeInfo);
13432         }else if(typeof nodeInfo == "number"){
13433             return this.nodes[nodeInfo];
13434         }
13435         return nodeInfo;
13436     },
13437
13438     /**
13439      * Gets a range template nodes.
13440      * @param {Number} startIndex
13441      * @param {Number} endIndex
13442      * @return {Array} An array of nodes
13443      */
13444     getNodes : function(start, end){
13445         var ns = this.nodes;
13446         start = start || 0;
13447         end = typeof end == "undefined" ? ns.length - 1 : end;
13448         var nodes = [];
13449         if(start <= end){
13450             for(var i = start; i <= end; i++){
13451                 nodes.push(ns[i]);
13452             }
13453         } else{
13454             for(var i = start; i >= end; i--){
13455                 nodes.push(ns[i]);
13456             }
13457         }
13458         return nodes;
13459     },
13460
13461     /**
13462      * Finds the index of the passed node
13463      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13464      * @return {Number} The index of the node or -1
13465      */
13466     indexOf : function(node){
13467         node = this.getNode(node);
13468         if(typeof node.nodeIndex == "number"){
13469             return node.nodeIndex;
13470         }
13471         var ns = this.nodes;
13472         for(var i = 0, len = ns.length; i < len; i++){
13473             if(ns[i] == node){
13474                 return i;
13475             }
13476         }
13477         return -1;
13478     }
13479 });
13480 /*
13481  * - LGPL
13482  *
13483  * based on jquery fullcalendar
13484  * 
13485  */
13486
13487 Roo.bootstrap = Roo.bootstrap || {};
13488 /**
13489  * @class Roo.bootstrap.Calendar
13490  * @extends Roo.bootstrap.Component
13491  * Bootstrap Calendar class
13492  * @cfg {Boolean} loadMask (true|false) default false
13493  * @cfg {Object} header generate the user specific header of the calendar, default false
13494
13495  * @constructor
13496  * Create a new Container
13497  * @param {Object} config The config object
13498  */
13499
13500
13501
13502 Roo.bootstrap.Calendar = function(config){
13503     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13504      this.addEvents({
13505         /**
13506              * @event select
13507              * Fires when a date is selected
13508              * @param {DatePicker} this
13509              * @param {Date} date The selected date
13510              */
13511         'select': true,
13512         /**
13513              * @event monthchange
13514              * Fires when the displayed month changes 
13515              * @param {DatePicker} this
13516              * @param {Date} date The selected month
13517              */
13518         'monthchange': true,
13519         /**
13520              * @event evententer
13521              * Fires when mouse over an event
13522              * @param {Calendar} this
13523              * @param {event} Event
13524              */
13525         'evententer': true,
13526         /**
13527              * @event eventleave
13528              * Fires when the mouse leaves an
13529              * @param {Calendar} this
13530              * @param {event}
13531              */
13532         'eventleave': true,
13533         /**
13534              * @event eventclick
13535              * Fires when the mouse click an
13536              * @param {Calendar} this
13537              * @param {event}
13538              */
13539         'eventclick': true
13540         
13541     });
13542
13543 };
13544
13545 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13546     
13547      /**
13548      * @cfg {Number} startDay
13549      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13550      */
13551     startDay : 0,
13552     
13553     loadMask : false,
13554     
13555     header : false,
13556       
13557     getAutoCreate : function(){
13558         
13559         
13560         var fc_button = function(name, corner, style, content ) {
13561             return Roo.apply({},{
13562                 tag : 'span',
13563                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13564                          (corner.length ?
13565                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13566                             ''
13567                         ),
13568                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13569                 unselectable: 'on'
13570             });
13571         };
13572         
13573         var header = {};
13574         
13575         if(!this.header){
13576             header = {
13577                 tag : 'table',
13578                 cls : 'fc-header',
13579                 style : 'width:100%',
13580                 cn : [
13581                     {
13582                         tag: 'tr',
13583                         cn : [
13584                             {
13585                                 tag : 'td',
13586                                 cls : 'fc-header-left',
13587                                 cn : [
13588                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13589                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13590                                     { tag: 'span', cls: 'fc-header-space' },
13591                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13592
13593
13594                                 ]
13595                             },
13596
13597                             {
13598                                 tag : 'td',
13599                                 cls : 'fc-header-center',
13600                                 cn : [
13601                                     {
13602                                         tag: 'span',
13603                                         cls: 'fc-header-title',
13604                                         cn : {
13605                                             tag: 'H2',
13606                                             html : 'month / year'
13607                                         }
13608                                     }
13609
13610                                 ]
13611                             },
13612                             {
13613                                 tag : 'td',
13614                                 cls : 'fc-header-right',
13615                                 cn : [
13616                               /*      fc_button('month', 'left', '', 'month' ),
13617                                     fc_button('week', '', '', 'week' ),
13618                                     fc_button('day', 'right', '', 'day' )
13619                                 */    
13620
13621                                 ]
13622                             }
13623
13624                         ]
13625                     }
13626                 ]
13627             };
13628         }
13629         
13630         header = this.header;
13631         
13632        
13633         var cal_heads = function() {
13634             var ret = [];
13635             // fixme - handle this.
13636             
13637             for (var i =0; i < Date.dayNames.length; i++) {
13638                 var d = Date.dayNames[i];
13639                 ret.push({
13640                     tag: 'th',
13641                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13642                     html : d.substring(0,3)
13643                 });
13644                 
13645             }
13646             ret[0].cls += ' fc-first';
13647             ret[6].cls += ' fc-last';
13648             return ret;
13649         };
13650         var cal_cell = function(n) {
13651             return  {
13652                 tag: 'td',
13653                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13654                 cn : [
13655                     {
13656                         cn : [
13657                             {
13658                                 cls: 'fc-day-number',
13659                                 html: 'D'
13660                             },
13661                             {
13662                                 cls: 'fc-day-content',
13663                              
13664                                 cn : [
13665                                      {
13666                                         style: 'position: relative;' // height: 17px;
13667                                     }
13668                                 ]
13669                             }
13670                             
13671                             
13672                         ]
13673                     }
13674                 ]
13675                 
13676             }
13677         };
13678         var cal_rows = function() {
13679             
13680             var ret = [];
13681             for (var r = 0; r < 6; r++) {
13682                 var row= {
13683                     tag : 'tr',
13684                     cls : 'fc-week',
13685                     cn : []
13686                 };
13687                 
13688                 for (var i =0; i < Date.dayNames.length; i++) {
13689                     var d = Date.dayNames[i];
13690                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13691
13692                 }
13693                 row.cn[0].cls+=' fc-first';
13694                 row.cn[0].cn[0].style = 'min-height:90px';
13695                 row.cn[6].cls+=' fc-last';
13696                 ret.push(row);
13697                 
13698             }
13699             ret[0].cls += ' fc-first';
13700             ret[4].cls += ' fc-prev-last';
13701             ret[5].cls += ' fc-last';
13702             return ret;
13703             
13704         };
13705         
13706         var cal_table = {
13707             tag: 'table',
13708             cls: 'fc-border-separate',
13709             style : 'width:100%',
13710             cellspacing  : 0,
13711             cn : [
13712                 { 
13713                     tag: 'thead',
13714                     cn : [
13715                         { 
13716                             tag: 'tr',
13717                             cls : 'fc-first fc-last',
13718                             cn : cal_heads()
13719                         }
13720                     ]
13721                 },
13722                 { 
13723                     tag: 'tbody',
13724                     cn : cal_rows()
13725                 }
13726                   
13727             ]
13728         };
13729          
13730          var cfg = {
13731             cls : 'fc fc-ltr',
13732             cn : [
13733                 header,
13734                 {
13735                     cls : 'fc-content',
13736                     style : "position: relative;",
13737                     cn : [
13738                         {
13739                             cls : 'fc-view fc-view-month fc-grid',
13740                             style : 'position: relative',
13741                             unselectable : 'on',
13742                             cn : [
13743                                 {
13744                                     cls : 'fc-event-container',
13745                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13746                                 },
13747                                 cal_table
13748                             ]
13749                         }
13750                     ]
13751     
13752                 }
13753            ] 
13754             
13755         };
13756         
13757          
13758         
13759         return cfg;
13760     },
13761     
13762     
13763     initEvents : function()
13764     {
13765         if(!this.store){
13766             throw "can not find store for calendar";
13767         }
13768         
13769         var mark = {
13770             tag: "div",
13771             cls:"x-dlg-mask",
13772             style: "text-align:center",
13773             cn: [
13774                 {
13775                     tag: "div",
13776                     style: "background-color:white;width:50%;margin:250 auto",
13777                     cn: [
13778                         {
13779                             tag: "img",
13780                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13781                         },
13782                         {
13783                             tag: "span",
13784                             html: "Loading"
13785                         }
13786                         
13787                     ]
13788                 }
13789             ]
13790         }
13791         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13792         
13793         var size = this.el.select('.fc-content', true).first().getSize();
13794         this.maskEl.setSize(size.width, size.height);
13795         this.maskEl.enableDisplayMode("block");
13796         if(!this.loadMask){
13797             this.maskEl.hide();
13798         }
13799         
13800         this.store = Roo.factory(this.store, Roo.data);
13801         this.store.on('load', this.onLoad, this);
13802         this.store.on('beforeload', this.onBeforeLoad, this);
13803         
13804         this.resize();
13805         
13806         this.cells = this.el.select('.fc-day',true);
13807         //Roo.log(this.cells);
13808         this.textNodes = this.el.query('.fc-day-number');
13809         this.cells.addClassOnOver('fc-state-hover');
13810         
13811         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13812         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13813         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13814         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13815         
13816         this.on('monthchange', this.onMonthChange, this);
13817         
13818         this.update(new Date().clearTime());
13819     },
13820     
13821     resize : function() {
13822         var sz  = this.el.getSize();
13823         
13824         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13825         this.el.select('.fc-day-content div',true).setHeight(34);
13826     },
13827     
13828     
13829     // private
13830     showPrevMonth : function(e){
13831         this.update(this.activeDate.add("mo", -1));
13832     },
13833     showToday : function(e){
13834         this.update(new Date().clearTime());
13835     },
13836     // private
13837     showNextMonth : function(e){
13838         this.update(this.activeDate.add("mo", 1));
13839     },
13840
13841     // private
13842     showPrevYear : function(){
13843         this.update(this.activeDate.add("y", -1));
13844     },
13845
13846     // private
13847     showNextYear : function(){
13848         this.update(this.activeDate.add("y", 1));
13849     },
13850
13851     
13852    // private
13853     update : function(date)
13854     {
13855         var vd = this.activeDate;
13856         this.activeDate = date;
13857 //        if(vd && this.el){
13858 //            var t = date.getTime();
13859 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13860 //                Roo.log('using add remove');
13861 //                
13862 //                this.fireEvent('monthchange', this, date);
13863 //                
13864 //                this.cells.removeClass("fc-state-highlight");
13865 //                this.cells.each(function(c){
13866 //                   if(c.dateValue == t){
13867 //                       c.addClass("fc-state-highlight");
13868 //                       setTimeout(function(){
13869 //                            try{c.dom.firstChild.focus();}catch(e){}
13870 //                       }, 50);
13871 //                       return false;
13872 //                   }
13873 //                   return true;
13874 //                });
13875 //                return;
13876 //            }
13877 //        }
13878         
13879         var days = date.getDaysInMonth();
13880         
13881         var firstOfMonth = date.getFirstDateOfMonth();
13882         var startingPos = firstOfMonth.getDay()-this.startDay;
13883         
13884         if(startingPos < this.startDay){
13885             startingPos += 7;
13886         }
13887         
13888         var pm = date.add(Date.MONTH, -1);
13889         var prevStart = pm.getDaysInMonth()-startingPos;
13890 //        
13891         this.cells = this.el.select('.fc-day',true);
13892         this.textNodes = this.el.query('.fc-day-number');
13893         this.cells.addClassOnOver('fc-state-hover');
13894         
13895         var cells = this.cells.elements;
13896         var textEls = this.textNodes;
13897         
13898         Roo.each(cells, function(cell){
13899             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13900         });
13901         
13902         days += startingPos;
13903
13904         // convert everything to numbers so it's fast
13905         var day = 86400000;
13906         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13907         //Roo.log(d);
13908         //Roo.log(pm);
13909         //Roo.log(prevStart);
13910         
13911         var today = new Date().clearTime().getTime();
13912         var sel = date.clearTime().getTime();
13913         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13914         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13915         var ddMatch = this.disabledDatesRE;
13916         var ddText = this.disabledDatesText;
13917         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13918         var ddaysText = this.disabledDaysText;
13919         var format = this.format;
13920         
13921         var setCellClass = function(cal, cell){
13922             cell.row = 0;
13923             cell.events = [];
13924             cell.more = [];
13925             //Roo.log('set Cell Class');
13926             cell.title = "";
13927             var t = d.getTime();
13928             
13929             //Roo.log(d);
13930             
13931             cell.dateValue = t;
13932             if(t == today){
13933                 cell.className += " fc-today";
13934                 cell.className += " fc-state-highlight";
13935                 cell.title = cal.todayText;
13936             }
13937             if(t == sel){
13938                 // disable highlight in other month..
13939                 //cell.className += " fc-state-highlight";
13940                 
13941             }
13942             // disabling
13943             if(t < min) {
13944                 cell.className = " fc-state-disabled";
13945                 cell.title = cal.minText;
13946                 return;
13947             }
13948             if(t > max) {
13949                 cell.className = " fc-state-disabled";
13950                 cell.title = cal.maxText;
13951                 return;
13952             }
13953             if(ddays){
13954                 if(ddays.indexOf(d.getDay()) != -1){
13955                     cell.title = ddaysText;
13956                     cell.className = " fc-state-disabled";
13957                 }
13958             }
13959             if(ddMatch && format){
13960                 var fvalue = d.dateFormat(format);
13961                 if(ddMatch.test(fvalue)){
13962                     cell.title = ddText.replace("%0", fvalue);
13963                     cell.className = " fc-state-disabled";
13964                 }
13965             }
13966             
13967             if (!cell.initialClassName) {
13968                 cell.initialClassName = cell.dom.className;
13969             }
13970             
13971             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13972         };
13973
13974         var i = 0;
13975         
13976         for(; i < startingPos; i++) {
13977             textEls[i].innerHTML = (++prevStart);
13978             d.setDate(d.getDate()+1);
13979             
13980             cells[i].className = "fc-past fc-other-month";
13981             setCellClass(this, cells[i]);
13982         }
13983         
13984         var intDay = 0;
13985         
13986         for(; i < days; i++){
13987             intDay = i - startingPos + 1;
13988             textEls[i].innerHTML = (intDay);
13989             d.setDate(d.getDate()+1);
13990             
13991             cells[i].className = ''; // "x-date-active";
13992             setCellClass(this, cells[i]);
13993         }
13994         var extraDays = 0;
13995         
13996         for(; i < 42; i++) {
13997             textEls[i].innerHTML = (++extraDays);
13998             d.setDate(d.getDate()+1);
13999             
14000             cells[i].className = "fc-future fc-other-month";
14001             setCellClass(this, cells[i]);
14002         }
14003         
14004         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14005         
14006         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14007         
14008         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14009         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14010         
14011         if(totalRows != 6){
14012             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14013             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14014         }
14015         
14016         this.fireEvent('monthchange', this, date);
14017         
14018         
14019         /*
14020         if(!this.internalRender){
14021             var main = this.el.dom.firstChild;
14022             var w = main.offsetWidth;
14023             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14024             Roo.fly(main).setWidth(w);
14025             this.internalRender = true;
14026             // opera does not respect the auto grow header center column
14027             // then, after it gets a width opera refuses to recalculate
14028             // without a second pass
14029             if(Roo.isOpera && !this.secondPass){
14030                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14031                 this.secondPass = true;
14032                 this.update.defer(10, this, [date]);
14033             }
14034         }
14035         */
14036         
14037     },
14038     
14039     findCell : function(dt) {
14040         dt = dt.clearTime().getTime();
14041         var ret = false;
14042         this.cells.each(function(c){
14043             //Roo.log("check " +c.dateValue + '?=' + dt);
14044             if(c.dateValue == dt){
14045                 ret = c;
14046                 return false;
14047             }
14048             return true;
14049         });
14050         
14051         return ret;
14052     },
14053     
14054     findCells : function(ev) {
14055         var s = ev.start.clone().clearTime().getTime();
14056        // Roo.log(s);
14057         var e= ev.end.clone().clearTime().getTime();
14058        // Roo.log(e);
14059         var ret = [];
14060         this.cells.each(function(c){
14061              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14062             
14063             if(c.dateValue > e){
14064                 return ;
14065             }
14066             if(c.dateValue < s){
14067                 return ;
14068             }
14069             ret.push(c);
14070         });
14071         
14072         return ret;    
14073     },
14074     
14075 //    findBestRow: function(cells)
14076 //    {
14077 //        var ret = 0;
14078 //        
14079 //        for (var i =0 ; i < cells.length;i++) {
14080 //            ret  = Math.max(cells[i].rows || 0,ret);
14081 //        }
14082 //        return ret;
14083 //        
14084 //    },
14085     
14086     
14087     addItem : function(ev)
14088     {
14089         // look for vertical location slot in
14090         var cells = this.findCells(ev);
14091         
14092 //        ev.row = this.findBestRow(cells);
14093         
14094         // work out the location.
14095         
14096         var crow = false;
14097         var rows = [];
14098         for(var i =0; i < cells.length; i++) {
14099             
14100             cells[i].row = cells[0].row;
14101             
14102             if(i == 0){
14103                 cells[i].row = cells[i].row + 1;
14104             }
14105             
14106             if (!crow) {
14107                 crow = {
14108                     start : cells[i],
14109                     end :  cells[i]
14110                 };
14111                 continue;
14112             }
14113             if (crow.start.getY() == cells[i].getY()) {
14114                 // on same row.
14115                 crow.end = cells[i];
14116                 continue;
14117             }
14118             // different row.
14119             rows.push(crow);
14120             crow = {
14121                 start: cells[i],
14122                 end : cells[i]
14123             };
14124             
14125         }
14126         
14127         rows.push(crow);
14128         ev.els = [];
14129         ev.rows = rows;
14130         ev.cells = cells;
14131         
14132         cells[0].events.push(ev);
14133         
14134         this.calevents.push(ev);
14135     },
14136     
14137     clearEvents: function() {
14138         
14139         if(!this.calevents){
14140             return;
14141         }
14142         
14143         Roo.each(this.cells.elements, function(c){
14144             c.row = 0;
14145             c.events = [];
14146             c.more = [];
14147         });
14148         
14149         Roo.each(this.calevents, function(e) {
14150             Roo.each(e.els, function(el) {
14151                 el.un('mouseenter' ,this.onEventEnter, this);
14152                 el.un('mouseleave' ,this.onEventLeave, this);
14153                 el.remove();
14154             },this);
14155         },this);
14156         
14157         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14158             e.remove();
14159         });
14160         
14161     },
14162     
14163     renderEvents: function()
14164     {   
14165         var _this = this;
14166         
14167         this.cells.each(function(c) {
14168             
14169             if(c.row < 5){
14170                 return;
14171             }
14172             
14173             var ev = c.events;
14174             
14175             var r = 4;
14176             if(c.row != c.events.length){
14177                 r = 4 - (4 - (c.row - c.events.length));
14178             }
14179             
14180             c.events = ev.slice(0, r);
14181             c.more = ev.slice(r);
14182             
14183             if(c.more.length && c.more.length == 1){
14184                 c.events.push(c.more.pop());
14185             }
14186             
14187             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14188             
14189         });
14190             
14191         this.cells.each(function(c) {
14192             
14193             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14194             
14195             
14196             for (var e = 0; e < c.events.length; e++){
14197                 var ev = c.events[e];
14198                 var rows = ev.rows;
14199                 
14200                 for(var i = 0; i < rows.length; i++) {
14201                 
14202                     // how many rows should it span..
14203
14204                     var  cfg = {
14205                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14206                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14207
14208                         unselectable : "on",
14209                         cn : [
14210                             {
14211                                 cls: 'fc-event-inner',
14212                                 cn : [
14213     //                                {
14214     //                                  tag:'span',
14215     //                                  cls: 'fc-event-time',
14216     //                                  html : cells.length > 1 ? '' : ev.time
14217     //                                },
14218                                     {
14219                                       tag:'span',
14220                                       cls: 'fc-event-title',
14221                                       html : String.format('{0}', ev.title)
14222                                     }
14223
14224
14225                                 ]
14226                             },
14227                             {
14228                                 cls: 'ui-resizable-handle ui-resizable-e',
14229                                 html : '&nbsp;&nbsp;&nbsp'
14230                             }
14231
14232                         ]
14233                     };
14234
14235                     if (i == 0) {
14236                         cfg.cls += ' fc-event-start';
14237                     }
14238                     if ((i+1) == rows.length) {
14239                         cfg.cls += ' fc-event-end';
14240                     }
14241
14242                     var ctr = _this.el.select('.fc-event-container',true).first();
14243                     var cg = ctr.createChild(cfg);
14244
14245                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14246                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14247
14248                     var r = (c.more.length) ? 1 : 0;
14249                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14250                     cg.setWidth(ebox.right - sbox.x -2);
14251
14252                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14253                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14254                     cg.on('click', _this.onEventClick, _this, ev);
14255
14256                     ev.els.push(cg);
14257                     
14258                 }
14259                 
14260             }
14261             
14262             
14263             if(c.more.length){
14264                 var  cfg = {
14265                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14266                     style : 'position: absolute',
14267                     unselectable : "on",
14268                     cn : [
14269                         {
14270                             cls: 'fc-event-inner',
14271                             cn : [
14272                                 {
14273                                   tag:'span',
14274                                   cls: 'fc-event-title',
14275                                   html : 'More'
14276                                 }
14277
14278
14279                             ]
14280                         },
14281                         {
14282                             cls: 'ui-resizable-handle ui-resizable-e',
14283                             html : '&nbsp;&nbsp;&nbsp'
14284                         }
14285
14286                     ]
14287                 };
14288
14289                 var ctr = _this.el.select('.fc-event-container',true).first();
14290                 var cg = ctr.createChild(cfg);
14291
14292                 var sbox = c.select('.fc-day-content',true).first().getBox();
14293                 var ebox = c.select('.fc-day-content',true).first().getBox();
14294                 //Roo.log(cg);
14295                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14296                 cg.setWidth(ebox.right - sbox.x -2);
14297
14298                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14299                 
14300             }
14301             
14302         });
14303         
14304         
14305         
14306     },
14307     
14308     onEventEnter: function (e, el,event,d) {
14309         this.fireEvent('evententer', this, el, event);
14310     },
14311     
14312     onEventLeave: function (e, el,event,d) {
14313         this.fireEvent('eventleave', this, el, event);
14314     },
14315     
14316     onEventClick: function (e, el,event,d) {
14317         this.fireEvent('eventclick', this, el, event);
14318     },
14319     
14320     onMonthChange: function () {
14321         this.store.load();
14322     },
14323     
14324     onMoreEventClick: function(e, el, more)
14325     {
14326         var _this = this;
14327         
14328         this.calpopover.placement = 'right';
14329         this.calpopover.setTitle('More');
14330         
14331         this.calpopover.setContent('');
14332         
14333         var ctr = this.calpopover.el.select('.popover-content', true).first();
14334         
14335         Roo.each(more, function(m){
14336             var cfg = {
14337                 cls : 'fc-event-hori fc-event-draggable',
14338                 html : m.title
14339             }
14340             var cg = ctr.createChild(cfg);
14341             
14342             cg.on('click', _this.onEventClick, _this, m);
14343         });
14344         
14345         this.calpopover.show(el);
14346         
14347         
14348     },
14349     
14350     onLoad: function () 
14351     {   
14352         this.calevents = [];
14353         var cal = this;
14354         
14355         if(this.store.getCount() > 0){
14356             this.store.data.each(function(d){
14357                cal.addItem({
14358                     id : d.data.id,
14359                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14360                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14361                     time : d.data.start_time,
14362                     title : d.data.title,
14363                     description : d.data.description,
14364                     venue : d.data.venue
14365                 });
14366             });
14367         }
14368         
14369         this.renderEvents();
14370         
14371         if(this.calevents.length && this.loadMask){
14372             this.maskEl.hide();
14373         }
14374     },
14375     
14376     onBeforeLoad: function()
14377     {
14378         this.clearEvents();
14379         if(this.loadMask){
14380             this.maskEl.show();
14381         }
14382     }
14383 });
14384
14385  
14386  /*
14387  * - LGPL
14388  *
14389  * element
14390  * 
14391  */
14392
14393 /**
14394  * @class Roo.bootstrap.Popover
14395  * @extends Roo.bootstrap.Component
14396  * Bootstrap Popover class
14397  * @cfg {String} html contents of the popover   (or false to use children..)
14398  * @cfg {String} title of popover (or false to hide)
14399  * @cfg {String} placement how it is placed
14400  * @cfg {String} trigger click || hover (or false to trigger manually)
14401  * @cfg {String} over what (parent or false to trigger manually.)
14402  * @cfg {Number} delay - delay before showing
14403  
14404  * @constructor
14405  * Create a new Popover
14406  * @param {Object} config The config object
14407  */
14408
14409 Roo.bootstrap.Popover = function(config){
14410     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14411 };
14412
14413 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14414     
14415     title: 'Fill in a title',
14416     html: false,
14417     
14418     placement : 'right',
14419     trigger : 'hover', // hover
14420     
14421     delay : 0,
14422     
14423     over: 'parent',
14424     
14425     can_build_overlaid : false,
14426     
14427     getChildContainer : function()
14428     {
14429         return this.el.select('.popover-content',true).first();
14430     },
14431     
14432     getAutoCreate : function(){
14433          Roo.log('make popover?');
14434         var cfg = {
14435            cls : 'popover roo-dynamic',
14436            style: 'display:block',
14437            cn : [
14438                 {
14439                     cls : 'arrow'
14440                 },
14441                 {
14442                     cls : 'popover-inner',
14443                     cn : [
14444                         {
14445                             tag: 'h3',
14446                             cls: 'popover-title',
14447                             html : this.title
14448                         },
14449                         {
14450                             cls : 'popover-content',
14451                             html : this.html
14452                         }
14453                     ]
14454                     
14455                 }
14456            ]
14457         };
14458         
14459         return cfg;
14460     },
14461     setTitle: function(str)
14462     {
14463         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14464     },
14465     setContent: function(str)
14466     {
14467         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14468     },
14469     // as it get's added to the bottom of the page.
14470     onRender : function(ct, position)
14471     {
14472         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14473         if(!this.el){
14474             var cfg = Roo.apply({},  this.getAutoCreate());
14475             cfg.id = Roo.id();
14476             
14477             if (this.cls) {
14478                 cfg.cls += ' ' + this.cls;
14479             }
14480             if (this.style) {
14481                 cfg.style = this.style;
14482             }
14483             Roo.log("adding to ")
14484             this.el = Roo.get(document.body).createChild(cfg, position);
14485             Roo.log(this.el);
14486         }
14487         this.initEvents();
14488     },
14489     
14490     initEvents : function()
14491     {
14492         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14493         this.el.enableDisplayMode('block');
14494         this.el.hide();
14495         if (this.over === false) {
14496             return; 
14497         }
14498         if (this.triggers === false) {
14499             return;
14500         }
14501         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14502         var triggers = this.trigger ? this.trigger.split(' ') : [];
14503         Roo.each(triggers, function(trigger) {
14504         
14505             if (trigger == 'click') {
14506                 on_el.on('click', this.toggle, this);
14507             } else if (trigger != 'manual') {
14508                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14509                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14510       
14511                 on_el.on(eventIn  ,this.enter, this);
14512                 on_el.on(eventOut, this.leave, this);
14513             }
14514         }, this);
14515         
14516     },
14517     
14518     
14519     // private
14520     timeout : null,
14521     hoverState : null,
14522     
14523     toggle : function () {
14524         this.hoverState == 'in' ? this.leave() : this.enter();
14525     },
14526     
14527     enter : function () {
14528        
14529     
14530         clearTimeout(this.timeout);
14531     
14532         this.hoverState = 'in';
14533     
14534         if (!this.delay || !this.delay.show) {
14535             this.show();
14536             return;
14537         }
14538         var _t = this;
14539         this.timeout = setTimeout(function () {
14540             if (_t.hoverState == 'in') {
14541                 _t.show();
14542             }
14543         }, this.delay.show)
14544     },
14545     leave : function() {
14546         clearTimeout(this.timeout);
14547     
14548         this.hoverState = 'out';
14549     
14550         if (!this.delay || !this.delay.hide) {
14551             this.hide();
14552             return;
14553         }
14554         var _t = this;
14555         this.timeout = setTimeout(function () {
14556             if (_t.hoverState == 'out') {
14557                 _t.hide();
14558             }
14559         }, this.delay.hide)
14560     },
14561     
14562     show : function (on_el)
14563     {
14564         if (!on_el) {
14565             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14566         }
14567         // set content.
14568         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14569         if (this.html !== false) {
14570             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14571         }
14572         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14573         if (!this.title.length) {
14574             this.el.select('.popover-title',true).hide();
14575         }
14576         
14577         var placement = typeof this.placement == 'function' ?
14578             this.placement.call(this, this.el, on_el) :
14579             this.placement;
14580             
14581         var autoToken = /\s?auto?\s?/i;
14582         var autoPlace = autoToken.test(placement);
14583         if (autoPlace) {
14584             placement = placement.replace(autoToken, '') || 'top';
14585         }
14586         
14587         //this.el.detach()
14588         //this.el.setXY([0,0]);
14589         this.el.show();
14590         this.el.dom.style.display='block';
14591         this.el.addClass(placement);
14592         
14593         //this.el.appendTo(on_el);
14594         
14595         var p = this.getPosition();
14596         var box = this.el.getBox();
14597         
14598         if (autoPlace) {
14599             // fixme..
14600         }
14601         var align = Roo.bootstrap.Popover.alignment[placement];
14602         this.el.alignTo(on_el, align[0],align[1]);
14603         //var arrow = this.el.select('.arrow',true).first();
14604         //arrow.set(align[2], 
14605         
14606         this.el.addClass('in');
14607         this.hoverState = null;
14608         
14609         if (this.el.hasClass('fade')) {
14610             // fade it?
14611         }
14612         
14613     },
14614     hide : function()
14615     {
14616         this.el.setXY([0,0]);
14617         this.el.removeClass('in');
14618         this.el.hide();
14619         
14620     }
14621     
14622 });
14623
14624 Roo.bootstrap.Popover.alignment = {
14625     'left' : ['r-l', [-10,0], 'right'],
14626     'right' : ['l-r', [10,0], 'left'],
14627     'bottom' : ['t-b', [0,10], 'top'],
14628     'top' : [ 'b-t', [0,-10], 'bottom']
14629 };
14630
14631  /*
14632  * - LGPL
14633  *
14634  * Progress
14635  * 
14636  */
14637
14638 /**
14639  * @class Roo.bootstrap.Progress
14640  * @extends Roo.bootstrap.Component
14641  * Bootstrap Progress class
14642  * @cfg {Boolean} striped striped of the progress bar
14643  * @cfg {Boolean} active animated of the progress bar
14644  * 
14645  * 
14646  * @constructor
14647  * Create a new Progress
14648  * @param {Object} config The config object
14649  */
14650
14651 Roo.bootstrap.Progress = function(config){
14652     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14653 };
14654
14655 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14656     
14657     striped : false,
14658     active: false,
14659     
14660     getAutoCreate : function(){
14661         var cfg = {
14662             tag: 'div',
14663             cls: 'progress'
14664         };
14665         
14666         
14667         if(this.striped){
14668             cfg.cls += ' progress-striped';
14669         }
14670       
14671         if(this.active){
14672             cfg.cls += ' active';
14673         }
14674         
14675         
14676         return cfg;
14677     }
14678    
14679 });
14680
14681  
14682
14683  /*
14684  * - LGPL
14685  *
14686  * ProgressBar
14687  * 
14688  */
14689
14690 /**
14691  * @class Roo.bootstrap.ProgressBar
14692  * @extends Roo.bootstrap.Component
14693  * Bootstrap ProgressBar class
14694  * @cfg {Number} aria_valuenow aria-value now
14695  * @cfg {Number} aria_valuemin aria-value min
14696  * @cfg {Number} aria_valuemax aria-value max
14697  * @cfg {String} label label for the progress bar
14698  * @cfg {String} panel (success | info | warning | danger )
14699  * @cfg {String} role role of the progress bar
14700  * @cfg {String} sr_only text
14701  * 
14702  * 
14703  * @constructor
14704  * Create a new ProgressBar
14705  * @param {Object} config The config object
14706  */
14707
14708 Roo.bootstrap.ProgressBar = function(config){
14709     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14710 };
14711
14712 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14713     
14714     aria_valuenow : 0,
14715     aria_valuemin : 0,
14716     aria_valuemax : 100,
14717     label : false,
14718     panel : false,
14719     role : false,
14720     sr_only: false,
14721     
14722     getAutoCreate : function()
14723     {
14724         
14725         var cfg = {
14726             tag: 'div',
14727             cls: 'progress-bar',
14728             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14729         };
14730         
14731         if(this.sr_only){
14732             cfg.cn = {
14733                 tag: 'span',
14734                 cls: 'sr-only',
14735                 html: this.sr_only
14736             }
14737         }
14738         
14739         if(this.role){
14740             cfg.role = this.role;
14741         }
14742         
14743         if(this.aria_valuenow){
14744             cfg['aria-valuenow'] = this.aria_valuenow;
14745         }
14746         
14747         if(this.aria_valuemin){
14748             cfg['aria-valuemin'] = this.aria_valuemin;
14749         }
14750         
14751         if(this.aria_valuemax){
14752             cfg['aria-valuemax'] = this.aria_valuemax;
14753         }
14754         
14755         if(this.label && !this.sr_only){
14756             cfg.html = this.label;
14757         }
14758         
14759         if(this.panel){
14760             cfg.cls += ' progress-bar-' + this.panel;
14761         }
14762         
14763         return cfg;
14764     },
14765     
14766     update : function(aria_valuenow)
14767     {
14768         this.aria_valuenow = aria_valuenow;
14769         
14770         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14771     }
14772    
14773 });
14774
14775  
14776
14777  /*
14778  * - LGPL
14779  *
14780  * column
14781  * 
14782  */
14783
14784 /**
14785  * @class Roo.bootstrap.TabGroup
14786  * @extends Roo.bootstrap.Column
14787  * Bootstrap Column class
14788  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14789  * @cfg {Boolean} carousel true to make the group behave like a carousel
14790  * @cfg {Number} bullets show the panel pointer.. default 0
14791  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14792  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14793  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14794  * 
14795  * @constructor
14796  * Create a new TabGroup
14797  * @param {Object} config The config object
14798  */
14799
14800 Roo.bootstrap.TabGroup = function(config){
14801     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14802     if (!this.navId) {
14803         this.navId = Roo.id();
14804     }
14805     this.tabs = [];
14806     Roo.bootstrap.TabGroup.register(this);
14807     
14808 };
14809
14810 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14811     
14812     carousel : false,
14813     transition : false,
14814     bullets : 0,
14815     timer : 0,
14816     autoslide : false,
14817     slideFn : false,
14818     slideOnTouch : false,
14819     
14820     getAutoCreate : function()
14821     {
14822         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14823         
14824         cfg.cls += ' tab-content';
14825         
14826         Roo.log('get auto create...............');
14827         
14828         if (this.carousel) {
14829             cfg.cls += ' carousel slide';
14830             
14831             cfg.cn = [{
14832                cls : 'carousel-inner'
14833             }];
14834         
14835             if(this.bullets > 0 && !Roo.isTouch){
14836                 
14837                 var bullets = {
14838                     cls : 'carousel-bullets',
14839                     cn : []
14840                 };
14841                 
14842                 if(this.bullets_cls){
14843                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14844                 }
14845                 
14846                 for (var i = 0; i < this.bullets; i++){
14847                     bullets.cn.push({
14848                         cls : 'bullet bullet-' + i
14849                     });
14850                 }
14851                 
14852                 bullets.cn.push({
14853                     cls : 'clear'
14854                 });
14855                 
14856                 cfg.cn[0].cn = bullets;
14857             }
14858         }
14859         
14860         return cfg;
14861     },
14862     
14863     initEvents:  function()
14864     {
14865         Roo.log('-------- init events on tab group ---------');
14866         
14867         if(this.bullets > 0 && !Roo.isTouch){
14868             this.initBullet();
14869         }
14870         
14871         Roo.log(this);
14872         
14873         if(Roo.isTouch && this.slideOnTouch){
14874             this.el.on("touchstart", this.onTouchStart, this);
14875         }
14876         
14877         if(this.autoslide){
14878             var _this = this;
14879             
14880             this.slideFn = window.setInterval(function() {
14881                 _this.showPanelNext();
14882             }, this.timer);
14883         }
14884         
14885     },
14886     
14887     onTouchStart : function(e, el, o)
14888     {
14889         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14890             return;
14891         }
14892         
14893         this.showPanelNext();
14894     },
14895     
14896     getChildContainer : function()
14897     {
14898         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14899     },
14900     
14901     /**
14902     * register a Navigation item
14903     * @param {Roo.bootstrap.NavItem} the navitem to add
14904     */
14905     register : function(item)
14906     {
14907         this.tabs.push( item);
14908         item.navId = this.navId; // not really needed..
14909     
14910     },
14911     
14912     getActivePanel : function()
14913     {
14914         var r = false;
14915         Roo.each(this.tabs, function(t) {
14916             if (t.active) {
14917                 r = t;
14918                 return false;
14919             }
14920             return null;
14921         });
14922         return r;
14923         
14924     },
14925     getPanelByName : function(n)
14926     {
14927         var r = false;
14928         Roo.each(this.tabs, function(t) {
14929             if (t.tabId == n) {
14930                 r = t;
14931                 return false;
14932             }
14933             return null;
14934         });
14935         return r;
14936     },
14937     indexOfPanel : function(p)
14938     {
14939         var r = false;
14940         Roo.each(this.tabs, function(t,i) {
14941             if (t.tabId == p.tabId) {
14942                 r = i;
14943                 return false;
14944             }
14945             return null;
14946         });
14947         return r;
14948     },
14949     /**
14950      * show a specific panel
14951      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14952      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14953      */
14954     showPanel : function (pan)
14955     {
14956         if(this.transition){
14957             Roo.log("waiting for the transitionend");
14958             return;
14959         }
14960         
14961         if (typeof(pan) == 'number') {
14962             pan = this.tabs[pan];
14963         }
14964         if (typeof(pan) == 'string') {
14965             pan = this.getPanelByName(pan);
14966         }
14967         if (pan.tabId == this.getActivePanel().tabId) {
14968             return true;
14969         }
14970         var cur = this.getActivePanel();
14971         
14972         if (false === cur.fireEvent('beforedeactivate')) {
14973             return false;
14974         }
14975         
14976         if(this.bullets > 0 && !Roo.isTouch){
14977             this.setActiveBullet(this.indexOfPanel(pan));
14978         }
14979         
14980         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14981             
14982             this.transition = true;
14983             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14984             var lr = dir == 'next' ? 'left' : 'right';
14985             pan.el.addClass(dir); // or prev
14986             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14987             cur.el.addClass(lr); // or right
14988             pan.el.addClass(lr);
14989             
14990             var _this = this;
14991             cur.el.on('transitionend', function() {
14992                 Roo.log("trans end?");
14993                 
14994                 pan.el.removeClass([lr,dir]);
14995                 pan.setActive(true);
14996                 
14997                 cur.el.removeClass([lr]);
14998                 cur.setActive(false);
14999                 
15000                 _this.transition = false;
15001                 
15002             }, this, { single:  true } );
15003             
15004             return true;
15005         }
15006         
15007         cur.setActive(false);
15008         pan.setActive(true);
15009         
15010         return true;
15011         
15012     },
15013     showPanelNext : function()
15014     {
15015         var i = this.indexOfPanel(this.getActivePanel());
15016         
15017         if (i >= this.tabs.length - 1 && !this.autoslide) {
15018             return;
15019         }
15020         
15021         if (i >= this.tabs.length - 1 && this.autoslide) {
15022             i = -1;
15023         }
15024         
15025         this.showPanel(this.tabs[i+1]);
15026     },
15027     
15028     showPanelPrev : function()
15029     {
15030         var i = this.indexOfPanel(this.getActivePanel());
15031         
15032         if (i  < 1 && !this.autoslide) {
15033             return;
15034         }
15035         
15036         if (i < 1 && this.autoslide) {
15037             i = this.tabs.length;
15038         }
15039         
15040         this.showPanel(this.tabs[i-1]);
15041     },
15042     
15043     initBullet : function()
15044     {
15045         if(Roo.isTouch){
15046             return;
15047         }
15048         
15049         var _this = this;
15050         
15051         for (var i = 0; i < this.bullets; i++){
15052             var bullet = this.el.select('.bullet-' + i, true).first();
15053
15054             if(!bullet){
15055                 continue;
15056             }
15057
15058             bullet.on('click', (function(e, el, o, ii, t){
15059
15060                 e.preventDefault();
15061
15062                 _this.showPanel(ii);
15063
15064                 if(_this.autoslide && _this.slideFn){
15065                     clearInterval(_this.slideFn);
15066                     _this.slideFn = window.setInterval(function() {
15067                         _this.showPanelNext();
15068                     }, _this.timer);
15069                 }
15070
15071             }).createDelegate(this, [i, bullet], true));
15072         }
15073     },
15074     
15075     setActiveBullet : function(i)
15076     {
15077         if(Roo.isTouch){
15078             return;
15079         }
15080         
15081         Roo.each(this.el.select('.bullet', true).elements, function(el){
15082             el.removeClass('selected');
15083         });
15084
15085         var bullet = this.el.select('.bullet-' + i, true).first();
15086         
15087         if(!bullet){
15088             return;
15089         }
15090         
15091         bullet.addClass('selected');
15092     }
15093     
15094     
15095   
15096 });
15097
15098  
15099
15100  
15101  
15102 Roo.apply(Roo.bootstrap.TabGroup, {
15103     
15104     groups: {},
15105      /**
15106     * register a Navigation Group
15107     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15108     */
15109     register : function(navgrp)
15110     {
15111         this.groups[navgrp.navId] = navgrp;
15112         
15113     },
15114     /**
15115     * fetch a Navigation Group based on the navigation ID
15116     * if one does not exist , it will get created.
15117     * @param {string} the navgroup to add
15118     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15119     */
15120     get: function(navId) {
15121         if (typeof(this.groups[navId]) == 'undefined') {
15122             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15123         }
15124         return this.groups[navId] ;
15125     }
15126     
15127     
15128     
15129 });
15130
15131  /*
15132  * - LGPL
15133  *
15134  * TabPanel
15135  * 
15136  */
15137
15138 /**
15139  * @class Roo.bootstrap.TabPanel
15140  * @extends Roo.bootstrap.Component
15141  * Bootstrap TabPanel class
15142  * @cfg {Boolean} active panel active
15143  * @cfg {String} html panel content
15144  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15145  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15146  * 
15147  * 
15148  * @constructor
15149  * Create a new TabPanel
15150  * @param {Object} config The config object
15151  */
15152
15153 Roo.bootstrap.TabPanel = function(config){
15154     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15155     this.addEvents({
15156         /**
15157              * @event changed
15158              * Fires when the active status changes
15159              * @param {Roo.bootstrap.TabPanel} this
15160              * @param {Boolean} state the new state
15161             
15162          */
15163         'changed': true,
15164         /**
15165              * @event beforedeactivate
15166              * Fires before a tab is de-activated - can be used to do validation on a form.
15167              * @param {Roo.bootstrap.TabPanel} this
15168              * @return {Boolean} false if there is an error
15169             
15170          */
15171         'beforedeactivate': true
15172      });
15173     
15174     this.tabId = this.tabId || Roo.id();
15175   
15176 };
15177
15178 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15179     
15180     active: false,
15181     html: false,
15182     tabId: false,
15183     navId : false,
15184     
15185     getAutoCreate : function(){
15186         var cfg = {
15187             tag: 'div',
15188             // item is needed for carousel - not sure if it has any effect otherwise
15189             cls: 'tab-pane item',
15190             html: this.html || ''
15191         };
15192         
15193         if(this.active){
15194             cfg.cls += ' active';
15195         }
15196         
15197         if(this.tabId){
15198             cfg.tabId = this.tabId;
15199         }
15200         
15201         
15202         return cfg;
15203     },
15204     
15205     initEvents:  function()
15206     {
15207         Roo.log('-------- init events on tab panel ---------');
15208         
15209         var p = this.parent();
15210         this.navId = this.navId || p.navId;
15211         
15212         if (typeof(this.navId) != 'undefined') {
15213             // not really needed.. but just in case.. parent should be a NavGroup.
15214             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15215             Roo.log(['register', tg, this]);
15216             tg.register(this);
15217             
15218             var i = tg.tabs.length - 1;
15219             
15220             if(this.active && tg.bullets > 0 && i < tg.bullets){
15221                 tg.setActiveBullet(i);
15222             }
15223         }
15224         
15225     },
15226     
15227     
15228     onRender : function(ct, position)
15229     {
15230        // Roo.log("Call onRender: " + this.xtype);
15231         
15232         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15233         
15234         
15235         
15236         
15237         
15238     },
15239     
15240     setActive: function(state)
15241     {
15242         Roo.log("panel - set active " + this.tabId + "=" + state);
15243         
15244         this.active = state;
15245         if (!state) {
15246             this.el.removeClass('active');
15247             
15248         } else  if (!this.el.hasClass('active')) {
15249             this.el.addClass('active');
15250         }
15251         
15252         this.fireEvent('changed', this, state);
15253     }
15254     
15255     
15256 });
15257  
15258
15259  
15260
15261  /*
15262  * - LGPL
15263  *
15264  * DateField
15265  * 
15266  */
15267
15268 /**
15269  * @class Roo.bootstrap.DateField
15270  * @extends Roo.bootstrap.Input
15271  * Bootstrap DateField class
15272  * @cfg {Number} weekStart default 0
15273  * @cfg {String} viewMode default empty, (months|years)
15274  * @cfg {String} minViewMode default empty, (months|years)
15275  * @cfg {Number} startDate default -Infinity
15276  * @cfg {Number} endDate default Infinity
15277  * @cfg {Boolean} todayHighlight default false
15278  * @cfg {Boolean} todayBtn default false
15279  * @cfg {Boolean} calendarWeeks default false
15280  * @cfg {Object} daysOfWeekDisabled default empty
15281  * @cfg {Boolean} singleMode default false (true | false)
15282  * 
15283  * @cfg {Boolean} keyboardNavigation default true
15284  * @cfg {String} language default en
15285  * 
15286  * @constructor
15287  * Create a new DateField
15288  * @param {Object} config The config object
15289  */
15290
15291 Roo.bootstrap.DateField = function(config){
15292     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15293      this.addEvents({
15294             /**
15295              * @event show
15296              * Fires when this field show.
15297              * @param {Roo.bootstrap.DateField} this
15298              * @param {Mixed} date The date value
15299              */
15300             show : true,
15301             /**
15302              * @event show
15303              * Fires when this field hide.
15304              * @param {Roo.bootstrap.DateField} this
15305              * @param {Mixed} date The date value
15306              */
15307             hide : true,
15308             /**
15309              * @event select
15310              * Fires when select a date.
15311              * @param {Roo.bootstrap.DateField} this
15312              * @param {Mixed} date The date value
15313              */
15314             select : true
15315         });
15316 };
15317
15318 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15319     
15320     /**
15321      * @cfg {String} format
15322      * The default date format string which can be overriden for localization support.  The format must be
15323      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15324      */
15325     format : "m/d/y",
15326     /**
15327      * @cfg {String} altFormats
15328      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15329      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15330      */
15331     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15332     
15333     weekStart : 0,
15334     
15335     viewMode : '',
15336     
15337     minViewMode : '',
15338     
15339     todayHighlight : false,
15340     
15341     todayBtn: false,
15342     
15343     language: 'en',
15344     
15345     keyboardNavigation: true,
15346     
15347     calendarWeeks: false,
15348     
15349     startDate: -Infinity,
15350     
15351     endDate: Infinity,
15352     
15353     daysOfWeekDisabled: [],
15354     
15355     _events: [],
15356     
15357     singleMode : false,
15358     
15359     UTCDate: function()
15360     {
15361         return new Date(Date.UTC.apply(Date, arguments));
15362     },
15363     
15364     UTCToday: function()
15365     {
15366         var today = new Date();
15367         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15368     },
15369     
15370     getDate: function() {
15371             var d = this.getUTCDate();
15372             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15373     },
15374     
15375     getUTCDate: function() {
15376             return this.date;
15377     },
15378     
15379     setDate: function(d) {
15380             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15381     },
15382     
15383     setUTCDate: function(d) {
15384             this.date = d;
15385             this.setValue(this.formatDate(this.date));
15386     },
15387         
15388     onRender: function(ct, position)
15389     {
15390         
15391         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15392         
15393         this.language = this.language || 'en';
15394         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15395         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15396         
15397         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15398         this.format = this.format || 'm/d/y';
15399         this.isInline = false;
15400         this.isInput = true;
15401         this.component = this.el.select('.add-on', true).first() || false;
15402         this.component = (this.component && this.component.length === 0) ? false : this.component;
15403         this.hasInput = this.component && this.inputEL().length;
15404         
15405         if (typeof(this.minViewMode === 'string')) {
15406             switch (this.minViewMode) {
15407                 case 'months':
15408                     this.minViewMode = 1;
15409                     break;
15410                 case 'years':
15411                     this.minViewMode = 2;
15412                     break;
15413                 default:
15414                     this.minViewMode = 0;
15415                     break;
15416             }
15417         }
15418         
15419         if (typeof(this.viewMode === 'string')) {
15420             switch (this.viewMode) {
15421                 case 'months':
15422                     this.viewMode = 1;
15423                     break;
15424                 case 'years':
15425                     this.viewMode = 2;
15426                     break;
15427                 default:
15428                     this.viewMode = 0;
15429                     break;
15430             }
15431         }
15432                 
15433         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15434         
15435 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15436         
15437         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15438         
15439         this.picker().on('mousedown', this.onMousedown, this);
15440         this.picker().on('click', this.onClick, this);
15441         
15442         this.picker().addClass('datepicker-dropdown');
15443         
15444         this.startViewMode = this.viewMode;
15445         
15446         if(this.singleMode){
15447             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15448                 v.setVisibilityMode(Roo.Element.DISPLAY)
15449                 v.hide();
15450             });
15451             
15452             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15453                 v.setStyle('width', '189px');
15454             });
15455         }
15456         
15457         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15458             if(!this.calendarWeeks){
15459                 v.remove();
15460                 return;
15461             }
15462             
15463             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15464             v.attr('colspan', function(i, val){
15465                 return parseInt(val) + 1;
15466             });
15467         })
15468                         
15469         
15470         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15471         
15472         this.setStartDate(this.startDate);
15473         this.setEndDate(this.endDate);
15474         
15475         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15476         
15477         this.fillDow();
15478         this.fillMonths();
15479         this.update();
15480         this.showMode();
15481         
15482         if(this.isInline) {
15483             this.show();
15484         }
15485     },
15486     
15487     picker : function()
15488     {
15489         return this.pickerEl;
15490 //        return this.el.select('.datepicker', true).first();
15491     },
15492     
15493     fillDow: function()
15494     {
15495         var dowCnt = this.weekStart;
15496         
15497         var dow = {
15498             tag: 'tr',
15499             cn: [
15500                 
15501             ]
15502         };
15503         
15504         if(this.calendarWeeks){
15505             dow.cn.push({
15506                 tag: 'th',
15507                 cls: 'cw',
15508                 html: '&nbsp;'
15509             })
15510         }
15511         
15512         while (dowCnt < this.weekStart + 7) {
15513             dow.cn.push({
15514                 tag: 'th',
15515                 cls: 'dow',
15516                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15517             });
15518         }
15519         
15520         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15521     },
15522     
15523     fillMonths: function()
15524     {    
15525         var i = 0;
15526         var months = this.picker().select('>.datepicker-months td', true).first();
15527         
15528         months.dom.innerHTML = '';
15529         
15530         while (i < 12) {
15531             var month = {
15532                 tag: 'span',
15533                 cls: 'month',
15534                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15535             }
15536             
15537             months.createChild(month);
15538         }
15539         
15540     },
15541     
15542     update: function()
15543     {
15544         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;
15545         
15546         if (this.date < this.startDate) {
15547             this.viewDate = new Date(this.startDate);
15548         } else if (this.date > this.endDate) {
15549             this.viewDate = new Date(this.endDate);
15550         } else {
15551             this.viewDate = new Date(this.date);
15552         }
15553         
15554         this.fill();
15555     },
15556     
15557     fill: function() 
15558     {
15559         var d = new Date(this.viewDate),
15560                 year = d.getUTCFullYear(),
15561                 month = d.getUTCMonth(),
15562                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15563                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15564                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15565                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15566                 currentDate = this.date && this.date.valueOf(),
15567                 today = this.UTCToday();
15568         
15569         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15570         
15571 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15572         
15573 //        this.picker.select('>tfoot th.today').
15574 //                                              .text(dates[this.language].today)
15575 //                                              .toggle(this.todayBtn !== false);
15576     
15577         this.updateNavArrows();
15578         this.fillMonths();
15579                                                 
15580         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15581         
15582         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15583          
15584         prevMonth.setUTCDate(day);
15585         
15586         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15587         
15588         var nextMonth = new Date(prevMonth);
15589         
15590         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15591         
15592         nextMonth = nextMonth.valueOf();
15593         
15594         var fillMonths = false;
15595         
15596         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15597         
15598         while(prevMonth.valueOf() < nextMonth) {
15599             var clsName = '';
15600             
15601             if (prevMonth.getUTCDay() === this.weekStart) {
15602                 if(fillMonths){
15603                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15604                 }
15605                     
15606                 fillMonths = {
15607                     tag: 'tr',
15608                     cn: []
15609                 };
15610                 
15611                 if(this.calendarWeeks){
15612                     // ISO 8601: First week contains first thursday.
15613                     // ISO also states week starts on Monday, but we can be more abstract here.
15614                     var
15615                     // Start of current week: based on weekstart/current date
15616                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15617                     // Thursday of this week
15618                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15619                     // First Thursday of year, year from thursday
15620                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15621                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15622                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15623                     
15624                     fillMonths.cn.push({
15625                         tag: 'td',
15626                         cls: 'cw',
15627                         html: calWeek
15628                     });
15629                 }
15630             }
15631             
15632             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15633                 clsName += ' old';
15634             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15635                 clsName += ' new';
15636             }
15637             if (this.todayHighlight &&
15638                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15639                 prevMonth.getUTCMonth() == today.getMonth() &&
15640                 prevMonth.getUTCDate() == today.getDate()) {
15641                 clsName += ' today';
15642             }
15643             
15644             if (currentDate && prevMonth.valueOf() === currentDate) {
15645                 clsName += ' active';
15646             }
15647             
15648             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15649                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15650                     clsName += ' disabled';
15651             }
15652             
15653             fillMonths.cn.push({
15654                 tag: 'td',
15655                 cls: 'day ' + clsName,
15656                 html: prevMonth.getDate()
15657             })
15658             
15659             prevMonth.setDate(prevMonth.getDate()+1);
15660         }
15661           
15662         var currentYear = this.date && this.date.getUTCFullYear();
15663         var currentMonth = this.date && this.date.getUTCMonth();
15664         
15665         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15666         
15667         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15668             v.removeClass('active');
15669             
15670             if(currentYear === year && k === currentMonth){
15671                 v.addClass('active');
15672             }
15673             
15674             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15675                 v.addClass('disabled');
15676             }
15677             
15678         });
15679         
15680         
15681         year = parseInt(year/10, 10) * 10;
15682         
15683         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15684         
15685         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15686         
15687         year -= 1;
15688         for (var i = -1; i < 11; i++) {
15689             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15690                 tag: 'span',
15691                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15692                 html: year
15693             })
15694             
15695             year += 1;
15696         }
15697     },
15698     
15699     showMode: function(dir) 
15700     {
15701         if (dir) {
15702             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15703         }
15704         
15705         Roo.each(this.picker().select('>div',true).elements, function(v){
15706             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15707             v.hide();
15708         });
15709         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15710     },
15711     
15712     place: function()
15713     {
15714         if(this.isInline) return;
15715         
15716         this.picker().removeClass(['bottom', 'top']);
15717         
15718         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15719             /*
15720              * place to the top of element!
15721              *
15722              */
15723             
15724             this.picker().addClass('top');
15725             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15726             
15727             return;
15728         }
15729         
15730         this.picker().addClass('bottom');
15731         
15732         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15733     },
15734     
15735     parseDate : function(value)
15736     {
15737         if(!value || value instanceof Date){
15738             return value;
15739         }
15740         var v = Date.parseDate(value, this.format);
15741         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15742             v = Date.parseDate(value, 'Y-m-d');
15743         }
15744         if(!v && this.altFormats){
15745             if(!this.altFormatsArray){
15746                 this.altFormatsArray = this.altFormats.split("|");
15747             }
15748             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15749                 v = Date.parseDate(value, this.altFormatsArray[i]);
15750             }
15751         }
15752         return v;
15753     },
15754     
15755     formatDate : function(date, fmt)
15756     {   
15757         return (!date || !(date instanceof Date)) ?
15758         date : date.dateFormat(fmt || this.format);
15759     },
15760     
15761     onFocus : function()
15762     {
15763         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15764         this.show();
15765     },
15766     
15767     onBlur : function()
15768     {
15769         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15770         
15771         var d = this.inputEl().getValue();
15772         
15773         this.setValue(d);
15774                 
15775         this.hide();
15776     },
15777     
15778     show : function()
15779     {
15780         this.picker().show();
15781         this.update();
15782         this.place();
15783         
15784         this.fireEvent('show', this, this.date);
15785     },
15786     
15787     hide : function()
15788     {
15789         if(this.isInline) return;
15790         this.picker().hide();
15791         this.viewMode = this.startViewMode;
15792         this.showMode();
15793         
15794         this.fireEvent('hide', this, this.date);
15795         
15796     },
15797     
15798     onMousedown: function(e)
15799     {
15800         e.stopPropagation();
15801         e.preventDefault();
15802     },
15803     
15804     keyup: function(e)
15805     {
15806         Roo.bootstrap.DateField.superclass.keyup.call(this);
15807         this.update();
15808     },
15809
15810     setValue: function(v)
15811     {
15812         
15813         // v can be a string or a date..
15814         
15815         
15816         var d = new Date(this.parseDate(v) ).clearTime();
15817         
15818         if(isNaN(d.getTime())){
15819             this.date = this.viewDate = '';
15820             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15821             return;
15822         }
15823         
15824         v = this.formatDate(d);
15825         
15826         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15827         
15828         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15829      
15830         this.update();
15831
15832         this.fireEvent('select', this, this.date);
15833         
15834     },
15835     
15836     getValue: function()
15837     {
15838         return this.formatDate(this.date);
15839     },
15840     
15841     fireKey: function(e)
15842     {
15843         if (!this.picker().isVisible()){
15844             if (e.keyCode == 27) // allow escape to hide and re-show picker
15845                 this.show();
15846             return;
15847         }
15848         
15849         var dateChanged = false,
15850         dir, day, month,
15851         newDate, newViewDate;
15852         
15853         switch(e.keyCode){
15854             case 27: // escape
15855                 this.hide();
15856                 e.preventDefault();
15857                 break;
15858             case 37: // left
15859             case 39: // right
15860                 if (!this.keyboardNavigation) break;
15861                 dir = e.keyCode == 37 ? -1 : 1;
15862                 
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);
15872                     newViewDate = new Date(this.viewDate);
15873                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
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 38: // up
15885             case 40: // down
15886                 if (!this.keyboardNavigation) break;
15887                 dir = e.keyCode == 38 ? -1 : 1;
15888                 if (e.ctrlKey){
15889                     newDate = this.moveYear(this.date, dir);
15890                     newViewDate = this.moveYear(this.viewDate, dir);
15891                 } else if (e.shiftKey){
15892                     newDate = this.moveMonth(this.date, dir);
15893                     newViewDate = this.moveMonth(this.viewDate, dir);
15894                 } else {
15895                     newDate = new Date(this.date);
15896                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15897                     newViewDate = new Date(this.viewDate);
15898                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15899                 }
15900                 if (this.dateWithinRange(newDate)){
15901                     this.date = newDate;
15902                     this.viewDate = newViewDate;
15903                     this.setValue(this.formatDate(this.date));
15904 //                    this.update();
15905                     e.preventDefault();
15906                     dateChanged = true;
15907                 }
15908                 break;
15909             case 13: // enter
15910                 this.setValue(this.formatDate(this.date));
15911                 this.hide();
15912                 e.preventDefault();
15913                 break;
15914             case 9: // tab
15915                 this.setValue(this.formatDate(this.date));
15916                 this.hide();
15917                 break;
15918             case 16: // shift
15919             case 17: // ctrl
15920             case 18: // alt
15921                 break;
15922             default :
15923                 this.hide();
15924                 
15925         }
15926     },
15927     
15928     
15929     onClick: function(e) 
15930     {
15931         e.stopPropagation();
15932         e.preventDefault();
15933         
15934         var target = e.getTarget();
15935         
15936         if(target.nodeName.toLowerCase() === 'i'){
15937             target = Roo.get(target).dom.parentNode;
15938         }
15939         
15940         var nodeName = target.nodeName;
15941         var className = target.className;
15942         var html = target.innerHTML;
15943         //Roo.log(nodeName);
15944         
15945         switch(nodeName.toLowerCase()) {
15946             case 'th':
15947                 switch(className) {
15948                     case 'switch':
15949                         this.showMode(1);
15950                         break;
15951                     case 'prev':
15952                     case 'next':
15953                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15954                         switch(this.viewMode){
15955                                 case 0:
15956                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15957                                         break;
15958                                 case 1:
15959                                 case 2:
15960                                         this.viewDate = this.moveYear(this.viewDate, dir);
15961                                         break;
15962                         }
15963                         this.fill();
15964                         break;
15965                     case 'today':
15966                         var date = new Date();
15967                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15968 //                        this.fill()
15969                         this.setValue(this.formatDate(this.date));
15970                         
15971                         this.hide();
15972                         break;
15973                 }
15974                 break;
15975             case 'span':
15976                 if (className.indexOf('disabled') < 0) {
15977                     this.viewDate.setUTCDate(1);
15978                     if (className.indexOf('month') > -1) {
15979                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15980                     } else {
15981                         var year = parseInt(html, 10) || 0;
15982                         this.viewDate.setUTCFullYear(year);
15983                         
15984                     }
15985                     
15986                     if(this.singleMode){
15987                         this.setValue(this.formatDate(this.viewDate));
15988                         this.hide();
15989                         return;
15990                     }
15991                     
15992                     this.showMode(-1);
15993                     this.fill();
15994                 }
15995                 break;
15996                 
15997             case 'td':
15998                 //Roo.log(className);
15999                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16000                     var day = parseInt(html, 10) || 1;
16001                     var year = this.viewDate.getUTCFullYear(),
16002                         month = this.viewDate.getUTCMonth();
16003
16004                     if (className.indexOf('old') > -1) {
16005                         if(month === 0 ){
16006                             month = 11;
16007                             year -= 1;
16008                         }else{
16009                             month -= 1;
16010                         }
16011                     } else if (className.indexOf('new') > -1) {
16012                         if (month == 11) {
16013                             month = 0;
16014                             year += 1;
16015                         } else {
16016                             month += 1;
16017                         }
16018                     }
16019                     //Roo.log([year,month,day]);
16020                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16021                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16022 //                    this.fill();
16023                     //Roo.log(this.formatDate(this.date));
16024                     this.setValue(this.formatDate(this.date));
16025                     this.hide();
16026                 }
16027                 break;
16028         }
16029     },
16030     
16031     setStartDate: function(startDate)
16032     {
16033         this.startDate = startDate || -Infinity;
16034         if (this.startDate !== -Infinity) {
16035             this.startDate = this.parseDate(this.startDate);
16036         }
16037         this.update();
16038         this.updateNavArrows();
16039     },
16040
16041     setEndDate: function(endDate)
16042     {
16043         this.endDate = endDate || Infinity;
16044         if (this.endDate !== Infinity) {
16045             this.endDate = this.parseDate(this.endDate);
16046         }
16047         this.update();
16048         this.updateNavArrows();
16049     },
16050     
16051     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16052     {
16053         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16054         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16055             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16056         }
16057         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16058             return parseInt(d, 10);
16059         });
16060         this.update();
16061         this.updateNavArrows();
16062     },
16063     
16064     updateNavArrows: function() 
16065     {
16066         if(this.singleMode){
16067             return;
16068         }
16069         
16070         var d = new Date(this.viewDate),
16071         year = d.getUTCFullYear(),
16072         month = d.getUTCMonth();
16073         
16074         Roo.each(this.picker().select('.prev', true).elements, function(v){
16075             v.show();
16076             switch (this.viewMode) {
16077                 case 0:
16078
16079                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16080                         v.hide();
16081                     }
16082                     break;
16083                 case 1:
16084                 case 2:
16085                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16086                         v.hide();
16087                     }
16088                     break;
16089             }
16090         });
16091         
16092         Roo.each(this.picker().select('.next', true).elements, function(v){
16093             v.show();
16094             switch (this.viewMode) {
16095                 case 0:
16096
16097                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16098                         v.hide();
16099                     }
16100                     break;
16101                 case 1:
16102                 case 2:
16103                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16104                         v.hide();
16105                     }
16106                     break;
16107             }
16108         })
16109     },
16110     
16111     moveMonth: function(date, dir)
16112     {
16113         if (!dir) return date;
16114         var new_date = new Date(date.valueOf()),
16115         day = new_date.getUTCDate(),
16116         month = new_date.getUTCMonth(),
16117         mag = Math.abs(dir),
16118         new_month, test;
16119         dir = dir > 0 ? 1 : -1;
16120         if (mag == 1){
16121             test = dir == -1
16122             // If going back one month, make sure month is not current month
16123             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16124             ? function(){
16125                 return new_date.getUTCMonth() == month;
16126             }
16127             // If going forward one month, make sure month is as expected
16128             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16129             : function(){
16130                 return new_date.getUTCMonth() != new_month;
16131             };
16132             new_month = month + dir;
16133             new_date.setUTCMonth(new_month);
16134             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16135             if (new_month < 0 || new_month > 11)
16136                 new_month = (new_month + 12) % 12;
16137         } else {
16138             // For magnitudes >1, move one month at a time...
16139             for (var i=0; i<mag; i++)
16140                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16141                 new_date = this.moveMonth(new_date, dir);
16142             // ...then reset the day, keeping it in the new month
16143             new_month = new_date.getUTCMonth();
16144             new_date.setUTCDate(day);
16145             test = function(){
16146                 return new_month != new_date.getUTCMonth();
16147             };
16148         }
16149         // Common date-resetting loop -- if date is beyond end of month, make it
16150         // end of month
16151         while (test()){
16152             new_date.setUTCDate(--day);
16153             new_date.setUTCMonth(new_month);
16154         }
16155         return new_date;
16156     },
16157
16158     moveYear: function(date, dir)
16159     {
16160         return this.moveMonth(date, dir*12);
16161     },
16162
16163     dateWithinRange: function(date)
16164     {
16165         return date >= this.startDate && date <= this.endDate;
16166     },
16167
16168     
16169     remove: function() 
16170     {
16171         this.picker().remove();
16172     }
16173    
16174 });
16175
16176 Roo.apply(Roo.bootstrap.DateField,  {
16177     
16178     head : {
16179         tag: 'thead',
16180         cn: [
16181         {
16182             tag: 'tr',
16183             cn: [
16184             {
16185                 tag: 'th',
16186                 cls: 'prev',
16187                 html: '<i class="fa fa-arrow-left"/>'
16188             },
16189             {
16190                 tag: 'th',
16191                 cls: 'switch',
16192                 colspan: '5'
16193             },
16194             {
16195                 tag: 'th',
16196                 cls: 'next',
16197                 html: '<i class="fa fa-arrow-right"/>'
16198             }
16199
16200             ]
16201         }
16202         ]
16203     },
16204     
16205     content : {
16206         tag: 'tbody',
16207         cn: [
16208         {
16209             tag: 'tr',
16210             cn: [
16211             {
16212                 tag: 'td',
16213                 colspan: '7'
16214             }
16215             ]
16216         }
16217         ]
16218     },
16219     
16220     footer : {
16221         tag: 'tfoot',
16222         cn: [
16223         {
16224             tag: 'tr',
16225             cn: [
16226             {
16227                 tag: 'th',
16228                 colspan: '7',
16229                 cls: 'today'
16230             }
16231                     
16232             ]
16233         }
16234         ]
16235     },
16236     
16237     dates:{
16238         en: {
16239             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16240             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16241             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16242             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16243             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16244             today: "Today"
16245         }
16246     },
16247     
16248     modes: [
16249     {
16250         clsName: 'days',
16251         navFnc: 'Month',
16252         navStep: 1
16253     },
16254     {
16255         clsName: 'months',
16256         navFnc: 'FullYear',
16257         navStep: 1
16258     },
16259     {
16260         clsName: 'years',
16261         navFnc: 'FullYear',
16262         navStep: 10
16263     }]
16264 });
16265
16266 Roo.apply(Roo.bootstrap.DateField,  {
16267   
16268     template : {
16269         tag: 'div',
16270         cls: 'datepicker dropdown-menu roo-dynamic',
16271         cn: [
16272         {
16273             tag: 'div',
16274             cls: 'datepicker-days',
16275             cn: [
16276             {
16277                 tag: 'table',
16278                 cls: 'table-condensed',
16279                 cn:[
16280                 Roo.bootstrap.DateField.head,
16281                 {
16282                     tag: 'tbody'
16283                 },
16284                 Roo.bootstrap.DateField.footer
16285                 ]
16286             }
16287             ]
16288         },
16289         {
16290             tag: 'div',
16291             cls: 'datepicker-months',
16292             cn: [
16293             {
16294                 tag: 'table',
16295                 cls: 'table-condensed',
16296                 cn:[
16297                 Roo.bootstrap.DateField.head,
16298                 Roo.bootstrap.DateField.content,
16299                 Roo.bootstrap.DateField.footer
16300                 ]
16301             }
16302             ]
16303         },
16304         {
16305             tag: 'div',
16306             cls: 'datepicker-years',
16307             cn: [
16308             {
16309                 tag: 'table',
16310                 cls: 'table-condensed',
16311                 cn:[
16312                 Roo.bootstrap.DateField.head,
16313                 Roo.bootstrap.DateField.content,
16314                 Roo.bootstrap.DateField.footer
16315                 ]
16316             }
16317             ]
16318         }
16319         ]
16320     }
16321 });
16322
16323  
16324
16325  /*
16326  * - LGPL
16327  *
16328  * TimeField
16329  * 
16330  */
16331
16332 /**
16333  * @class Roo.bootstrap.TimeField
16334  * @extends Roo.bootstrap.Input
16335  * Bootstrap DateField class
16336  * 
16337  * 
16338  * @constructor
16339  * Create a new TimeField
16340  * @param {Object} config The config object
16341  */
16342
16343 Roo.bootstrap.TimeField = function(config){
16344     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16345     this.addEvents({
16346             /**
16347              * @event show
16348              * Fires when this field show.
16349              * @param {Roo.bootstrap.DateField} thisthis
16350              * @param {Mixed} date The date value
16351              */
16352             show : true,
16353             /**
16354              * @event show
16355              * Fires when this field hide.
16356              * @param {Roo.bootstrap.DateField} this
16357              * @param {Mixed} date The date value
16358              */
16359             hide : true,
16360             /**
16361              * @event select
16362              * Fires when select a date.
16363              * @param {Roo.bootstrap.DateField} this
16364              * @param {Mixed} date The date value
16365              */
16366             select : true
16367         });
16368 };
16369
16370 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16371     
16372     /**
16373      * @cfg {String} format
16374      * The default time format string which can be overriden for localization support.  The format must be
16375      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16376      */
16377     format : "H:i",
16378        
16379     onRender: function(ct, position)
16380     {
16381         
16382         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16383                 
16384         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16385         
16386         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16387         
16388         this.pop = this.picker().select('>.datepicker-time',true).first();
16389         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16390         
16391         this.picker().on('mousedown', this.onMousedown, this);
16392         this.picker().on('click', this.onClick, this);
16393         
16394         this.picker().addClass('datepicker-dropdown');
16395     
16396         this.fillTime();
16397         this.update();
16398             
16399         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16400         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16401         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16402         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16403         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16404         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16405
16406     },
16407     
16408     fireKey: function(e){
16409         if (!this.picker().isVisible()){
16410             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16411                 this.show();
16412             }
16413             return;
16414         }
16415
16416         e.preventDefault();
16417         
16418         switch(e.keyCode){
16419             case 27: // escape
16420                 this.hide();
16421                 break;
16422             case 37: // left
16423             case 39: // right
16424                 this.onTogglePeriod();
16425                 break;
16426             case 38: // up
16427                 this.onIncrementMinutes();
16428                 break;
16429             case 40: // down
16430                 this.onDecrementMinutes();
16431                 break;
16432             case 13: // enter
16433             case 9: // tab
16434                 this.setTime();
16435                 break;
16436         }
16437     },
16438     
16439     onClick: function(e) {
16440         e.stopPropagation();
16441         e.preventDefault();
16442     },
16443     
16444     picker : function()
16445     {
16446         return this.el.select('.datepicker', true).first();
16447     },
16448     
16449     fillTime: function()
16450     {    
16451         var time = this.pop.select('tbody', true).first();
16452         
16453         time.dom.innerHTML = '';
16454         
16455         time.createChild({
16456             tag: 'tr',
16457             cn: [
16458                 {
16459                     tag: 'td',
16460                     cn: [
16461                         {
16462                             tag: 'a',
16463                             href: '#',
16464                             cls: 'btn',
16465                             cn: [
16466                                 {
16467                                     tag: 'span',
16468                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16469                                 }
16470                             ]
16471                         } 
16472                     ]
16473                 },
16474                 {
16475                     tag: 'td',
16476                     cls: 'separator'
16477                 },
16478                 {
16479                     tag: 'td',
16480                     cn: [
16481                         {
16482                             tag: 'a',
16483                             href: '#',
16484                             cls: 'btn',
16485                             cn: [
16486                                 {
16487                                     tag: 'span',
16488                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16489                                 }
16490                             ]
16491                         }
16492                     ]
16493                 },
16494                 {
16495                     tag: 'td',
16496                     cls: 'separator'
16497                 }
16498             ]
16499         });
16500         
16501         time.createChild({
16502             tag: 'tr',
16503             cn: [
16504                 {
16505                     tag: 'td',
16506                     cn: [
16507                         {
16508                             tag: 'span',
16509                             cls: 'timepicker-hour',
16510                             html: '00'
16511                         }  
16512                     ]
16513                 },
16514                 {
16515                     tag: 'td',
16516                     cls: 'separator',
16517                     html: ':'
16518                 },
16519                 {
16520                     tag: 'td',
16521                     cn: [
16522                         {
16523                             tag: 'span',
16524                             cls: 'timepicker-minute',
16525                             html: '00'
16526                         }  
16527                     ]
16528                 },
16529                 {
16530                     tag: 'td',
16531                     cls: 'separator'
16532                 },
16533                 {
16534                     tag: 'td',
16535                     cn: [
16536                         {
16537                             tag: 'button',
16538                             type: 'button',
16539                             cls: 'btn btn-primary period',
16540                             html: 'AM'
16541                             
16542                         }
16543                     ]
16544                 }
16545             ]
16546         });
16547         
16548         time.createChild({
16549             tag: 'tr',
16550             cn: [
16551                 {
16552                     tag: 'td',
16553                     cn: [
16554                         {
16555                             tag: 'a',
16556                             href: '#',
16557                             cls: 'btn',
16558                             cn: [
16559                                 {
16560                                     tag: 'span',
16561                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16562                                 }
16563                             ]
16564                         }
16565                     ]
16566                 },
16567                 {
16568                     tag: 'td',
16569                     cls: 'separator'
16570                 },
16571                 {
16572                     tag: 'td',
16573                     cn: [
16574                         {
16575                             tag: 'a',
16576                             href: '#',
16577                             cls: 'btn',
16578                             cn: [
16579                                 {
16580                                     tag: 'span',
16581                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16582                                 }
16583                             ]
16584                         }
16585                     ]
16586                 },
16587                 {
16588                     tag: 'td',
16589                     cls: 'separator'
16590                 }
16591             ]
16592         });
16593         
16594     },
16595     
16596     update: function()
16597     {
16598         
16599         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16600         
16601         this.fill();
16602     },
16603     
16604     fill: function() 
16605     {
16606         var hours = this.time.getHours();
16607         var minutes = this.time.getMinutes();
16608         var period = 'AM';
16609         
16610         if(hours > 11){
16611             period = 'PM';
16612         }
16613         
16614         if(hours == 0){
16615             hours = 12;
16616         }
16617         
16618         
16619         if(hours > 12){
16620             hours = hours - 12;
16621         }
16622         
16623         if(hours < 10){
16624             hours = '0' + hours;
16625         }
16626         
16627         if(minutes < 10){
16628             minutes = '0' + minutes;
16629         }
16630         
16631         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16632         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16633         this.pop.select('button', true).first().dom.innerHTML = period;
16634         
16635     },
16636     
16637     place: function()
16638     {   
16639         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16640         
16641         var cls = ['bottom'];
16642         
16643         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16644             cls.pop();
16645             cls.push('top');
16646         }
16647         
16648         cls.push('right');
16649         
16650         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16651             cls.pop();
16652             cls.push('left');
16653         }
16654         
16655         this.picker().addClass(cls.join('-'));
16656         
16657         var _this = this;
16658         
16659         Roo.each(cls, function(c){
16660             if(c == 'bottom'){
16661                 _this.picker().setTop(_this.inputEl().getHeight());
16662                 return;
16663             }
16664             if(c == 'top'){
16665                 _this.picker().setTop(0 - _this.picker().getHeight());
16666                 return;
16667             }
16668             
16669             if(c == 'left'){
16670                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16671                 return;
16672             }
16673             if(c == 'right'){
16674                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16675                 return;
16676             }
16677         });
16678         
16679     },
16680   
16681     onFocus : function()
16682     {
16683         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16684         this.show();
16685     },
16686     
16687     onBlur : function()
16688     {
16689         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16690         this.hide();
16691     },
16692     
16693     show : function()
16694     {
16695         this.picker().show();
16696         this.pop.show();
16697         this.update();
16698         this.place();
16699         
16700         this.fireEvent('show', this, this.date);
16701     },
16702     
16703     hide : function()
16704     {
16705         this.picker().hide();
16706         this.pop.hide();
16707         
16708         this.fireEvent('hide', this, this.date);
16709     },
16710     
16711     setTime : function()
16712     {
16713         this.hide();
16714         this.setValue(this.time.format(this.format));
16715         
16716         this.fireEvent('select', this, this.date);
16717         
16718         
16719     },
16720     
16721     onMousedown: function(e){
16722         e.stopPropagation();
16723         e.preventDefault();
16724     },
16725     
16726     onIncrementHours: function()
16727     {
16728         Roo.log('onIncrementHours');
16729         this.time = this.time.add(Date.HOUR, 1);
16730         this.update();
16731         
16732     },
16733     
16734     onDecrementHours: function()
16735     {
16736         Roo.log('onDecrementHours');
16737         this.time = this.time.add(Date.HOUR, -1);
16738         this.update();
16739     },
16740     
16741     onIncrementMinutes: function()
16742     {
16743         Roo.log('onIncrementMinutes');
16744         this.time = this.time.add(Date.MINUTE, 1);
16745         this.update();
16746     },
16747     
16748     onDecrementMinutes: function()
16749     {
16750         Roo.log('onDecrementMinutes');
16751         this.time = this.time.add(Date.MINUTE, -1);
16752         this.update();
16753     },
16754     
16755     onTogglePeriod: function()
16756     {
16757         Roo.log('onTogglePeriod');
16758         this.time = this.time.add(Date.HOUR, 12);
16759         this.update();
16760     }
16761     
16762    
16763 });
16764
16765 Roo.apply(Roo.bootstrap.TimeField,  {
16766     
16767     content : {
16768         tag: 'tbody',
16769         cn: [
16770             {
16771                 tag: 'tr',
16772                 cn: [
16773                 {
16774                     tag: 'td',
16775                     colspan: '7'
16776                 }
16777                 ]
16778             }
16779         ]
16780     },
16781     
16782     footer : {
16783         tag: 'tfoot',
16784         cn: [
16785             {
16786                 tag: 'tr',
16787                 cn: [
16788                 {
16789                     tag: 'th',
16790                     colspan: '7',
16791                     cls: '',
16792                     cn: [
16793                         {
16794                             tag: 'button',
16795                             cls: 'btn btn-info ok',
16796                             html: 'OK'
16797                         }
16798                     ]
16799                 }
16800
16801                 ]
16802             }
16803         ]
16804     }
16805 });
16806
16807 Roo.apply(Roo.bootstrap.TimeField,  {
16808   
16809     template : {
16810         tag: 'div',
16811         cls: 'datepicker dropdown-menu',
16812         cn: [
16813             {
16814                 tag: 'div',
16815                 cls: 'datepicker-time',
16816                 cn: [
16817                 {
16818                     tag: 'table',
16819                     cls: 'table-condensed',
16820                     cn:[
16821                     Roo.bootstrap.TimeField.content,
16822                     Roo.bootstrap.TimeField.footer
16823                     ]
16824                 }
16825                 ]
16826             }
16827         ]
16828     }
16829 });
16830
16831  
16832
16833  /*
16834  * - LGPL
16835  *
16836  * MonthField
16837  * 
16838  */
16839
16840 /**
16841  * @class Roo.bootstrap.MonthField
16842  * @extends Roo.bootstrap.Input
16843  * Bootstrap MonthField class
16844  * 
16845  * @cfg {String} language default en
16846  * 
16847  * @constructor
16848  * Create a new MonthField
16849  * @param {Object} config The config object
16850  */
16851
16852 Roo.bootstrap.MonthField = function(config){
16853     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16854     
16855     this.addEvents({
16856         /**
16857          * @event show
16858          * Fires when this field show.
16859          * @param {Roo.bootstrap.MonthField} this
16860          * @param {Mixed} date The date value
16861          */
16862         show : true,
16863         /**
16864          * @event show
16865          * Fires when this field hide.
16866          * @param {Roo.bootstrap.MonthField} this
16867          * @param {Mixed} date The date value
16868          */
16869         hide : true,
16870         /**
16871          * @event select
16872          * Fires when select a date.
16873          * @param {Roo.bootstrap.MonthField} this
16874          * @param {String} oldvalue The old value
16875          * @param {String} newvalue The new value
16876          */
16877         select : true
16878     });
16879 };
16880
16881 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16882     
16883     onRender: function(ct, position)
16884     {
16885         
16886         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16887         
16888         this.language = this.language || 'en';
16889         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16890         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16891         
16892         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16893         this.isInline = false;
16894         this.isInput = true;
16895         this.component = this.el.select('.add-on', true).first() || false;
16896         this.component = (this.component && this.component.length === 0) ? false : this.component;
16897         this.hasInput = this.component && this.inputEL().length;
16898         
16899         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16900         
16901         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16902         
16903         this.picker().on('mousedown', this.onMousedown, this);
16904         this.picker().on('click', this.onClick, this);
16905         
16906         this.picker().addClass('datepicker-dropdown');
16907         
16908         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16909             v.setStyle('width', '189px');
16910         });
16911         
16912         this.fillMonths();
16913         
16914         this.update();
16915         
16916         if(this.isInline) {
16917             this.show();
16918         }
16919         
16920     },
16921     
16922     setValue: function(v, suppressEvent)
16923     {   
16924         var o = this.getValue();
16925         
16926         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16927         
16928         this.update();
16929
16930         if(suppressEvent !== true){
16931             this.fireEvent('select', this, o, v);
16932         }
16933         
16934     },
16935     
16936     getValue: function()
16937     {
16938         return this.value;
16939     },
16940     
16941     onClick: function(e) 
16942     {
16943         e.stopPropagation();
16944         e.preventDefault();
16945         
16946         var target = e.getTarget();
16947         
16948         if(target.nodeName.toLowerCase() === 'i'){
16949             target = Roo.get(target).dom.parentNode;
16950         }
16951         
16952         var nodeName = target.nodeName;
16953         var className = target.className;
16954         var html = target.innerHTML;
16955         
16956         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16957             return;
16958         }
16959         
16960         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16961         
16962         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16963         
16964         this.hide();
16965                         
16966     },
16967     
16968     picker : function()
16969     {
16970         return this.pickerEl;
16971     },
16972     
16973     fillMonths: function()
16974     {    
16975         var i = 0;
16976         var months = this.picker().select('>.datepicker-months td', true).first();
16977         
16978         months.dom.innerHTML = '';
16979         
16980         while (i < 12) {
16981             var month = {
16982                 tag: 'span',
16983                 cls: 'month',
16984                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16985             }
16986             
16987             months.createChild(month);
16988         }
16989         
16990     },
16991     
16992     update: function()
16993     {
16994         var _this = this;
16995         
16996         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16997             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16998         }
16999         
17000         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17001             e.removeClass('active');
17002             
17003             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17004                 e.addClass('active');
17005             }
17006         })
17007     },
17008     
17009     place: function()
17010     {
17011         if(this.isInline) return;
17012         
17013         this.picker().removeClass(['bottom', 'top']);
17014         
17015         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17016             /*
17017              * place to the top of element!
17018              *
17019              */
17020             
17021             this.picker().addClass('top');
17022             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17023             
17024             return;
17025         }
17026         
17027         this.picker().addClass('bottom');
17028         
17029         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17030     },
17031     
17032     onFocus : function()
17033     {
17034         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17035         this.show();
17036     },
17037     
17038     onBlur : function()
17039     {
17040         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17041         
17042         var d = this.inputEl().getValue();
17043         
17044         this.setValue(d);
17045                 
17046         this.hide();
17047     },
17048     
17049     show : function()
17050     {
17051         this.picker().show();
17052         this.picker().select('>.datepicker-months', true).first().show();
17053         this.update();
17054         this.place();
17055         
17056         this.fireEvent('show', this, this.date);
17057     },
17058     
17059     hide : function()
17060     {
17061         if(this.isInline) return;
17062         this.picker().hide();
17063         this.fireEvent('hide', this, this.date);
17064         
17065     },
17066     
17067     onMousedown: function(e)
17068     {
17069         e.stopPropagation();
17070         e.preventDefault();
17071     },
17072     
17073     keyup: function(e)
17074     {
17075         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17076         this.update();
17077     },
17078
17079     fireKey: function(e)
17080     {
17081         if (!this.picker().isVisible()){
17082             if (e.keyCode == 27) // allow escape to hide and re-show picker
17083                 this.show();
17084             return;
17085         }
17086         
17087         var dir;
17088         
17089         switch(e.keyCode){
17090             case 27: // escape
17091                 this.hide();
17092                 e.preventDefault();
17093                 break;
17094             case 37: // left
17095             case 39: // right
17096                 dir = e.keyCode == 37 ? -1 : 1;
17097                 
17098                 this.vIndex = this.vIndex + dir;
17099                 
17100                 if(this.vIndex < 0){
17101                     this.vIndex = 0;
17102                 }
17103                 
17104                 if(this.vIndex > 11){
17105                     this.vIndex = 11;
17106                 }
17107                 
17108                 if(isNaN(this.vIndex)){
17109                     this.vIndex = 0;
17110                 }
17111                 
17112                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17113                 
17114                 break;
17115             case 38: // up
17116             case 40: // down
17117                 
17118                 dir = e.keyCode == 38 ? -1 : 1;
17119                 
17120                 this.vIndex = this.vIndex + dir * 4;
17121                 
17122                 if(this.vIndex < 0){
17123                     this.vIndex = 0;
17124                 }
17125                 
17126                 if(this.vIndex > 11){
17127                     this.vIndex = 11;
17128                 }
17129                 
17130                 if(isNaN(this.vIndex)){
17131                     this.vIndex = 0;
17132                 }
17133                 
17134                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17135                 break;
17136                 
17137             case 13: // enter
17138                 
17139                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17140                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17141                 }
17142                 
17143                 this.hide();
17144                 e.preventDefault();
17145                 break;
17146             case 9: // tab
17147                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17148                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17149                 }
17150                 this.hide();
17151                 break;
17152             case 16: // shift
17153             case 17: // ctrl
17154             case 18: // alt
17155                 break;
17156             default :
17157                 this.hide();
17158                 
17159         }
17160     },
17161     
17162     remove: function() 
17163     {
17164         this.picker().remove();
17165     }
17166    
17167 });
17168
17169 Roo.apply(Roo.bootstrap.MonthField,  {
17170     
17171     content : {
17172         tag: 'tbody',
17173         cn: [
17174         {
17175             tag: 'tr',
17176             cn: [
17177             {
17178                 tag: 'td',
17179                 colspan: '7'
17180             }
17181             ]
17182         }
17183         ]
17184     },
17185     
17186     dates:{
17187         en: {
17188             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17189             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17190         }
17191     }
17192 });
17193
17194 Roo.apply(Roo.bootstrap.MonthField,  {
17195   
17196     template : {
17197         tag: 'div',
17198         cls: 'datepicker dropdown-menu roo-dynamic',
17199         cn: [
17200             {
17201                 tag: 'div',
17202                 cls: 'datepicker-months',
17203                 cn: [
17204                 {
17205                     tag: 'table',
17206                     cls: 'table-condensed',
17207                     cn:[
17208                         Roo.bootstrap.DateField.content
17209                     ]
17210                 }
17211                 ]
17212             }
17213         ]
17214     }
17215 });
17216
17217  
17218
17219  
17220  /*
17221  * - LGPL
17222  *
17223  * CheckBox
17224  * 
17225  */
17226
17227 /**
17228  * @class Roo.bootstrap.CheckBox
17229  * @extends Roo.bootstrap.Input
17230  * Bootstrap CheckBox class
17231  * 
17232  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17233  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17234  * @cfg {String} boxLabel The text that appears beside the checkbox
17235  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17236  * @cfg {Boolean} checked initnal the element
17237  * @cfg {Boolean} inline inline the element (default false)
17238  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17239  * 
17240  * @constructor
17241  * Create a new CheckBox
17242  * @param {Object} config The config object
17243  */
17244
17245 Roo.bootstrap.CheckBox = function(config){
17246     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17247    
17248     this.addEvents({
17249         /**
17250         * @event check
17251         * Fires when the element is checked or unchecked.
17252         * @param {Roo.bootstrap.CheckBox} this This input
17253         * @param {Boolean} checked The new checked value
17254         */
17255        check : true
17256     });
17257     
17258 };
17259
17260 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17261   
17262     inputType: 'checkbox',
17263     inputValue: 1,
17264     valueOff: 0,
17265     boxLabel: false,
17266     checked: false,
17267     weight : false,
17268     inline: false,
17269     
17270     getAutoCreate : function()
17271     {
17272         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17273         
17274         var id = Roo.id();
17275         
17276         var cfg = {};
17277         
17278         cfg.cls = 'form-group ' + this.inputType; //input-group
17279         
17280         if(this.inline){
17281             cfg.cls += ' ' + this.inputType + '-inline';
17282         }
17283         
17284         var input =  {
17285             tag: 'input',
17286             id : id,
17287             type : this.inputType,
17288             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17289             cls : 'roo-' + this.inputType, //'form-box',
17290             placeholder : this.placeholder || ''
17291             
17292         };
17293         
17294         if (this.weight) { // Validity check?
17295             cfg.cls += " " + this.inputType + "-" + this.weight;
17296         }
17297         
17298         if (this.disabled) {
17299             input.disabled=true;
17300         }
17301         
17302         if(this.checked){
17303             input.checked = this.checked;
17304         }
17305         
17306         if (this.name) {
17307             input.name = this.name;
17308         }
17309         
17310         if (this.size) {
17311             input.cls += ' input-' + this.size;
17312         }
17313         
17314         var settings=this;
17315         
17316         ['xs','sm','md','lg'].map(function(size){
17317             if (settings[size]) {
17318                 cfg.cls += ' col-' + size + '-' + settings[size];
17319             }
17320         });
17321         
17322         var inputblock = input;
17323          
17324         if (this.before || this.after) {
17325             
17326             inputblock = {
17327                 cls : 'input-group',
17328                 cn :  [] 
17329             };
17330             
17331             if (this.before) {
17332                 inputblock.cn.push({
17333                     tag :'span',
17334                     cls : 'input-group-addon',
17335                     html : this.before
17336                 });
17337             }
17338             
17339             inputblock.cn.push(input);
17340             
17341             if (this.after) {
17342                 inputblock.cn.push({
17343                     tag :'span',
17344                     cls : 'input-group-addon',
17345                     html : this.after
17346                 });
17347             }
17348             
17349         }
17350         
17351         if (align ==='left' && this.fieldLabel.length) {
17352                 Roo.log("left and has label");
17353                 cfg.cn = [
17354                     
17355                     {
17356                         tag: 'label',
17357                         'for' :  id,
17358                         cls : 'control-label col-md-' + this.labelWidth,
17359                         html : this.fieldLabel
17360                         
17361                     },
17362                     {
17363                         cls : "col-md-" + (12 - this.labelWidth), 
17364                         cn: [
17365                             inputblock
17366                         ]
17367                     }
17368                     
17369                 ];
17370         } else if ( this.fieldLabel.length) {
17371                 Roo.log(" label");
17372                 cfg.cn = [
17373                    
17374                     {
17375                         tag: this.boxLabel ? 'span' : 'label',
17376                         'for': id,
17377                         cls: 'control-label box-input-label',
17378                         //cls : 'input-group-addon',
17379                         html : this.fieldLabel
17380                         
17381                     },
17382                     
17383                     inputblock
17384                     
17385                 ];
17386
17387         } else {
17388             
17389                 Roo.log(" no label && no align");
17390                 cfg.cn = [  inputblock ] ;
17391                 
17392                 
17393         }
17394         if(this.boxLabel){
17395              var boxLabelCfg = {
17396                 tag: 'label',
17397                 //'for': id, // box label is handled by onclick - so no for...
17398                 cls: 'box-label',
17399                 html: this.boxLabel
17400             }
17401             
17402             if(this.tooltip){
17403                 boxLabelCfg.tooltip = this.tooltip;
17404             }
17405              
17406             cfg.cn.push(boxLabelCfg);
17407         }
17408         
17409         
17410        
17411         return cfg;
17412         
17413     },
17414     
17415     /**
17416      * return the real input element.
17417      */
17418     inputEl: function ()
17419     {
17420         return this.el.select('input.roo-' + this.inputType,true).first();
17421     },
17422     
17423     labelEl: function()
17424     {
17425         return this.el.select('label.control-label',true).first();
17426     },
17427     /* depricated... */
17428     
17429     label: function()
17430     {
17431         return this.labelEl();
17432     },
17433     
17434     initEvents : function()
17435     {
17436 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17437         
17438         this.inputEl().on('click', this.onClick,  this);
17439         
17440         if (this.boxLabel) { 
17441             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17442         }
17443         
17444         this.startValue = this.getValue();
17445         
17446         if(this.groupId){
17447             Roo.bootstrap.CheckBox.register(this);
17448         }
17449     },
17450     
17451     onClick : function()
17452     {   
17453         this.setChecked(!this.checked);
17454     },
17455     
17456     setChecked : function(state,suppressEvent)
17457     {
17458         this.startValue = this.getValue();
17459         
17460         if(this.inputType == 'radio'){
17461             
17462             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17463                 e.dom.checked = false;
17464             });
17465             
17466             this.inputEl().dom.checked = true;
17467             
17468             this.inputEl().dom.value = this.inputValue;
17469             
17470             if(suppressEvent !== true){
17471                 this.fireEvent('check', this, true);
17472             }
17473             
17474             this.validate();
17475             
17476             return;
17477         }
17478         
17479         this.checked = state;
17480         
17481         this.inputEl().dom.checked = state;
17482         
17483         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17484         
17485         if(suppressEvent !== true){
17486             this.fireEvent('check', this, state);
17487         }
17488         
17489         this.validate();
17490     },
17491     
17492     getValue : function()
17493     {
17494         if(this.inputType == 'radio'){
17495             return this.getGroupValue();
17496         }
17497         
17498         return this.inputEl().getValue();
17499         
17500     },
17501     
17502     getGroupValue : function()
17503     {
17504         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17505             return '';
17506         }
17507         
17508         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17509     },
17510     
17511     setValue : function(v,suppressEvent)
17512     {
17513         if(this.inputType == 'radio'){
17514             this.setGroupValue(v, suppressEvent);
17515             return;
17516         }
17517         
17518         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17519         
17520         this.validate();
17521     },
17522     
17523     setGroupValue : function(v, suppressEvent)
17524     {
17525         this.startValue = this.getValue();
17526         
17527         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17528             e.dom.checked = false;
17529             
17530             if(e.dom.value == v){
17531                 e.dom.checked = true;
17532             }
17533         });
17534         
17535         if(suppressEvent !== true){
17536             this.fireEvent('check', this, true);
17537         }
17538
17539         this.validate();
17540         
17541         return;
17542     },
17543     
17544     validate : function()
17545     {
17546         if(
17547                 this.disabled || 
17548                 (this.inputType == 'radio' && this.validateRadio()) ||
17549                 (this.inputType == 'checkbox' && this.validateCheckbox())
17550         ){
17551             this.markValid();
17552             return true;
17553         }
17554         
17555         this.markInvalid();
17556         return false;
17557     },
17558     
17559     validateRadio : function()
17560     {
17561         var valid = false;
17562         
17563         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17564             if(!e.dom.checked){
17565                 return;
17566             }
17567             
17568             valid = true;
17569             
17570             return false;
17571         });
17572         
17573         return valid;
17574     },
17575     
17576     validateCheckbox : function()
17577     {
17578         if(!this.groupId){
17579             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17580         }
17581         
17582         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17583         
17584         if(!group){
17585             return false;
17586         }
17587         
17588         var r = false;
17589         
17590         for(var i in group){
17591             if(r){
17592                 break;
17593             }
17594             
17595             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17596         }
17597         
17598         return r;
17599     },
17600     
17601     /**
17602      * Mark this field as valid
17603      */
17604     markValid : function()
17605     {
17606         if(this.allowBlank){
17607             return;
17608         }
17609         
17610         var _this = this;
17611         
17612         this.fireEvent('valid', this);
17613         
17614         if(this.inputType == 'radio'){
17615             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17616                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17617                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17618             });
17619             
17620             return;
17621         }
17622         
17623         if(!this.groupId){
17624             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17625             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17626             return;
17627         }
17628         
17629         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17630             
17631         if(!group){
17632             return;
17633         }
17634         
17635         for(var i in group){
17636             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17637             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17638         }
17639     },
17640     
17641      /**
17642      * Mark this field as invalid
17643      * @param {String} msg The validation message
17644      */
17645     markInvalid : function(msg)
17646     {
17647         if(this.allowBlank){
17648             return;
17649         }
17650         
17651         var _this = this;
17652         
17653         this.fireEvent('invalid', this, msg);
17654         
17655         if(this.inputType == 'radio'){
17656             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17657                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17658                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17659             });
17660             
17661             return;
17662         }
17663         
17664         if(!this.groupId){
17665             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17666             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17667             return;
17668         }
17669         
17670         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17671             
17672         if(!group){
17673             return;
17674         }
17675         
17676         for(var i in group){
17677             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17678             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17679         }
17680         
17681     }
17682     
17683 });
17684
17685 Roo.apply(Roo.bootstrap.CheckBox, {
17686     
17687     groups: {},
17688     
17689      /**
17690     * register a CheckBox Group
17691     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17692     */
17693     register : function(checkbox)
17694     {
17695         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17696             this.groups[checkbox.groupId] = {};
17697         }
17698         
17699         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17700             return;
17701         }
17702         
17703         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17704         
17705     },
17706     /**
17707     * fetch a CheckBox Group based on the group ID
17708     * @param {string} the group ID
17709     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17710     */
17711     get: function(groupId) {
17712         if (typeof(this.groups[groupId]) == 'undefined') {
17713             return false;
17714         }
17715         
17716         return this.groups[groupId] ;
17717     }
17718     
17719     
17720 });
17721 /*
17722  * - LGPL
17723  *
17724  * Radio
17725  *
17726  *
17727  * not inline
17728  *<div class="radio">
17729   <label>
17730     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17731     Option one is this and that&mdash;be sure to include why it's great
17732   </label>
17733 </div>
17734  *
17735  *
17736  *inline
17737  *<span>
17738  *<label class="radio-inline">fieldLabel</label>
17739  *<label class="radio-inline">
17740   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17741 </label>
17742 <span>
17743  * 
17744  * 
17745  */
17746
17747 /**
17748  * @class Roo.bootstrap.Radio
17749  * @extends Roo.bootstrap.CheckBox
17750  * Bootstrap Radio class
17751
17752  * @constructor
17753  * Create a new Radio
17754  * @param {Object} config The config object
17755  */
17756
17757 Roo.bootstrap.Radio = function(config){
17758     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17759    
17760 };
17761
17762 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17763     
17764     inputType: 'radio',
17765     inputValue: '',
17766     valueOff: '',
17767     
17768     getAutoCreate : function()
17769     {
17770         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17771         align = align || 'left'; // default...
17772         
17773         
17774         
17775         var id = Roo.id();
17776         
17777         var cfg = {
17778                 tag : this.inline ? 'span' : 'div',
17779                 cls : '',
17780                 cn : []
17781         };
17782         
17783         var inline = this.inline ? ' radio-inline' : '';
17784         
17785         var lbl = {
17786                 tag: 'label' ,
17787                 // does not need for, as we wrap the input with it..
17788                 'for' : id,
17789                 cls : 'control-label box-label' + inline,
17790                 cn : []
17791         };
17792         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17793         
17794         var fieldLabel = {
17795             tag: 'label' ,
17796             //cls : 'control-label' + inline,
17797             html : this.fieldLabel,
17798             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17799         };
17800         
17801  
17802         
17803         
17804         var input =  {
17805             tag: 'input',
17806             id : id,
17807             type : this.inputType,
17808             //value : (!this.checked) ? this.valueOff : this.inputValue,
17809             value : this.inputValue,
17810             cls : 'roo-radio',
17811             placeholder : this.placeholder || '' // ?? needed????
17812             
17813         };
17814         if (this.weight) { // Validity check?
17815             input.cls += " radio-" + this.weight;
17816         }
17817         if (this.disabled) {
17818             input.disabled=true;
17819         }
17820         
17821         if(this.checked){
17822             input.checked = this.checked;
17823         }
17824         
17825         if (this.name) {
17826             input.name = this.name;
17827         }
17828         
17829         if (this.size) {
17830             input.cls += ' input-' + this.size;
17831         }
17832         
17833         //?? can span's inline have a width??
17834         
17835         var settings=this;
17836         ['xs','sm','md','lg'].map(function(size){
17837             if (settings[size]) {
17838                 cfg.cls += ' col-' + size + '-' + settings[size];
17839             }
17840         });
17841         
17842         var inputblock = input;
17843         
17844         if (this.before || this.after) {
17845             
17846             inputblock = {
17847                 cls : 'input-group',
17848                 tag : 'span',
17849                 cn :  [] 
17850             };
17851             if (this.before) {
17852                 inputblock.cn.push({
17853                     tag :'span',
17854                     cls : 'input-group-addon',
17855                     html : this.before
17856                 });
17857             }
17858             inputblock.cn.push(input);
17859             if (this.after) {
17860                 inputblock.cn.push({
17861                     tag :'span',
17862                     cls : 'input-group-addon',
17863                     html : this.after
17864                 });
17865             }
17866             
17867         };
17868         
17869         
17870         if (this.fieldLabel && this.fieldLabel.length) {
17871             cfg.cn.push(fieldLabel);
17872         }
17873        
17874         // normal bootstrap puts the input inside the label.
17875         // however with our styled version - it has to go after the input.
17876        
17877         //lbl.cn.push(inputblock);
17878         
17879         var lblwrap =  {
17880             tag: 'span',
17881             cls: 'radio' + inline,
17882             cn: [
17883                 inputblock,
17884                 lbl
17885             ]
17886         };
17887         
17888         cfg.cn.push( lblwrap);
17889         
17890         if(this.boxLabel){
17891             lbl.cn.push({
17892                 tag: 'span',
17893                 html: this.boxLabel
17894             })
17895         }
17896          
17897         
17898         return cfg;
17899         
17900     },
17901     
17902     initEvents : function()
17903     {
17904 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17905         
17906         this.inputEl().on('click', this.onClick,  this);
17907         if (this.boxLabel) {
17908             Roo.log('find label')
17909             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17910         }
17911         
17912     },
17913     
17914     inputEl: function ()
17915     {
17916         return this.el.select('input.roo-radio',true).first();
17917     },
17918     onClick : function()
17919     {   
17920         Roo.log("click");
17921         this.setChecked(true);
17922     },
17923     
17924     setChecked : function(state,suppressEvent)
17925     {
17926         if(state){
17927             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17928                 v.dom.checked = false;
17929             });
17930         }
17931         Roo.log(this.inputEl().dom);
17932         this.checked = state;
17933         this.inputEl().dom.checked = state;
17934         
17935         if(suppressEvent !== true){
17936             this.fireEvent('check', this, state);
17937         }
17938         
17939         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17940         
17941     },
17942     
17943     getGroupValue : function()
17944     {
17945         var value = '';
17946         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17947             if(v.dom.checked == true){
17948                 value = v.dom.value;
17949             }
17950         });
17951         
17952         return value;
17953     },
17954     
17955     /**
17956      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17957      * @return {Mixed} value The field value
17958      */
17959     getValue : function(){
17960         return this.getGroupValue();
17961     }
17962     
17963 });
17964
17965  
17966 //<script type="text/javascript">
17967
17968 /*
17969  * Based  Ext JS Library 1.1.1
17970  * Copyright(c) 2006-2007, Ext JS, LLC.
17971  * LGPL
17972  *
17973  */
17974  
17975 /**
17976  * @class Roo.HtmlEditorCore
17977  * @extends Roo.Component
17978  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17979  *
17980  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17981  */
17982
17983 Roo.HtmlEditorCore = function(config){
17984     
17985     
17986     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17987     
17988     
17989     this.addEvents({
17990         /**
17991          * @event initialize
17992          * Fires when the editor is fully initialized (including the iframe)
17993          * @param {Roo.HtmlEditorCore} this
17994          */
17995         initialize: true,
17996         /**
17997          * @event activate
17998          * Fires when the editor is first receives the focus. Any insertion must wait
17999          * until after this event.
18000          * @param {Roo.HtmlEditorCore} this
18001          */
18002         activate: true,
18003          /**
18004          * @event beforesync
18005          * Fires before the textarea is updated with content from the editor iframe. Return false
18006          * to cancel the sync.
18007          * @param {Roo.HtmlEditorCore} this
18008          * @param {String} html
18009          */
18010         beforesync: true,
18011          /**
18012          * @event beforepush
18013          * Fires before the iframe editor is updated with content from the textarea. Return false
18014          * to cancel the push.
18015          * @param {Roo.HtmlEditorCore} this
18016          * @param {String} html
18017          */
18018         beforepush: true,
18019          /**
18020          * @event sync
18021          * Fires when the textarea is updated with content from the editor iframe.
18022          * @param {Roo.HtmlEditorCore} this
18023          * @param {String} html
18024          */
18025         sync: true,
18026          /**
18027          * @event push
18028          * Fires when the iframe editor is updated with content from the textarea.
18029          * @param {Roo.HtmlEditorCore} this
18030          * @param {String} html
18031          */
18032         push: true,
18033         
18034         /**
18035          * @event editorevent
18036          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18037          * @param {Roo.HtmlEditorCore} this
18038          */
18039         editorevent: true
18040         
18041     });
18042     
18043     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18044     
18045     // defaults : white / black...
18046     this.applyBlacklists();
18047     
18048     
18049     
18050 };
18051
18052
18053 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18054
18055
18056      /**
18057      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18058      */
18059     
18060     owner : false,
18061     
18062      /**
18063      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18064      *                        Roo.resizable.
18065      */
18066     resizable : false,
18067      /**
18068      * @cfg {Number} height (in pixels)
18069      */   
18070     height: 300,
18071    /**
18072      * @cfg {Number} width (in pixels)
18073      */   
18074     width: 500,
18075     
18076     /**
18077      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18078      * 
18079      */
18080     stylesheets: false,
18081     
18082     // id of frame..
18083     frameId: false,
18084     
18085     // private properties
18086     validationEvent : false,
18087     deferHeight: true,
18088     initialized : false,
18089     activated : false,
18090     sourceEditMode : false,
18091     onFocus : Roo.emptyFn,
18092     iframePad:3,
18093     hideMode:'offsets',
18094     
18095     clearUp: true,
18096     
18097     // blacklist + whitelisted elements..
18098     black: false,
18099     white: false,
18100      
18101     
18102
18103     /**
18104      * Protected method that will not generally be called directly. It
18105      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18106      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18107      */
18108     getDocMarkup : function(){
18109         // body styles..
18110         var st = '';
18111         
18112         // inherit styels from page...?? 
18113         if (this.stylesheets === false) {
18114             
18115             Roo.get(document.head).select('style').each(function(node) {
18116                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18117             });
18118             
18119             Roo.get(document.head).select('link').each(function(node) { 
18120                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18121             });
18122             
18123         } else if (!this.stylesheets.length) {
18124                 // simple..
18125                 st = '<style type="text/css">' +
18126                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18127                    '</style>';
18128         } else { 
18129             
18130         }
18131         
18132         st +=  '<style type="text/css">' +
18133             'IMG { cursor: pointer } ' +
18134         '</style>';
18135
18136         
18137         return '<html><head>' + st  +
18138             //<style type="text/css">' +
18139             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18140             //'</style>' +
18141             ' </head><body class="roo-htmleditor-body"></body></html>';
18142     },
18143
18144     // private
18145     onRender : function(ct, position)
18146     {
18147         var _t = this;
18148         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18149         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18150         
18151         
18152         this.el.dom.style.border = '0 none';
18153         this.el.dom.setAttribute('tabIndex', -1);
18154         this.el.addClass('x-hidden hide');
18155         
18156         
18157         
18158         if(Roo.isIE){ // fix IE 1px bogus margin
18159             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18160         }
18161        
18162         
18163         this.frameId = Roo.id();
18164         
18165          
18166         
18167         var iframe = this.owner.wrap.createChild({
18168             tag: 'iframe',
18169             cls: 'form-control', // bootstrap..
18170             id: this.frameId,
18171             name: this.frameId,
18172             frameBorder : 'no',
18173             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18174         }, this.el
18175         );
18176         
18177         
18178         this.iframe = iframe.dom;
18179
18180          this.assignDocWin();
18181         
18182         this.doc.designMode = 'on';
18183        
18184         this.doc.open();
18185         this.doc.write(this.getDocMarkup());
18186         this.doc.close();
18187
18188         
18189         var task = { // must defer to wait for browser to be ready
18190             run : function(){
18191                 //console.log("run task?" + this.doc.readyState);
18192                 this.assignDocWin();
18193                 if(this.doc.body || this.doc.readyState == 'complete'){
18194                     try {
18195                         this.doc.designMode="on";
18196                     } catch (e) {
18197                         return;
18198                     }
18199                     Roo.TaskMgr.stop(task);
18200                     this.initEditor.defer(10, this);
18201                 }
18202             },
18203             interval : 10,
18204             duration: 10000,
18205             scope: this
18206         };
18207         Roo.TaskMgr.start(task);
18208
18209     },
18210
18211     // private
18212     onResize : function(w, h)
18213     {
18214          Roo.log('resize: ' +w + ',' + h );
18215         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18216         if(!this.iframe){
18217             return;
18218         }
18219         if(typeof w == 'number'){
18220             
18221             this.iframe.style.width = w + 'px';
18222         }
18223         if(typeof h == 'number'){
18224             
18225             this.iframe.style.height = h + 'px';
18226             if(this.doc){
18227                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18228             }
18229         }
18230         
18231     },
18232
18233     /**
18234      * Toggles the editor between standard and source edit mode.
18235      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18236      */
18237     toggleSourceEdit : function(sourceEditMode){
18238         
18239         this.sourceEditMode = sourceEditMode === true;
18240         
18241         if(this.sourceEditMode){
18242  
18243             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18244             
18245         }else{
18246             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18247             //this.iframe.className = '';
18248             this.deferFocus();
18249         }
18250         //this.setSize(this.owner.wrap.getSize());
18251         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18252     },
18253
18254     
18255   
18256
18257     /**
18258      * Protected method that will not generally be called directly. If you need/want
18259      * custom HTML cleanup, this is the method you should override.
18260      * @param {String} html The HTML to be cleaned
18261      * return {String} The cleaned HTML
18262      */
18263     cleanHtml : function(html){
18264         html = String(html);
18265         if(html.length > 5){
18266             if(Roo.isSafari){ // strip safari nonsense
18267                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18268             }
18269         }
18270         if(html == '&nbsp;'){
18271             html = '';
18272         }
18273         return html;
18274     },
18275
18276     /**
18277      * HTML Editor -> Textarea
18278      * Protected method that will not generally be called directly. Syncs the contents
18279      * of the editor iframe with the textarea.
18280      */
18281     syncValue : function(){
18282         if(this.initialized){
18283             var bd = (this.doc.body || this.doc.documentElement);
18284             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18285             var html = bd.innerHTML;
18286             if(Roo.isSafari){
18287                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18288                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18289                 if(m && m[1]){
18290                     html = '<div style="'+m[0]+'">' + html + '</div>';
18291                 }
18292             }
18293             html = this.cleanHtml(html);
18294             // fix up the special chars.. normaly like back quotes in word...
18295             // however we do not want to do this with chinese..
18296             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18297                 var cc = b.charCodeAt();
18298                 if (
18299                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18300                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18301                     (cc >= 0xf900 && cc < 0xfb00 )
18302                 ) {
18303                         return b;
18304                 }
18305                 return "&#"+cc+";" 
18306             });
18307             if(this.owner.fireEvent('beforesync', this, html) !== false){
18308                 this.el.dom.value = html;
18309                 this.owner.fireEvent('sync', this, html);
18310             }
18311         }
18312     },
18313
18314     /**
18315      * Protected method that will not generally be called directly. Pushes the value of the textarea
18316      * into the iframe editor.
18317      */
18318     pushValue : function(){
18319         if(this.initialized){
18320             var v = this.el.dom.value.trim();
18321             
18322 //            if(v.length < 1){
18323 //                v = '&#160;';
18324 //            }
18325             
18326             if(this.owner.fireEvent('beforepush', this, v) !== false){
18327                 var d = (this.doc.body || this.doc.documentElement);
18328                 d.innerHTML = v;
18329                 this.cleanUpPaste();
18330                 this.el.dom.value = d.innerHTML;
18331                 this.owner.fireEvent('push', this, v);
18332             }
18333         }
18334     },
18335
18336     // private
18337     deferFocus : function(){
18338         this.focus.defer(10, this);
18339     },
18340
18341     // doc'ed in Field
18342     focus : function(){
18343         if(this.win && !this.sourceEditMode){
18344             this.win.focus();
18345         }else{
18346             this.el.focus();
18347         }
18348     },
18349     
18350     assignDocWin: function()
18351     {
18352         var iframe = this.iframe;
18353         
18354          if(Roo.isIE){
18355             this.doc = iframe.contentWindow.document;
18356             this.win = iframe.contentWindow;
18357         } else {
18358 //            if (!Roo.get(this.frameId)) {
18359 //                return;
18360 //            }
18361 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18362 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18363             
18364             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18365                 return;
18366             }
18367             
18368             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18369             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18370         }
18371     },
18372     
18373     // private
18374     initEditor : function(){
18375         //console.log("INIT EDITOR");
18376         this.assignDocWin();
18377         
18378         
18379         
18380         this.doc.designMode="on";
18381         this.doc.open();
18382         this.doc.write(this.getDocMarkup());
18383         this.doc.close();
18384         
18385         var dbody = (this.doc.body || this.doc.documentElement);
18386         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18387         // this copies styles from the containing element into thsi one..
18388         // not sure why we need all of this..
18389         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18390         
18391         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18392         //ss['background-attachment'] = 'fixed'; // w3c
18393         dbody.bgProperties = 'fixed'; // ie
18394         //Roo.DomHelper.applyStyles(dbody, ss);
18395         Roo.EventManager.on(this.doc, {
18396             //'mousedown': this.onEditorEvent,
18397             'mouseup': this.onEditorEvent,
18398             'dblclick': this.onEditorEvent,
18399             'click': this.onEditorEvent,
18400             'keyup': this.onEditorEvent,
18401             buffer:100,
18402             scope: this
18403         });
18404         if(Roo.isGecko){
18405             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18406         }
18407         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18408             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18409         }
18410         this.initialized = true;
18411
18412         this.owner.fireEvent('initialize', this);
18413         this.pushValue();
18414     },
18415
18416     // private
18417     onDestroy : function(){
18418         
18419         
18420         
18421         if(this.rendered){
18422             
18423             //for (var i =0; i < this.toolbars.length;i++) {
18424             //    // fixme - ask toolbars for heights?
18425             //    this.toolbars[i].onDestroy();
18426            // }
18427             
18428             //this.wrap.dom.innerHTML = '';
18429             //this.wrap.remove();
18430         }
18431     },
18432
18433     // private
18434     onFirstFocus : function(){
18435         
18436         this.assignDocWin();
18437         
18438         
18439         this.activated = true;
18440          
18441     
18442         if(Roo.isGecko){ // prevent silly gecko errors
18443             this.win.focus();
18444             var s = this.win.getSelection();
18445             if(!s.focusNode || s.focusNode.nodeType != 3){
18446                 var r = s.getRangeAt(0);
18447                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18448                 r.collapse(true);
18449                 this.deferFocus();
18450             }
18451             try{
18452                 this.execCmd('useCSS', true);
18453                 this.execCmd('styleWithCSS', false);
18454             }catch(e){}
18455         }
18456         this.owner.fireEvent('activate', this);
18457     },
18458
18459     // private
18460     adjustFont: function(btn){
18461         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18462         //if(Roo.isSafari){ // safari
18463         //    adjust *= 2;
18464        // }
18465         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18466         if(Roo.isSafari){ // safari
18467             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18468             v =  (v < 10) ? 10 : v;
18469             v =  (v > 48) ? 48 : v;
18470             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18471             
18472         }
18473         
18474         
18475         v = Math.max(1, v+adjust);
18476         
18477         this.execCmd('FontSize', v  );
18478     },
18479
18480     onEditorEvent : function(e)
18481     {
18482         this.owner.fireEvent('editorevent', this, e);
18483       //  this.updateToolbar();
18484         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18485     },
18486
18487     insertTag : function(tg)
18488     {
18489         // could be a bit smarter... -> wrap the current selected tRoo..
18490         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18491             
18492             range = this.createRange(this.getSelection());
18493             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18494             wrappingNode.appendChild(range.extractContents());
18495             range.insertNode(wrappingNode);
18496
18497             return;
18498             
18499             
18500             
18501         }
18502         this.execCmd("formatblock",   tg);
18503         
18504     },
18505     
18506     insertText : function(txt)
18507     {
18508         
18509         
18510         var range = this.createRange();
18511         range.deleteContents();
18512                //alert(Sender.getAttribute('label'));
18513                
18514         range.insertNode(this.doc.createTextNode(txt));
18515     } ,
18516     
18517      
18518
18519     /**
18520      * Executes a Midas editor command on the editor document and performs necessary focus and
18521      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18522      * @param {String} cmd The Midas command
18523      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18524      */
18525     relayCmd : function(cmd, value){
18526         this.win.focus();
18527         this.execCmd(cmd, value);
18528         this.owner.fireEvent('editorevent', this);
18529         //this.updateToolbar();
18530         this.owner.deferFocus();
18531     },
18532
18533     /**
18534      * Executes a Midas editor command directly on the editor document.
18535      * For visual commands, you should use {@link #relayCmd} instead.
18536      * <b>This should only be called after the editor is initialized.</b>
18537      * @param {String} cmd The Midas command
18538      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18539      */
18540     execCmd : function(cmd, value){
18541         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18542         this.syncValue();
18543     },
18544  
18545  
18546    
18547     /**
18548      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18549      * to insert tRoo.
18550      * @param {String} text | dom node.. 
18551      */
18552     insertAtCursor : function(text)
18553     {
18554         
18555         
18556         
18557         if(!this.activated){
18558             return;
18559         }
18560         /*
18561         if(Roo.isIE){
18562             this.win.focus();
18563             var r = this.doc.selection.createRange();
18564             if(r){
18565                 r.collapse(true);
18566                 r.pasteHTML(text);
18567                 this.syncValue();
18568                 this.deferFocus();
18569             
18570             }
18571             return;
18572         }
18573         */
18574         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18575             this.win.focus();
18576             
18577             
18578             // from jquery ui (MIT licenced)
18579             var range, node;
18580             var win = this.win;
18581             
18582             if (win.getSelection && win.getSelection().getRangeAt) {
18583                 range = win.getSelection().getRangeAt(0);
18584                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18585                 range.insertNode(node);
18586             } else if (win.document.selection && win.document.selection.createRange) {
18587                 // no firefox support
18588                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18589                 win.document.selection.createRange().pasteHTML(txt);
18590             } else {
18591                 // no firefox support
18592                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18593                 this.execCmd('InsertHTML', txt);
18594             } 
18595             
18596             this.syncValue();
18597             
18598             this.deferFocus();
18599         }
18600     },
18601  // private
18602     mozKeyPress : function(e){
18603         if(e.ctrlKey){
18604             var c = e.getCharCode(), cmd;
18605           
18606             if(c > 0){
18607                 c = String.fromCharCode(c).toLowerCase();
18608                 switch(c){
18609                     case 'b':
18610                         cmd = 'bold';
18611                         break;
18612                     case 'i':
18613                         cmd = 'italic';
18614                         break;
18615                     
18616                     case 'u':
18617                         cmd = 'underline';
18618                         break;
18619                     
18620                     case 'v':
18621                         this.cleanUpPaste.defer(100, this);
18622                         return;
18623                         
18624                 }
18625                 if(cmd){
18626                     this.win.focus();
18627                     this.execCmd(cmd);
18628                     this.deferFocus();
18629                     e.preventDefault();
18630                 }
18631                 
18632             }
18633         }
18634     },
18635
18636     // private
18637     fixKeys : function(){ // load time branching for fastest keydown performance
18638         if(Roo.isIE){
18639             return function(e){
18640                 var k = e.getKey(), r;
18641                 if(k == e.TAB){
18642                     e.stopEvent();
18643                     r = this.doc.selection.createRange();
18644                     if(r){
18645                         r.collapse(true);
18646                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18647                         this.deferFocus();
18648                     }
18649                     return;
18650                 }
18651                 
18652                 if(k == e.ENTER){
18653                     r = this.doc.selection.createRange();
18654                     if(r){
18655                         var target = r.parentElement();
18656                         if(!target || target.tagName.toLowerCase() != 'li'){
18657                             e.stopEvent();
18658                             r.pasteHTML('<br />');
18659                             r.collapse(false);
18660                             r.select();
18661                         }
18662                     }
18663                 }
18664                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18665                     this.cleanUpPaste.defer(100, this);
18666                     return;
18667                 }
18668                 
18669                 
18670             };
18671         }else if(Roo.isOpera){
18672             return function(e){
18673                 var k = e.getKey();
18674                 if(k == e.TAB){
18675                     e.stopEvent();
18676                     this.win.focus();
18677                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18678                     this.deferFocus();
18679                 }
18680                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18681                     this.cleanUpPaste.defer(100, this);
18682                     return;
18683                 }
18684                 
18685             };
18686         }else if(Roo.isSafari){
18687             return function(e){
18688                 var k = e.getKey();
18689                 
18690                 if(k == e.TAB){
18691                     e.stopEvent();
18692                     this.execCmd('InsertText','\t');
18693                     this.deferFocus();
18694                     return;
18695                 }
18696                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18697                     this.cleanUpPaste.defer(100, this);
18698                     return;
18699                 }
18700                 
18701              };
18702         }
18703     }(),
18704     
18705     getAllAncestors: function()
18706     {
18707         var p = this.getSelectedNode();
18708         var a = [];
18709         if (!p) {
18710             a.push(p); // push blank onto stack..
18711             p = this.getParentElement();
18712         }
18713         
18714         
18715         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18716             a.push(p);
18717             p = p.parentNode;
18718         }
18719         a.push(this.doc.body);
18720         return a;
18721     },
18722     lastSel : false,
18723     lastSelNode : false,
18724     
18725     
18726     getSelection : function() 
18727     {
18728         this.assignDocWin();
18729         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18730     },
18731     
18732     getSelectedNode: function() 
18733     {
18734         // this may only work on Gecko!!!
18735         
18736         // should we cache this!!!!
18737         
18738         
18739         
18740          
18741         var range = this.createRange(this.getSelection()).cloneRange();
18742         
18743         if (Roo.isIE) {
18744             var parent = range.parentElement();
18745             while (true) {
18746                 var testRange = range.duplicate();
18747                 testRange.moveToElementText(parent);
18748                 if (testRange.inRange(range)) {
18749                     break;
18750                 }
18751                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18752                     break;
18753                 }
18754                 parent = parent.parentElement;
18755             }
18756             return parent;
18757         }
18758         
18759         // is ancestor a text element.
18760         var ac =  range.commonAncestorContainer;
18761         if (ac.nodeType == 3) {
18762             ac = ac.parentNode;
18763         }
18764         
18765         var ar = ac.childNodes;
18766          
18767         var nodes = [];
18768         var other_nodes = [];
18769         var has_other_nodes = false;
18770         for (var i=0;i<ar.length;i++) {
18771             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18772                 continue;
18773             }
18774             // fullly contained node.
18775             
18776             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18777                 nodes.push(ar[i]);
18778                 continue;
18779             }
18780             
18781             // probably selected..
18782             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18783                 other_nodes.push(ar[i]);
18784                 continue;
18785             }
18786             // outer..
18787             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18788                 continue;
18789             }
18790             
18791             
18792             has_other_nodes = true;
18793         }
18794         if (!nodes.length && other_nodes.length) {
18795             nodes= other_nodes;
18796         }
18797         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18798             return false;
18799         }
18800         
18801         return nodes[0];
18802     },
18803     createRange: function(sel)
18804     {
18805         // this has strange effects when using with 
18806         // top toolbar - not sure if it's a great idea.
18807         //this.editor.contentWindow.focus();
18808         if (typeof sel != "undefined") {
18809             try {
18810                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18811             } catch(e) {
18812                 return this.doc.createRange();
18813             }
18814         } else {
18815             return this.doc.createRange();
18816         }
18817     },
18818     getParentElement: function()
18819     {
18820         
18821         this.assignDocWin();
18822         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18823         
18824         var range = this.createRange(sel);
18825          
18826         try {
18827             var p = range.commonAncestorContainer;
18828             while (p.nodeType == 3) { // text node
18829                 p = p.parentNode;
18830             }
18831             return p;
18832         } catch (e) {
18833             return null;
18834         }
18835     
18836     },
18837     /***
18838      *
18839      * Range intersection.. the hard stuff...
18840      *  '-1' = before
18841      *  '0' = hits..
18842      *  '1' = after.
18843      *         [ -- selected range --- ]
18844      *   [fail]                        [fail]
18845      *
18846      *    basically..
18847      *      if end is before start or  hits it. fail.
18848      *      if start is after end or hits it fail.
18849      *
18850      *   if either hits (but other is outside. - then it's not 
18851      *   
18852      *    
18853      **/
18854     
18855     
18856     // @see http://www.thismuchiknow.co.uk/?p=64.
18857     rangeIntersectsNode : function(range, node)
18858     {
18859         var nodeRange = node.ownerDocument.createRange();
18860         try {
18861             nodeRange.selectNode(node);
18862         } catch (e) {
18863             nodeRange.selectNodeContents(node);
18864         }
18865     
18866         var rangeStartRange = range.cloneRange();
18867         rangeStartRange.collapse(true);
18868     
18869         var rangeEndRange = range.cloneRange();
18870         rangeEndRange.collapse(false);
18871     
18872         var nodeStartRange = nodeRange.cloneRange();
18873         nodeStartRange.collapse(true);
18874     
18875         var nodeEndRange = nodeRange.cloneRange();
18876         nodeEndRange.collapse(false);
18877     
18878         return rangeStartRange.compareBoundaryPoints(
18879                  Range.START_TO_START, nodeEndRange) == -1 &&
18880                rangeEndRange.compareBoundaryPoints(
18881                  Range.START_TO_START, nodeStartRange) == 1;
18882         
18883          
18884     },
18885     rangeCompareNode : function(range, node)
18886     {
18887         var nodeRange = node.ownerDocument.createRange();
18888         try {
18889             nodeRange.selectNode(node);
18890         } catch (e) {
18891             nodeRange.selectNodeContents(node);
18892         }
18893         
18894         
18895         range.collapse(true);
18896     
18897         nodeRange.collapse(true);
18898      
18899         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18900         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18901          
18902         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18903         
18904         var nodeIsBefore   =  ss == 1;
18905         var nodeIsAfter    = ee == -1;
18906         
18907         if (nodeIsBefore && nodeIsAfter)
18908             return 0; // outer
18909         if (!nodeIsBefore && nodeIsAfter)
18910             return 1; //right trailed.
18911         
18912         if (nodeIsBefore && !nodeIsAfter)
18913             return 2;  // left trailed.
18914         // fully contined.
18915         return 3;
18916     },
18917
18918     // private? - in a new class?
18919     cleanUpPaste :  function()
18920     {
18921         // cleans up the whole document..
18922         Roo.log('cleanuppaste');
18923         
18924         this.cleanUpChildren(this.doc.body);
18925         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18926         if (clean != this.doc.body.innerHTML) {
18927             this.doc.body.innerHTML = clean;
18928         }
18929         
18930     },
18931     
18932     cleanWordChars : function(input) {// change the chars to hex code
18933         var he = Roo.HtmlEditorCore;
18934         
18935         var output = input;
18936         Roo.each(he.swapCodes, function(sw) { 
18937             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18938             
18939             output = output.replace(swapper, sw[1]);
18940         });
18941         
18942         return output;
18943     },
18944     
18945     
18946     cleanUpChildren : function (n)
18947     {
18948         if (!n.childNodes.length) {
18949             return;
18950         }
18951         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18952            this.cleanUpChild(n.childNodes[i]);
18953         }
18954     },
18955     
18956     
18957         
18958     
18959     cleanUpChild : function (node)
18960     {
18961         var ed = this;
18962         //console.log(node);
18963         if (node.nodeName == "#text") {
18964             // clean up silly Windows -- stuff?
18965             return; 
18966         }
18967         if (node.nodeName == "#comment") {
18968             node.parentNode.removeChild(node);
18969             // clean up silly Windows -- stuff?
18970             return; 
18971         }
18972         var lcname = node.tagName.toLowerCase();
18973         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18974         // whitelist of tags..
18975         
18976         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18977             // remove node.
18978             node.parentNode.removeChild(node);
18979             return;
18980             
18981         }
18982         
18983         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18984         
18985         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18986         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18987         
18988         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18989         //    remove_keep_children = true;
18990         //}
18991         
18992         if (remove_keep_children) {
18993             this.cleanUpChildren(node);
18994             // inserts everything just before this node...
18995             while (node.childNodes.length) {
18996                 var cn = node.childNodes[0];
18997                 node.removeChild(cn);
18998                 node.parentNode.insertBefore(cn, node);
18999             }
19000             node.parentNode.removeChild(node);
19001             return;
19002         }
19003         
19004         if (!node.attributes || !node.attributes.length) {
19005             this.cleanUpChildren(node);
19006             return;
19007         }
19008         
19009         function cleanAttr(n,v)
19010         {
19011             
19012             if (v.match(/^\./) || v.match(/^\//)) {
19013                 return;
19014             }
19015             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19016                 return;
19017             }
19018             if (v.match(/^#/)) {
19019                 return;
19020             }
19021 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19022             node.removeAttribute(n);
19023             
19024         }
19025         
19026         var cwhite = this.cwhite;
19027         var cblack = this.cblack;
19028             
19029         function cleanStyle(n,v)
19030         {
19031             if (v.match(/expression/)) { //XSS?? should we even bother..
19032                 node.removeAttribute(n);
19033                 return;
19034             }
19035             
19036             var parts = v.split(/;/);
19037             var clean = [];
19038             
19039             Roo.each(parts, function(p) {
19040                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19041                 if (!p.length) {
19042                     return true;
19043                 }
19044                 var l = p.split(':').shift().replace(/\s+/g,'');
19045                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19046                 
19047                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19048 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19049                     //node.removeAttribute(n);
19050                     return true;
19051                 }
19052                 //Roo.log()
19053                 // only allow 'c whitelisted system attributes'
19054                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19055 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19056                     //node.removeAttribute(n);
19057                     return true;
19058                 }
19059                 
19060                 
19061                  
19062                 
19063                 clean.push(p);
19064                 return true;
19065             });
19066             if (clean.length) { 
19067                 node.setAttribute(n, clean.join(';'));
19068             } else {
19069                 node.removeAttribute(n);
19070             }
19071             
19072         }
19073         
19074         
19075         for (var i = node.attributes.length-1; i > -1 ; i--) {
19076             var a = node.attributes[i];
19077             //console.log(a);
19078             
19079             if (a.name.toLowerCase().substr(0,2)=='on')  {
19080                 node.removeAttribute(a.name);
19081                 continue;
19082             }
19083             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19084                 node.removeAttribute(a.name);
19085                 continue;
19086             }
19087             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19088                 cleanAttr(a.name,a.value); // fixme..
19089                 continue;
19090             }
19091             if (a.name == 'style') {
19092                 cleanStyle(a.name,a.value);
19093                 continue;
19094             }
19095             /// clean up MS crap..
19096             // tecnically this should be a list of valid class'es..
19097             
19098             
19099             if (a.name == 'class') {
19100                 if (a.value.match(/^Mso/)) {
19101                     node.className = '';
19102                 }
19103                 
19104                 if (a.value.match(/body/)) {
19105                     node.className = '';
19106                 }
19107                 continue;
19108             }
19109             
19110             // style cleanup!?
19111             // class cleanup?
19112             
19113         }
19114         
19115         
19116         this.cleanUpChildren(node);
19117         
19118         
19119     },
19120     /**
19121      * Clean up MS wordisms...
19122      */
19123     cleanWord : function(node)
19124     {
19125         var _t = this;
19126         var cleanWordChildren = function()
19127         {
19128             if (!node.childNodes.length) {
19129                 return;
19130             }
19131             for (var i = node.childNodes.length-1; i > -1 ; i--) {
19132                _t.cleanWord(node.childNodes[i]);
19133             }
19134         }
19135         
19136         
19137         if (!node) {
19138             this.cleanWord(this.doc.body);
19139             return;
19140         }
19141         if (node.nodeName == "#text") {
19142             // clean up silly Windows -- stuff?
19143             return; 
19144         }
19145         if (node.nodeName == "#comment") {
19146             node.parentNode.removeChild(node);
19147             // clean up silly Windows -- stuff?
19148             return; 
19149         }
19150         
19151         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19152             node.parentNode.removeChild(node);
19153             return;
19154         }
19155         
19156         // remove - but keep children..
19157         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19158             while (node.childNodes.length) {
19159                 var cn = node.childNodes[0];
19160                 node.removeChild(cn);
19161                 node.parentNode.insertBefore(cn, node);
19162             }
19163             node.parentNode.removeChild(node);
19164             cleanWordChildren();
19165             return;
19166         }
19167         // clean styles
19168         if (node.className.length) {
19169             
19170             var cn = node.className.split(/\W+/);
19171             var cna = [];
19172             Roo.each(cn, function(cls) {
19173                 if (cls.match(/Mso[a-zA-Z]+/)) {
19174                     return;
19175                 }
19176                 cna.push(cls);
19177             });
19178             node.className = cna.length ? cna.join(' ') : '';
19179             if (!cna.length) {
19180                 node.removeAttribute("class");
19181             }
19182         }
19183         
19184         if (node.hasAttribute("lang")) {
19185             node.removeAttribute("lang");
19186         }
19187         
19188         if (node.hasAttribute("style")) {
19189             
19190             var styles = node.getAttribute("style").split(";");
19191             var nstyle = [];
19192             Roo.each(styles, function(s) {
19193                 if (!s.match(/:/)) {
19194                     return;
19195                 }
19196                 var kv = s.split(":");
19197                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19198                     return;
19199                 }
19200                 // what ever is left... we allow.
19201                 nstyle.push(s);
19202             });
19203             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19204             if (!nstyle.length) {
19205                 node.removeAttribute('style');
19206             }
19207         }
19208         
19209         cleanWordChildren();
19210         
19211         
19212     },
19213     domToHTML : function(currentElement, depth, nopadtext) {
19214         
19215         depth = depth || 0;
19216         nopadtext = nopadtext || false;
19217     
19218         if (!currentElement) {
19219             return this.domToHTML(this.doc.body);
19220         }
19221         
19222         //Roo.log(currentElement);
19223         var j;
19224         var allText = false;
19225         var nodeName = currentElement.nodeName;
19226         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19227         
19228         if  (nodeName == '#text') {
19229             
19230             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19231         }
19232         
19233         
19234         var ret = '';
19235         if (nodeName != 'BODY') {
19236              
19237             var i = 0;
19238             // Prints the node tagName, such as <A>, <IMG>, etc
19239             if (tagName) {
19240                 var attr = [];
19241                 for(i = 0; i < currentElement.attributes.length;i++) {
19242                     // quoting?
19243                     var aname = currentElement.attributes.item(i).name;
19244                     if (!currentElement.attributes.item(i).value.length) {
19245                         continue;
19246                     }
19247                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19248                 }
19249                 
19250                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19251             } 
19252             else {
19253                 
19254                 // eack
19255             }
19256         } else {
19257             tagName = false;
19258         }
19259         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19260             return ret;
19261         }
19262         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19263             nopadtext = true;
19264         }
19265         
19266         
19267         // Traverse the tree
19268         i = 0;
19269         var currentElementChild = currentElement.childNodes.item(i);
19270         var allText = true;
19271         var innerHTML  = '';
19272         lastnode = '';
19273         while (currentElementChild) {
19274             // Formatting code (indent the tree so it looks nice on the screen)
19275             var nopad = nopadtext;
19276             if (lastnode == 'SPAN') {
19277                 nopad  = true;
19278             }
19279             // text
19280             if  (currentElementChild.nodeName == '#text') {
19281                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19282                 toadd = nopadtext ? toadd : toadd.trim();
19283                 if (!nopad && toadd.length > 80) {
19284                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19285                 }
19286                 innerHTML  += toadd;
19287                 
19288                 i++;
19289                 currentElementChild = currentElement.childNodes.item(i);
19290                 lastNode = '';
19291                 continue;
19292             }
19293             allText = false;
19294             
19295             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19296                 
19297             // Recursively traverse the tree structure of the child node
19298             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19299             lastnode = currentElementChild.nodeName;
19300             i++;
19301             currentElementChild=currentElement.childNodes.item(i);
19302         }
19303         
19304         ret += innerHTML;
19305         
19306         if (!allText) {
19307                 // The remaining code is mostly for formatting the tree
19308             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19309         }
19310         
19311         
19312         if (tagName) {
19313             ret+= "</"+tagName+">";
19314         }
19315         return ret;
19316         
19317     },
19318         
19319     applyBlacklists : function()
19320     {
19321         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19322         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19323         
19324         this.white = [];
19325         this.black = [];
19326         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19327             if (b.indexOf(tag) > -1) {
19328                 return;
19329             }
19330             this.white.push(tag);
19331             
19332         }, this);
19333         
19334         Roo.each(w, function(tag) {
19335             if (b.indexOf(tag) > -1) {
19336                 return;
19337             }
19338             if (this.white.indexOf(tag) > -1) {
19339                 return;
19340             }
19341             this.white.push(tag);
19342             
19343         }, this);
19344         
19345         
19346         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19347             if (w.indexOf(tag) > -1) {
19348                 return;
19349             }
19350             this.black.push(tag);
19351             
19352         }, this);
19353         
19354         Roo.each(b, function(tag) {
19355             if (w.indexOf(tag) > -1) {
19356                 return;
19357             }
19358             if (this.black.indexOf(tag) > -1) {
19359                 return;
19360             }
19361             this.black.push(tag);
19362             
19363         }, this);
19364         
19365         
19366         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19367         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19368         
19369         this.cwhite = [];
19370         this.cblack = [];
19371         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19372             if (b.indexOf(tag) > -1) {
19373                 return;
19374             }
19375             this.cwhite.push(tag);
19376             
19377         }, this);
19378         
19379         Roo.each(w, function(tag) {
19380             if (b.indexOf(tag) > -1) {
19381                 return;
19382             }
19383             if (this.cwhite.indexOf(tag) > -1) {
19384                 return;
19385             }
19386             this.cwhite.push(tag);
19387             
19388         }, this);
19389         
19390         
19391         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19392             if (w.indexOf(tag) > -1) {
19393                 return;
19394             }
19395             this.cblack.push(tag);
19396             
19397         }, this);
19398         
19399         Roo.each(b, function(tag) {
19400             if (w.indexOf(tag) > -1) {
19401                 return;
19402             }
19403             if (this.cblack.indexOf(tag) > -1) {
19404                 return;
19405             }
19406             this.cblack.push(tag);
19407             
19408         }, this);
19409     },
19410     
19411     setStylesheets : function(stylesheets)
19412     {
19413         if(typeof(stylesheets) == 'string'){
19414             Roo.get(this.iframe.contentDocument.head).createChild({
19415                 tag : 'link',
19416                 rel : 'stylesheet',
19417                 type : 'text/css',
19418                 href : stylesheets
19419             });
19420             
19421             return;
19422         }
19423         var _this = this;
19424      
19425         Roo.each(stylesheets, function(s) {
19426             if(!s.length){
19427                 return;
19428             }
19429             
19430             Roo.get(_this.iframe.contentDocument.head).createChild({
19431                 tag : 'link',
19432                 rel : 'stylesheet',
19433                 type : 'text/css',
19434                 href : s
19435             });
19436         });
19437
19438         
19439     },
19440     
19441     removeStylesheets : function()
19442     {
19443         var _this = this;
19444         
19445         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19446             s.remove();
19447         });
19448     }
19449     
19450     // hide stuff that is not compatible
19451     /**
19452      * @event blur
19453      * @hide
19454      */
19455     /**
19456      * @event change
19457      * @hide
19458      */
19459     /**
19460      * @event focus
19461      * @hide
19462      */
19463     /**
19464      * @event specialkey
19465      * @hide
19466      */
19467     /**
19468      * @cfg {String} fieldClass @hide
19469      */
19470     /**
19471      * @cfg {String} focusClass @hide
19472      */
19473     /**
19474      * @cfg {String} autoCreate @hide
19475      */
19476     /**
19477      * @cfg {String} inputType @hide
19478      */
19479     /**
19480      * @cfg {String} invalidClass @hide
19481      */
19482     /**
19483      * @cfg {String} invalidText @hide
19484      */
19485     /**
19486      * @cfg {String} msgFx @hide
19487      */
19488     /**
19489      * @cfg {String} validateOnBlur @hide
19490      */
19491 });
19492
19493 Roo.HtmlEditorCore.white = [
19494         'area', 'br', 'img', 'input', 'hr', 'wbr',
19495         
19496        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19497        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19498        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19499        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19500        'table',   'ul',         'xmp', 
19501        
19502        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19503       'thead',   'tr', 
19504      
19505       'dir', 'menu', 'ol', 'ul', 'dl',
19506        
19507       'embed',  'object'
19508 ];
19509
19510
19511 Roo.HtmlEditorCore.black = [
19512     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19513         'applet', // 
19514         'base',   'basefont', 'bgsound', 'blink',  'body', 
19515         'frame',  'frameset', 'head',    'html',   'ilayer', 
19516         'iframe', 'layer',  'link',     'meta',    'object',   
19517         'script', 'style' ,'title',  'xml' // clean later..
19518 ];
19519 Roo.HtmlEditorCore.clean = [
19520     'script', 'style', 'title', 'xml'
19521 ];
19522 Roo.HtmlEditorCore.remove = [
19523     'font'
19524 ];
19525 // attributes..
19526
19527 Roo.HtmlEditorCore.ablack = [
19528     'on'
19529 ];
19530     
19531 Roo.HtmlEditorCore.aclean = [ 
19532     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19533 ];
19534
19535 // protocols..
19536 Roo.HtmlEditorCore.pwhite= [
19537         'http',  'https',  'mailto'
19538 ];
19539
19540 // white listed style attributes.
19541 Roo.HtmlEditorCore.cwhite= [
19542       //  'text-align', /// default is to allow most things..
19543       
19544          
19545 //        'font-size'//??
19546 ];
19547
19548 // black listed style attributes.
19549 Roo.HtmlEditorCore.cblack= [
19550       //  'font-size' -- this can be set by the project 
19551 ];
19552
19553
19554 Roo.HtmlEditorCore.swapCodes   =[ 
19555     [    8211, "--" ], 
19556     [    8212, "--" ], 
19557     [    8216,  "'" ],  
19558     [    8217, "'" ],  
19559     [    8220, '"' ],  
19560     [    8221, '"' ],  
19561     [    8226, "*" ],  
19562     [    8230, "..." ]
19563 ]; 
19564
19565     /*
19566  * - LGPL
19567  *
19568  * HtmlEditor
19569  * 
19570  */
19571
19572 /**
19573  * @class Roo.bootstrap.HtmlEditor
19574  * @extends Roo.bootstrap.TextArea
19575  * Bootstrap HtmlEditor class
19576
19577  * @constructor
19578  * Create a new HtmlEditor
19579  * @param {Object} config The config object
19580  */
19581
19582 Roo.bootstrap.HtmlEditor = function(config){
19583     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19584     if (!this.toolbars) {
19585         this.toolbars = [];
19586     }
19587     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19588     this.addEvents({
19589             /**
19590              * @event initialize
19591              * Fires when the editor is fully initialized (including the iframe)
19592              * @param {HtmlEditor} this
19593              */
19594             initialize: true,
19595             /**
19596              * @event activate
19597              * Fires when the editor is first receives the focus. Any insertion must wait
19598              * until after this event.
19599              * @param {HtmlEditor} this
19600              */
19601             activate: true,
19602              /**
19603              * @event beforesync
19604              * Fires before the textarea is updated with content from the editor iframe. Return false
19605              * to cancel the sync.
19606              * @param {HtmlEditor} this
19607              * @param {String} html
19608              */
19609             beforesync: true,
19610              /**
19611              * @event beforepush
19612              * Fires before the iframe editor is updated with content from the textarea. Return false
19613              * to cancel the push.
19614              * @param {HtmlEditor} this
19615              * @param {String} html
19616              */
19617             beforepush: true,
19618              /**
19619              * @event sync
19620              * Fires when the textarea is updated with content from the editor iframe.
19621              * @param {HtmlEditor} this
19622              * @param {String} html
19623              */
19624             sync: true,
19625              /**
19626              * @event push
19627              * Fires when the iframe editor is updated with content from the textarea.
19628              * @param {HtmlEditor} this
19629              * @param {String} html
19630              */
19631             push: true,
19632              /**
19633              * @event editmodechange
19634              * Fires when the editor switches edit modes
19635              * @param {HtmlEditor} this
19636              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19637              */
19638             editmodechange: true,
19639             /**
19640              * @event editorevent
19641              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19642              * @param {HtmlEditor} this
19643              */
19644             editorevent: true,
19645             /**
19646              * @event firstfocus
19647              * Fires when on first focus - needed by toolbars..
19648              * @param {HtmlEditor} this
19649              */
19650             firstfocus: true,
19651             /**
19652              * @event autosave
19653              * Auto save the htmlEditor value as a file into Events
19654              * @param {HtmlEditor} this
19655              */
19656             autosave: true,
19657             /**
19658              * @event savedpreview
19659              * preview the saved version of htmlEditor
19660              * @param {HtmlEditor} this
19661              */
19662             savedpreview: true
19663         });
19664 };
19665
19666
19667 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19668     
19669     
19670       /**
19671      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19672      */
19673     toolbars : false,
19674    
19675      /**
19676      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19677      *                        Roo.resizable.
19678      */
19679     resizable : false,
19680      /**
19681      * @cfg {Number} height (in pixels)
19682      */   
19683     height: 300,
19684    /**
19685      * @cfg {Number} width (in pixels)
19686      */   
19687     width: false,
19688     
19689     /**
19690      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19691      * 
19692      */
19693     stylesheets: false,
19694     
19695     // id of frame..
19696     frameId: false,
19697     
19698     // private properties
19699     validationEvent : false,
19700     deferHeight: true,
19701     initialized : false,
19702     activated : false,
19703     
19704     onFocus : Roo.emptyFn,
19705     iframePad:3,
19706     hideMode:'offsets',
19707     
19708     
19709     tbContainer : false,
19710     
19711     toolbarContainer :function() {
19712         return this.wrap.select('.x-html-editor-tb',true).first();
19713     },
19714
19715     /**
19716      * Protected method that will not generally be called directly. It
19717      * is called when the editor creates its toolbar. Override this method if you need to
19718      * add custom toolbar buttons.
19719      * @param {HtmlEditor} editor
19720      */
19721     createToolbar : function(){
19722         
19723         Roo.log("create toolbars");
19724         
19725         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19726         this.toolbars[0].render(this.toolbarContainer());
19727         
19728         return;
19729         
19730 //        if (!editor.toolbars || !editor.toolbars.length) {
19731 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19732 //        }
19733 //        
19734 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19735 //            editor.toolbars[i] = Roo.factory(
19736 //                    typeof(editor.toolbars[i]) == 'string' ?
19737 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19738 //                Roo.bootstrap.HtmlEditor);
19739 //            editor.toolbars[i].init(editor);
19740 //        }
19741     },
19742
19743      
19744     // private
19745     onRender : function(ct, position)
19746     {
19747        // Roo.log("Call onRender: " + this.xtype);
19748         var _t = this;
19749         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19750       
19751         this.wrap = this.inputEl().wrap({
19752             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19753         });
19754         
19755         this.editorcore.onRender(ct, position);
19756          
19757         if (this.resizable) {
19758             this.resizeEl = new Roo.Resizable(this.wrap, {
19759                 pinned : true,
19760                 wrap: true,
19761                 dynamic : true,
19762                 minHeight : this.height,
19763                 height: this.height,
19764                 handles : this.resizable,
19765                 width: this.width,
19766                 listeners : {
19767                     resize : function(r, w, h) {
19768                         _t.onResize(w,h); // -something
19769                     }
19770                 }
19771             });
19772             
19773         }
19774         this.createToolbar(this);
19775        
19776         
19777         if(!this.width && this.resizable){
19778             this.setSize(this.wrap.getSize());
19779         }
19780         if (this.resizeEl) {
19781             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19782             // should trigger onReize..
19783         }
19784         
19785     },
19786
19787     // private
19788     onResize : function(w, h)
19789     {
19790         Roo.log('resize: ' +w + ',' + h );
19791         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19792         var ew = false;
19793         var eh = false;
19794         
19795         if(this.inputEl() ){
19796             if(typeof w == 'number'){
19797                 var aw = w - this.wrap.getFrameWidth('lr');
19798                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19799                 ew = aw;
19800             }
19801             if(typeof h == 'number'){
19802                  var tbh = -11;  // fixme it needs to tool bar size!
19803                 for (var i =0; i < this.toolbars.length;i++) {
19804                     // fixme - ask toolbars for heights?
19805                     tbh += this.toolbars[i].el.getHeight();
19806                     //if (this.toolbars[i].footer) {
19807                     //    tbh += this.toolbars[i].footer.el.getHeight();
19808                     //}
19809                 }
19810               
19811                 
19812                 
19813                 
19814                 
19815                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19816                 ah -= 5; // knock a few pixes off for look..
19817                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19818                 var eh = ah;
19819             }
19820         }
19821         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19822         this.editorcore.onResize(ew,eh);
19823         
19824     },
19825
19826     /**
19827      * Toggles the editor between standard and source edit mode.
19828      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19829      */
19830     toggleSourceEdit : function(sourceEditMode)
19831     {
19832         this.editorcore.toggleSourceEdit(sourceEditMode);
19833         
19834         if(this.editorcore.sourceEditMode){
19835             Roo.log('editor - showing textarea');
19836             
19837 //            Roo.log('in');
19838 //            Roo.log(this.syncValue());
19839             this.syncValue();
19840             this.inputEl().removeClass(['hide', 'x-hidden']);
19841             this.inputEl().dom.removeAttribute('tabIndex');
19842             this.inputEl().focus();
19843         }else{
19844             Roo.log('editor - hiding textarea');
19845 //            Roo.log('out')
19846 //            Roo.log(this.pushValue()); 
19847             this.pushValue();
19848             
19849             this.inputEl().addClass(['hide', 'x-hidden']);
19850             this.inputEl().dom.setAttribute('tabIndex', -1);
19851             //this.deferFocus();
19852         }
19853          
19854         if(this.resizable){
19855             this.setSize(this.wrap.getSize());
19856         }
19857         
19858         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19859     },
19860  
19861     // private (for BoxComponent)
19862     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19863
19864     // private (for BoxComponent)
19865     getResizeEl : function(){
19866         return this.wrap;
19867     },
19868
19869     // private (for BoxComponent)
19870     getPositionEl : function(){
19871         return this.wrap;
19872     },
19873
19874     // private
19875     initEvents : function(){
19876         this.originalValue = this.getValue();
19877     },
19878
19879 //    /**
19880 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19881 //     * @method
19882 //     */
19883 //    markInvalid : Roo.emptyFn,
19884 //    /**
19885 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19886 //     * @method
19887 //     */
19888 //    clearInvalid : Roo.emptyFn,
19889
19890     setValue : function(v){
19891         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19892         this.editorcore.pushValue();
19893     },
19894
19895      
19896     // private
19897     deferFocus : function(){
19898         this.focus.defer(10, this);
19899     },
19900
19901     // doc'ed in Field
19902     focus : function(){
19903         this.editorcore.focus();
19904         
19905     },
19906       
19907
19908     // private
19909     onDestroy : function(){
19910         
19911         
19912         
19913         if(this.rendered){
19914             
19915             for (var i =0; i < this.toolbars.length;i++) {
19916                 // fixme - ask toolbars for heights?
19917                 this.toolbars[i].onDestroy();
19918             }
19919             
19920             this.wrap.dom.innerHTML = '';
19921             this.wrap.remove();
19922         }
19923     },
19924
19925     // private
19926     onFirstFocus : function(){
19927         //Roo.log("onFirstFocus");
19928         this.editorcore.onFirstFocus();
19929          for (var i =0; i < this.toolbars.length;i++) {
19930             this.toolbars[i].onFirstFocus();
19931         }
19932         
19933     },
19934     
19935     // private
19936     syncValue : function()
19937     {   
19938         this.editorcore.syncValue();
19939     },
19940     
19941     pushValue : function()
19942     {   
19943         this.editorcore.pushValue();
19944     }
19945      
19946     
19947     // hide stuff that is not compatible
19948     /**
19949      * @event blur
19950      * @hide
19951      */
19952     /**
19953      * @event change
19954      * @hide
19955      */
19956     /**
19957      * @event focus
19958      * @hide
19959      */
19960     /**
19961      * @event specialkey
19962      * @hide
19963      */
19964     /**
19965      * @cfg {String} fieldClass @hide
19966      */
19967     /**
19968      * @cfg {String} focusClass @hide
19969      */
19970     /**
19971      * @cfg {String} autoCreate @hide
19972      */
19973     /**
19974      * @cfg {String} inputType @hide
19975      */
19976     /**
19977      * @cfg {String} invalidClass @hide
19978      */
19979     /**
19980      * @cfg {String} invalidText @hide
19981      */
19982     /**
19983      * @cfg {String} msgFx @hide
19984      */
19985     /**
19986      * @cfg {String} validateOnBlur @hide
19987      */
19988 });
19989  
19990     
19991    
19992    
19993    
19994       
19995 Roo.namespace('Roo.bootstrap.htmleditor');
19996 /**
19997  * @class Roo.bootstrap.HtmlEditorToolbar1
19998  * Basic Toolbar
19999  * 
20000  * Usage:
20001  *
20002  new Roo.bootstrap.HtmlEditor({
20003     ....
20004     toolbars : [
20005         new Roo.bootstrap.HtmlEditorToolbar1({
20006             disable : { fonts: 1 , format: 1, ..., ... , ...],
20007             btns : [ .... ]
20008         })
20009     }
20010      
20011  * 
20012  * @cfg {Object} disable List of elements to disable..
20013  * @cfg {Array} btns List of additional buttons.
20014  * 
20015  * 
20016  * NEEDS Extra CSS? 
20017  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20018  */
20019  
20020 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20021 {
20022     
20023     Roo.apply(this, config);
20024     
20025     // default disabled, based on 'good practice'..
20026     this.disable = this.disable || {};
20027     Roo.applyIf(this.disable, {
20028         fontSize : true,
20029         colors : true,
20030         specialElements : true
20031     });
20032     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20033     
20034     this.editor = config.editor;
20035     this.editorcore = config.editor.editorcore;
20036     
20037     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20038     
20039     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20040     // dont call parent... till later.
20041 }
20042 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20043      
20044     bar : true,
20045     
20046     editor : false,
20047     editorcore : false,
20048     
20049     
20050     formats : [
20051         "p" ,  
20052         "h1","h2","h3","h4","h5","h6", 
20053         "pre", "code", 
20054         "abbr", "acronym", "address", "cite", "samp", "var",
20055         'div','span'
20056     ],
20057     
20058     onRender : function(ct, position)
20059     {
20060        // Roo.log("Call onRender: " + this.xtype);
20061         
20062        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20063        Roo.log(this.el);
20064        this.el.dom.style.marginBottom = '0';
20065        var _this = this;
20066        var editorcore = this.editorcore;
20067        var editor= this.editor;
20068        
20069        var children = [];
20070        var btn = function(id,cmd , toggle, handler){
20071        
20072             var  event = toggle ? 'toggle' : 'click';
20073        
20074             var a = {
20075                 size : 'sm',
20076                 xtype: 'Button',
20077                 xns: Roo.bootstrap,
20078                 glyphicon : id,
20079                 cmd : id || cmd,
20080                 enableToggle:toggle !== false,
20081                 //html : 'submit'
20082                 pressed : toggle ? false : null,
20083                 listeners : {}
20084             }
20085             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20086                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20087             }
20088             children.push(a);
20089             return a;
20090        }
20091         
20092         var style = {
20093                 xtype: 'Button',
20094                 size : 'sm',
20095                 xns: Roo.bootstrap,
20096                 glyphicon : 'font',
20097                 //html : 'submit'
20098                 menu : {
20099                     xtype: 'Menu',
20100                     xns: Roo.bootstrap,
20101                     items:  []
20102                 }
20103         };
20104         Roo.each(this.formats, function(f) {
20105             style.menu.items.push({
20106                 xtype :'MenuItem',
20107                 xns: Roo.bootstrap,
20108                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20109                 tagname : f,
20110                 listeners : {
20111                     click : function()
20112                     {
20113                         editorcore.insertTag(this.tagname);
20114                         editor.focus();
20115                     }
20116                 }
20117                 
20118             });
20119         });
20120          children.push(style);   
20121             
20122             
20123         btn('bold',false,true);
20124         btn('italic',false,true);
20125         btn('align-left', 'justifyleft',true);
20126         btn('align-center', 'justifycenter',true);
20127         btn('align-right' , 'justifyright',true);
20128         btn('link', false, false, function(btn) {
20129             //Roo.log("create link?");
20130             var url = prompt(this.createLinkText, this.defaultLinkValue);
20131             if(url && url != 'http:/'+'/'){
20132                 this.editorcore.relayCmd('createlink', url);
20133             }
20134         }),
20135         btn('list','insertunorderedlist',true);
20136         btn('pencil', false,true, function(btn){
20137                 Roo.log(this);
20138                 
20139                 this.toggleSourceEdit(btn.pressed);
20140         });
20141         /*
20142         var cog = {
20143                 xtype: 'Button',
20144                 size : 'sm',
20145                 xns: Roo.bootstrap,
20146                 glyphicon : 'cog',
20147                 //html : 'submit'
20148                 menu : {
20149                     xtype: 'Menu',
20150                     xns: Roo.bootstrap,
20151                     items:  []
20152                 }
20153         };
20154         
20155         cog.menu.items.push({
20156             xtype :'MenuItem',
20157             xns: Roo.bootstrap,
20158             html : Clean styles,
20159             tagname : f,
20160             listeners : {
20161                 click : function()
20162                 {
20163                     editorcore.insertTag(this.tagname);
20164                     editor.focus();
20165                 }
20166             }
20167             
20168         });
20169        */
20170         
20171          
20172        this.xtype = 'NavSimplebar';
20173         
20174         for(var i=0;i< children.length;i++) {
20175             
20176             this.buttons.add(this.addxtypeChild(children[i]));
20177             
20178         }
20179         
20180         editor.on('editorevent', this.updateToolbar, this);
20181     },
20182     onBtnClick : function(id)
20183     {
20184        this.editorcore.relayCmd(id);
20185        this.editorcore.focus();
20186     },
20187     
20188     /**
20189      * Protected method that will not generally be called directly. It triggers
20190      * a toolbar update by reading the markup state of the current selection in the editor.
20191      */
20192     updateToolbar: function(){
20193
20194         if(!this.editorcore.activated){
20195             this.editor.onFirstFocus(); // is this neeed?
20196             return;
20197         }
20198
20199         var btns = this.buttons; 
20200         var doc = this.editorcore.doc;
20201         btns.get('bold').setActive(doc.queryCommandState('bold'));
20202         btns.get('italic').setActive(doc.queryCommandState('italic'));
20203         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20204         
20205         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20206         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20207         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20208         
20209         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20210         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20211          /*
20212         
20213         var ans = this.editorcore.getAllAncestors();
20214         if (this.formatCombo) {
20215             
20216             
20217             var store = this.formatCombo.store;
20218             this.formatCombo.setValue("");
20219             for (var i =0; i < ans.length;i++) {
20220                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20221                     // select it..
20222                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20223                     break;
20224                 }
20225             }
20226         }
20227         
20228         
20229         
20230         // hides menus... - so this cant be on a menu...
20231         Roo.bootstrap.MenuMgr.hideAll();
20232         */
20233         Roo.bootstrap.MenuMgr.hideAll();
20234         //this.editorsyncValue();
20235     },
20236     onFirstFocus: function() {
20237         this.buttons.each(function(item){
20238            item.enable();
20239         });
20240     },
20241     toggleSourceEdit : function(sourceEditMode){
20242         
20243           
20244         if(sourceEditMode){
20245             Roo.log("disabling buttons");
20246            this.buttons.each( function(item){
20247                 if(item.cmd != 'pencil'){
20248                     item.disable();
20249                 }
20250             });
20251           
20252         }else{
20253             Roo.log("enabling buttons");
20254             if(this.editorcore.initialized){
20255                 this.buttons.each( function(item){
20256                     item.enable();
20257                 });
20258             }
20259             
20260         }
20261         Roo.log("calling toggole on editor");
20262         // tell the editor that it's been pressed..
20263         this.editor.toggleSourceEdit(sourceEditMode);
20264        
20265     }
20266 });
20267
20268
20269
20270
20271
20272 /**
20273  * @class Roo.bootstrap.Table.AbstractSelectionModel
20274  * @extends Roo.util.Observable
20275  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20276  * implemented by descendant classes.  This class should not be directly instantiated.
20277  * @constructor
20278  */
20279 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20280     this.locked = false;
20281     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20282 };
20283
20284
20285 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20286     /** @ignore Called by the grid automatically. Do not call directly. */
20287     init : function(grid){
20288         this.grid = grid;
20289         this.initEvents();
20290     },
20291
20292     /**
20293      * Locks the selections.
20294      */
20295     lock : function(){
20296         this.locked = true;
20297     },
20298
20299     /**
20300      * Unlocks the selections.
20301      */
20302     unlock : function(){
20303         this.locked = false;
20304     },
20305
20306     /**
20307      * Returns true if the selections are locked.
20308      * @return {Boolean}
20309      */
20310     isLocked : function(){
20311         return this.locked;
20312     }
20313 });
20314 /**
20315  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20316  * @class Roo.bootstrap.Table.RowSelectionModel
20317  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20318  * It supports multiple selections and keyboard selection/navigation. 
20319  * @constructor
20320  * @param {Object} config
20321  */
20322
20323 Roo.bootstrap.Table.RowSelectionModel = function(config){
20324     Roo.apply(this, config);
20325     this.selections = new Roo.util.MixedCollection(false, function(o){
20326         return o.id;
20327     });
20328
20329     this.last = false;
20330     this.lastActive = false;
20331
20332     this.addEvents({
20333         /**
20334              * @event selectionchange
20335              * Fires when the selection changes
20336              * @param {SelectionModel} this
20337              */
20338             "selectionchange" : true,
20339         /**
20340              * @event afterselectionchange
20341              * Fires after the selection changes (eg. by key press or clicking)
20342              * @param {SelectionModel} this
20343              */
20344             "afterselectionchange" : true,
20345         /**
20346              * @event beforerowselect
20347              * Fires when a row is selected being selected, return false to cancel.
20348              * @param {SelectionModel} this
20349              * @param {Number} rowIndex The selected index
20350              * @param {Boolean} keepExisting False if other selections will be cleared
20351              */
20352             "beforerowselect" : true,
20353         /**
20354              * @event rowselect
20355              * Fires when a row is selected.
20356              * @param {SelectionModel} this
20357              * @param {Number} rowIndex The selected index
20358              * @param {Roo.data.Record} r The record
20359              */
20360             "rowselect" : true,
20361         /**
20362              * @event rowdeselect
20363              * Fires when a row is deselected.
20364              * @param {SelectionModel} this
20365              * @param {Number} rowIndex The selected index
20366              */
20367         "rowdeselect" : true
20368     });
20369     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20370     this.locked = false;
20371 };
20372
20373 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20374     /**
20375      * @cfg {Boolean} singleSelect
20376      * True to allow selection of only one row at a time (defaults to false)
20377      */
20378     singleSelect : false,
20379
20380     // private
20381     initEvents : function(){
20382
20383         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20384             this.grid.on("mousedown", this.handleMouseDown, this);
20385         }else{ // allow click to work like normal
20386             this.grid.on("rowclick", this.handleDragableRowClick, this);
20387         }
20388
20389         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20390             "up" : function(e){
20391                 if(!e.shiftKey){
20392                     this.selectPrevious(e.shiftKey);
20393                 }else if(this.last !== false && this.lastActive !== false){
20394                     var last = this.last;
20395                     this.selectRange(this.last,  this.lastActive-1);
20396                     this.grid.getView().focusRow(this.lastActive);
20397                     if(last !== false){
20398                         this.last = last;
20399                     }
20400                 }else{
20401                     this.selectFirstRow();
20402                 }
20403                 this.fireEvent("afterselectionchange", this);
20404             },
20405             "down" : function(e){
20406                 if(!e.shiftKey){
20407                     this.selectNext(e.shiftKey);
20408                 }else if(this.last !== false && this.lastActive !== false){
20409                     var last = this.last;
20410                     this.selectRange(this.last,  this.lastActive+1);
20411                     this.grid.getView().focusRow(this.lastActive);
20412                     if(last !== false){
20413                         this.last = last;
20414                     }
20415                 }else{
20416                     this.selectFirstRow();
20417                 }
20418                 this.fireEvent("afterselectionchange", this);
20419             },
20420             scope: this
20421         });
20422
20423         var view = this.grid.view;
20424         view.on("refresh", this.onRefresh, this);
20425         view.on("rowupdated", this.onRowUpdated, this);
20426         view.on("rowremoved", this.onRemove, this);
20427     },
20428
20429     // private
20430     onRefresh : function(){
20431         var ds = this.grid.dataSource, i, v = this.grid.view;
20432         var s = this.selections;
20433         s.each(function(r){
20434             if((i = ds.indexOfId(r.id)) != -1){
20435                 v.onRowSelect(i);
20436             }else{
20437                 s.remove(r);
20438             }
20439         });
20440     },
20441
20442     // private
20443     onRemove : function(v, index, r){
20444         this.selections.remove(r);
20445     },
20446
20447     // private
20448     onRowUpdated : function(v, index, r){
20449         if(this.isSelected(r)){
20450             v.onRowSelect(index);
20451         }
20452     },
20453
20454     /**
20455      * Select records.
20456      * @param {Array} records The records to select
20457      * @param {Boolean} keepExisting (optional) True to keep existing selections
20458      */
20459     selectRecords : function(records, keepExisting){
20460         if(!keepExisting){
20461             this.clearSelections();
20462         }
20463         var ds = this.grid.dataSource;
20464         for(var i = 0, len = records.length; i < len; i++){
20465             this.selectRow(ds.indexOf(records[i]), true);
20466         }
20467     },
20468
20469     /**
20470      * Gets the number of selected rows.
20471      * @return {Number}
20472      */
20473     getCount : function(){
20474         return this.selections.length;
20475     },
20476
20477     /**
20478      * Selects the first row in the grid.
20479      */
20480     selectFirstRow : function(){
20481         this.selectRow(0);
20482     },
20483
20484     /**
20485      * Select the last row.
20486      * @param {Boolean} keepExisting (optional) True to keep existing selections
20487      */
20488     selectLastRow : function(keepExisting){
20489         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20490     },
20491
20492     /**
20493      * Selects the row immediately following the last selected row.
20494      * @param {Boolean} keepExisting (optional) True to keep existing selections
20495      */
20496     selectNext : function(keepExisting){
20497         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20498             this.selectRow(this.last+1, keepExisting);
20499             this.grid.getView().focusRow(this.last);
20500         }
20501     },
20502
20503     /**
20504      * Selects the row that precedes the last selected row.
20505      * @param {Boolean} keepExisting (optional) True to keep existing selections
20506      */
20507     selectPrevious : function(keepExisting){
20508         if(this.last){
20509             this.selectRow(this.last-1, keepExisting);
20510             this.grid.getView().focusRow(this.last);
20511         }
20512     },
20513
20514     /**
20515      * Returns the selected records
20516      * @return {Array} Array of selected records
20517      */
20518     getSelections : function(){
20519         return [].concat(this.selections.items);
20520     },
20521
20522     /**
20523      * Returns the first selected record.
20524      * @return {Record}
20525      */
20526     getSelected : function(){
20527         return this.selections.itemAt(0);
20528     },
20529
20530
20531     /**
20532      * Clears all selections.
20533      */
20534     clearSelections : function(fast){
20535         if(this.locked) return;
20536         if(fast !== true){
20537             var ds = this.grid.dataSource;
20538             var s = this.selections;
20539             s.each(function(r){
20540                 this.deselectRow(ds.indexOfId(r.id));
20541             }, this);
20542             s.clear();
20543         }else{
20544             this.selections.clear();
20545         }
20546         this.last = false;
20547     },
20548
20549
20550     /**
20551      * Selects all rows.
20552      */
20553     selectAll : function(){
20554         if(this.locked) return;
20555         this.selections.clear();
20556         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20557             this.selectRow(i, true);
20558         }
20559     },
20560
20561     /**
20562      * Returns True if there is a selection.
20563      * @return {Boolean}
20564      */
20565     hasSelection : function(){
20566         return this.selections.length > 0;
20567     },
20568
20569     /**
20570      * Returns True if the specified row is selected.
20571      * @param {Number/Record} record The record or index of the record to check
20572      * @return {Boolean}
20573      */
20574     isSelected : function(index){
20575         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20576         return (r && this.selections.key(r.id) ? true : false);
20577     },
20578
20579     /**
20580      * Returns True if the specified record id is selected.
20581      * @param {String} id The id of record to check
20582      * @return {Boolean}
20583      */
20584     isIdSelected : function(id){
20585         return (this.selections.key(id) ? true : false);
20586     },
20587
20588     // private
20589     handleMouseDown : function(e, t){
20590         var view = this.grid.getView(), rowIndex;
20591         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20592             return;
20593         };
20594         if(e.shiftKey && this.last !== false){
20595             var last = this.last;
20596             this.selectRange(last, rowIndex, e.ctrlKey);
20597             this.last = last; // reset the last
20598             view.focusRow(rowIndex);
20599         }else{
20600             var isSelected = this.isSelected(rowIndex);
20601             if(e.button !== 0 && isSelected){
20602                 view.focusRow(rowIndex);
20603             }else if(e.ctrlKey && isSelected){
20604                 this.deselectRow(rowIndex);
20605             }else if(!isSelected){
20606                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20607                 view.focusRow(rowIndex);
20608             }
20609         }
20610         this.fireEvent("afterselectionchange", this);
20611     },
20612     // private
20613     handleDragableRowClick :  function(grid, rowIndex, e) 
20614     {
20615         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20616             this.selectRow(rowIndex, false);
20617             grid.view.focusRow(rowIndex);
20618              this.fireEvent("afterselectionchange", this);
20619         }
20620     },
20621     
20622     /**
20623      * Selects multiple rows.
20624      * @param {Array} rows Array of the indexes of the row to select
20625      * @param {Boolean} keepExisting (optional) True to keep existing selections
20626      */
20627     selectRows : function(rows, keepExisting){
20628         if(!keepExisting){
20629             this.clearSelections();
20630         }
20631         for(var i = 0, len = rows.length; i < len; i++){
20632             this.selectRow(rows[i], true);
20633         }
20634     },
20635
20636     /**
20637      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20638      * @param {Number} startRow The index of the first row in the range
20639      * @param {Number} endRow The index of the last row in the range
20640      * @param {Boolean} keepExisting (optional) True to retain existing selections
20641      */
20642     selectRange : function(startRow, endRow, keepExisting){
20643         if(this.locked) return;
20644         if(!keepExisting){
20645             this.clearSelections();
20646         }
20647         if(startRow <= endRow){
20648             for(var i = startRow; i <= endRow; i++){
20649                 this.selectRow(i, true);
20650             }
20651         }else{
20652             for(var i = startRow; i >= endRow; i--){
20653                 this.selectRow(i, true);
20654             }
20655         }
20656     },
20657
20658     /**
20659      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20660      * @param {Number} startRow The index of the first row in the range
20661      * @param {Number} endRow The index of the last row in the range
20662      */
20663     deselectRange : function(startRow, endRow, preventViewNotify){
20664         if(this.locked) return;
20665         for(var i = startRow; i <= endRow; i++){
20666             this.deselectRow(i, preventViewNotify);
20667         }
20668     },
20669
20670     /**
20671      * Selects a row.
20672      * @param {Number} row The index of the row to select
20673      * @param {Boolean} keepExisting (optional) True to keep existing selections
20674      */
20675     selectRow : function(index, keepExisting, preventViewNotify){
20676         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20677         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20678             if(!keepExisting || this.singleSelect){
20679                 this.clearSelections();
20680             }
20681             var r = this.grid.dataSource.getAt(index);
20682             this.selections.add(r);
20683             this.last = this.lastActive = index;
20684             if(!preventViewNotify){
20685                 this.grid.getView().onRowSelect(index);
20686             }
20687             this.fireEvent("rowselect", this, index, r);
20688             this.fireEvent("selectionchange", this);
20689         }
20690     },
20691
20692     /**
20693      * Deselects a row.
20694      * @param {Number} row The index of the row to deselect
20695      */
20696     deselectRow : function(index, preventViewNotify){
20697         if(this.locked) return;
20698         if(this.last == index){
20699             this.last = false;
20700         }
20701         if(this.lastActive == index){
20702             this.lastActive = false;
20703         }
20704         var r = this.grid.dataSource.getAt(index);
20705         this.selections.remove(r);
20706         if(!preventViewNotify){
20707             this.grid.getView().onRowDeselect(index);
20708         }
20709         this.fireEvent("rowdeselect", this, index);
20710         this.fireEvent("selectionchange", this);
20711     },
20712
20713     // private
20714     restoreLast : function(){
20715         if(this._last){
20716             this.last = this._last;
20717         }
20718     },
20719
20720     // private
20721     acceptsNav : function(row, col, cm){
20722         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20723     },
20724
20725     // private
20726     onEditorKey : function(field, e){
20727         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20728         if(k == e.TAB){
20729             e.stopEvent();
20730             ed.completeEdit();
20731             if(e.shiftKey){
20732                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20733             }else{
20734                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20735             }
20736         }else if(k == e.ENTER && !e.ctrlKey){
20737             e.stopEvent();
20738             ed.completeEdit();
20739             if(e.shiftKey){
20740                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20741             }else{
20742                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20743             }
20744         }else if(k == e.ESC){
20745             ed.cancelEdit();
20746         }
20747         if(newCell){
20748             g.startEditing(newCell[0], newCell[1]);
20749         }
20750     }
20751 });/*
20752  * Based on:
20753  * Ext JS Library 1.1.1
20754  * Copyright(c) 2006-2007, Ext JS, LLC.
20755  *
20756  * Originally Released Under LGPL - original licence link has changed is not relivant.
20757  *
20758  * Fork - LGPL
20759  * <script type="text/javascript">
20760  */
20761  
20762 /**
20763  * @class Roo.bootstrap.PagingToolbar
20764  * @extends Roo.Row
20765  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20766  * @constructor
20767  * Create a new PagingToolbar
20768  * @param {Object} config The config object
20769  */
20770 Roo.bootstrap.PagingToolbar = function(config)
20771 {
20772     // old args format still supported... - xtype is prefered..
20773         // created from xtype...
20774     var ds = config.dataSource;
20775     this.toolbarItems = [];
20776     if (config.items) {
20777         this.toolbarItems = config.items;
20778 //        config.items = [];
20779     }
20780     
20781     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20782     this.ds = ds;
20783     this.cursor = 0;
20784     if (ds) { 
20785         this.bind(ds);
20786     }
20787     
20788     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20789     
20790 };
20791
20792 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20793     /**
20794      * @cfg {Roo.data.Store} dataSource
20795      * The underlying data store providing the paged data
20796      */
20797     /**
20798      * @cfg {String/HTMLElement/Element} container
20799      * container The id or element that will contain the toolbar
20800      */
20801     /**
20802      * @cfg {Boolean} displayInfo
20803      * True to display the displayMsg (defaults to false)
20804      */
20805     /**
20806      * @cfg {Number} pageSize
20807      * The number of records to display per page (defaults to 20)
20808      */
20809     pageSize: 20,
20810     /**
20811      * @cfg {String} displayMsg
20812      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20813      */
20814     displayMsg : 'Displaying {0} - {1} of {2}',
20815     /**
20816      * @cfg {String} emptyMsg
20817      * The message to display when no records are found (defaults to "No data to display")
20818      */
20819     emptyMsg : 'No data to display',
20820     /**
20821      * Customizable piece of the default paging text (defaults to "Page")
20822      * @type String
20823      */
20824     beforePageText : "Page",
20825     /**
20826      * Customizable piece of the default paging text (defaults to "of %0")
20827      * @type String
20828      */
20829     afterPageText : "of {0}",
20830     /**
20831      * Customizable piece of the default paging text (defaults to "First Page")
20832      * @type String
20833      */
20834     firstText : "First Page",
20835     /**
20836      * Customizable piece of the default paging text (defaults to "Previous Page")
20837      * @type String
20838      */
20839     prevText : "Previous Page",
20840     /**
20841      * Customizable piece of the default paging text (defaults to "Next Page")
20842      * @type String
20843      */
20844     nextText : "Next Page",
20845     /**
20846      * Customizable piece of the default paging text (defaults to "Last Page")
20847      * @type String
20848      */
20849     lastText : "Last Page",
20850     /**
20851      * Customizable piece of the default paging text (defaults to "Refresh")
20852      * @type String
20853      */
20854     refreshText : "Refresh",
20855
20856     buttons : false,
20857     // private
20858     onRender : function(ct, position) 
20859     {
20860         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20861         this.navgroup.parentId = this.id;
20862         this.navgroup.onRender(this.el, null);
20863         // add the buttons to the navgroup
20864         
20865         if(this.displayInfo){
20866             Roo.log(this.el.select('ul.navbar-nav',true).first());
20867             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20868             this.displayEl = this.el.select('.x-paging-info', true).first();
20869 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20870 //            this.displayEl = navel.el.select('span',true).first();
20871         }
20872         
20873         var _this = this;
20874         
20875         if(this.buttons){
20876             Roo.each(_this.buttons, function(e){
20877                Roo.factory(e).onRender(_this.el, null);
20878             });
20879         }
20880             
20881         Roo.each(_this.toolbarItems, function(e) {
20882             _this.navgroup.addItem(e);
20883         });
20884         
20885         
20886         this.first = this.navgroup.addItem({
20887             tooltip: this.firstText,
20888             cls: "prev",
20889             icon : 'fa fa-backward',
20890             disabled: true,
20891             preventDefault: true,
20892             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20893         });
20894         
20895         this.prev =  this.navgroup.addItem({
20896             tooltip: this.prevText,
20897             cls: "prev",
20898             icon : 'fa fa-step-backward',
20899             disabled: true,
20900             preventDefault: true,
20901             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20902         });
20903     //this.addSeparator();
20904         
20905         
20906         var field = this.navgroup.addItem( {
20907             tagtype : 'span',
20908             cls : 'x-paging-position',
20909             
20910             html : this.beforePageText  +
20911                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20912                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20913          } ); //?? escaped?
20914         
20915         this.field = field.el.select('input', true).first();
20916         this.field.on("keydown", this.onPagingKeydown, this);
20917         this.field.on("focus", function(){this.dom.select();});
20918     
20919     
20920         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20921         //this.field.setHeight(18);
20922         //this.addSeparator();
20923         this.next = this.navgroup.addItem({
20924             tooltip: this.nextText,
20925             cls: "next",
20926             html : ' <i class="fa fa-step-forward">',
20927             disabled: true,
20928             preventDefault: true,
20929             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20930         });
20931         this.last = this.navgroup.addItem({
20932             tooltip: this.lastText,
20933             icon : 'fa fa-forward',
20934             cls: "next",
20935             disabled: true,
20936             preventDefault: true,
20937             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20938         });
20939     //this.addSeparator();
20940         this.loading = this.navgroup.addItem({
20941             tooltip: this.refreshText,
20942             icon: 'fa fa-refresh',
20943             preventDefault: true,
20944             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20945         });
20946
20947     },
20948
20949     // private
20950     updateInfo : function(){
20951         if(this.displayEl){
20952             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20953             var msg = count == 0 ?
20954                 this.emptyMsg :
20955                 String.format(
20956                     this.displayMsg,
20957                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20958                 );
20959             this.displayEl.update(msg);
20960         }
20961     },
20962
20963     // private
20964     onLoad : function(ds, r, o){
20965        this.cursor = o.params ? o.params.start : 0;
20966        var d = this.getPageData(),
20967             ap = d.activePage,
20968             ps = d.pages;
20969         
20970        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20971        this.field.dom.value = ap;
20972        this.first.setDisabled(ap == 1);
20973        this.prev.setDisabled(ap == 1);
20974        this.next.setDisabled(ap == ps);
20975        this.last.setDisabled(ap == ps);
20976        this.loading.enable();
20977        this.updateInfo();
20978     },
20979
20980     // private
20981     getPageData : function(){
20982         var total = this.ds.getTotalCount();
20983         return {
20984             total : total,
20985             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20986             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20987         };
20988     },
20989
20990     // private
20991     onLoadError : function(){
20992         this.loading.enable();
20993     },
20994
20995     // private
20996     onPagingKeydown : function(e){
20997         var k = e.getKey();
20998         var d = this.getPageData();
20999         if(k == e.RETURN){
21000             var v = this.field.dom.value, pageNum;
21001             if(!v || isNaN(pageNum = parseInt(v, 10))){
21002                 this.field.dom.value = d.activePage;
21003                 return;
21004             }
21005             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21006             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21007             e.stopEvent();
21008         }
21009         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))
21010         {
21011           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21012           this.field.dom.value = pageNum;
21013           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21014           e.stopEvent();
21015         }
21016         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21017         {
21018           var v = this.field.dom.value, pageNum; 
21019           var increment = (e.shiftKey) ? 10 : 1;
21020           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21021             increment *= -1;
21022           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21023             this.field.dom.value = d.activePage;
21024             return;
21025           }
21026           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21027           {
21028             this.field.dom.value = parseInt(v, 10) + increment;
21029             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21030             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21031           }
21032           e.stopEvent();
21033         }
21034     },
21035
21036     // private
21037     beforeLoad : function(){
21038         if(this.loading){
21039             this.loading.disable();
21040         }
21041     },
21042
21043     // private
21044     onClick : function(which){
21045         
21046         var ds = this.ds;
21047         if (!ds) {
21048             return;
21049         }
21050         
21051         switch(which){
21052             case "first":
21053                 ds.load({params:{start: 0, limit: this.pageSize}});
21054             break;
21055             case "prev":
21056                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21057             break;
21058             case "next":
21059                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21060             break;
21061             case "last":
21062                 var total = ds.getTotalCount();
21063                 var extra = total % this.pageSize;
21064                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21065                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21066             break;
21067             case "refresh":
21068                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21069             break;
21070         }
21071     },
21072
21073     /**
21074      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21075      * @param {Roo.data.Store} store The data store to unbind
21076      */
21077     unbind : function(ds){
21078         ds.un("beforeload", this.beforeLoad, this);
21079         ds.un("load", this.onLoad, this);
21080         ds.un("loadexception", this.onLoadError, this);
21081         ds.un("remove", this.updateInfo, this);
21082         ds.un("add", this.updateInfo, this);
21083         this.ds = undefined;
21084     },
21085
21086     /**
21087      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21088      * @param {Roo.data.Store} store The data store to bind
21089      */
21090     bind : function(ds){
21091         ds.on("beforeload", this.beforeLoad, this);
21092         ds.on("load", this.onLoad, this);
21093         ds.on("loadexception", this.onLoadError, this);
21094         ds.on("remove", this.updateInfo, this);
21095         ds.on("add", this.updateInfo, this);
21096         this.ds = ds;
21097     }
21098 });/*
21099  * - LGPL
21100  *
21101  * element
21102  * 
21103  */
21104
21105 /**
21106  * @class Roo.bootstrap.MessageBar
21107  * @extends Roo.bootstrap.Component
21108  * Bootstrap MessageBar class
21109  * @cfg {String} html contents of the MessageBar
21110  * @cfg {String} weight (info | success | warning | danger) default info
21111  * @cfg {String} beforeClass insert the bar before the given class
21112  * @cfg {Boolean} closable (true | false) default false
21113  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21114  * 
21115  * @constructor
21116  * Create a new Element
21117  * @param {Object} config The config object
21118  */
21119
21120 Roo.bootstrap.MessageBar = function(config){
21121     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21122 };
21123
21124 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21125     
21126     html: '',
21127     weight: 'info',
21128     closable: false,
21129     fixed: false,
21130     beforeClass: 'bootstrap-sticky-wrap',
21131     
21132     getAutoCreate : function(){
21133         
21134         var cfg = {
21135             tag: 'div',
21136             cls: 'alert alert-dismissable alert-' + this.weight,
21137             cn: [
21138                 {
21139                     tag: 'span',
21140                     cls: 'message',
21141                     html: this.html || ''
21142                 }
21143             ]
21144         }
21145         
21146         if(this.fixed){
21147             cfg.cls += ' alert-messages-fixed';
21148         }
21149         
21150         if(this.closable){
21151             cfg.cn.push({
21152                 tag: 'button',
21153                 cls: 'close',
21154                 html: 'x'
21155             });
21156         }
21157         
21158         return cfg;
21159     },
21160     
21161     onRender : function(ct, position)
21162     {
21163         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21164         
21165         if(!this.el){
21166             var cfg = Roo.apply({},  this.getAutoCreate());
21167             cfg.id = Roo.id();
21168             
21169             if (this.cls) {
21170                 cfg.cls += ' ' + this.cls;
21171             }
21172             if (this.style) {
21173                 cfg.style = this.style;
21174             }
21175             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21176             
21177             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21178         }
21179         
21180         this.el.select('>button.close').on('click', this.hide, this);
21181         
21182     },
21183     
21184     show : function()
21185     {
21186         if (!this.rendered) {
21187             this.render();
21188         }
21189         
21190         this.el.show();
21191         
21192         this.fireEvent('show', this);
21193         
21194     },
21195     
21196     hide : function()
21197     {
21198         if (!this.rendered) {
21199             this.render();
21200         }
21201         
21202         this.el.hide();
21203         
21204         this.fireEvent('hide', this);
21205     },
21206     
21207     update : function()
21208     {
21209 //        var e = this.el.dom.firstChild;
21210 //        
21211 //        if(this.closable){
21212 //            e = e.nextSibling;
21213 //        }
21214 //        
21215 //        e.data = this.html || '';
21216
21217         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21218     }
21219    
21220 });
21221
21222  
21223
21224      /*
21225  * - LGPL
21226  *
21227  * Graph
21228  * 
21229  */
21230
21231
21232 /**
21233  * @class Roo.bootstrap.Graph
21234  * @extends Roo.bootstrap.Component
21235  * Bootstrap Graph class
21236 > Prameters
21237  -sm {number} sm 4
21238  -md {number} md 5
21239  @cfg {String} graphtype  bar | vbar | pie
21240  @cfg {number} g_x coodinator | centre x (pie)
21241  @cfg {number} g_y coodinator | centre y (pie)
21242  @cfg {number} g_r radius (pie)
21243  @cfg {number} g_height height of the chart (respected by all elements in the set)
21244  @cfg {number} g_width width of the chart (respected by all elements in the set)
21245  @cfg {Object} title The title of the chart
21246     
21247  -{Array}  values
21248  -opts (object) options for the chart 
21249      o {
21250      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21251      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21252      o vgutter (number)
21253      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.
21254      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21255      o to
21256      o stretch (boolean)
21257      o }
21258  -opts (object) options for the pie
21259      o{
21260      o cut
21261      o startAngle (number)
21262      o endAngle (number)
21263      } 
21264  *
21265  * @constructor
21266  * Create a new Input
21267  * @param {Object} config The config object
21268  */
21269
21270 Roo.bootstrap.Graph = function(config){
21271     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21272     
21273     this.addEvents({
21274         // img events
21275         /**
21276          * @event click
21277          * The img click event for the img.
21278          * @param {Roo.EventObject} e
21279          */
21280         "click" : true
21281     });
21282 };
21283
21284 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21285     
21286     sm: 4,
21287     md: 5,
21288     graphtype: 'bar',
21289     g_height: 250,
21290     g_width: 400,
21291     g_x: 50,
21292     g_y: 50,
21293     g_r: 30,
21294     opts:{
21295         //g_colors: this.colors,
21296         g_type: 'soft',
21297         g_gutter: '20%'
21298
21299     },
21300     title : false,
21301
21302     getAutoCreate : function(){
21303         
21304         var cfg = {
21305             tag: 'div',
21306             html : null
21307         }
21308         
21309         
21310         return  cfg;
21311     },
21312
21313     onRender : function(ct,position){
21314         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21315         this.raphael = Raphael(this.el.dom);
21316         
21317                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21318                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21319                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21320                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21321                 /*
21322                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21323                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21324                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21325                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21326                 
21327                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21328                 r.barchart(330, 10, 300, 220, data1);
21329                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21330                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21331                 */
21332                 
21333                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21334                 // r.barchart(30, 30, 560, 250,  xdata, {
21335                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21336                 //     axis : "0 0 1 1",
21337                 //     axisxlabels :  xdata
21338                 //     //yvalues : cols,
21339                    
21340                 // });
21341 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21342 //        
21343 //        this.load(null,xdata,{
21344 //                axis : "0 0 1 1",
21345 //                axisxlabels :  xdata
21346 //                });
21347
21348     },
21349
21350     load : function(graphtype,xdata,opts){
21351         this.raphael.clear();
21352         if(!graphtype) {
21353             graphtype = this.graphtype;
21354         }
21355         if(!opts){
21356             opts = this.opts;
21357         }
21358         var r = this.raphael,
21359             fin = function () {
21360                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21361             },
21362             fout = function () {
21363                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21364             },
21365             pfin = function() {
21366                 this.sector.stop();
21367                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21368
21369                 if (this.label) {
21370                     this.label[0].stop();
21371                     this.label[0].attr({ r: 7.5 });
21372                     this.label[1].attr({ "font-weight": 800 });
21373                 }
21374             },
21375             pfout = function() {
21376                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21377
21378                 if (this.label) {
21379                     this.label[0].animate({ r: 5 }, 500, "bounce");
21380                     this.label[1].attr({ "font-weight": 400 });
21381                 }
21382             };
21383
21384         switch(graphtype){
21385             case 'bar':
21386                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21387                 break;
21388             case 'hbar':
21389                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21390                 break;
21391             case 'pie':
21392 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21393 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21394 //            
21395                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21396                 
21397                 break;
21398
21399         }
21400         
21401         if(this.title){
21402             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21403         }
21404         
21405     },
21406     
21407     setTitle: function(o)
21408     {
21409         this.title = o;
21410     },
21411     
21412     initEvents: function() {
21413         
21414         if(!this.href){
21415             this.el.on('click', this.onClick, this);
21416         }
21417     },
21418     
21419     onClick : function(e)
21420     {
21421         Roo.log('img onclick');
21422         this.fireEvent('click', this, e);
21423     }
21424    
21425 });
21426
21427  
21428 /*
21429  * - LGPL
21430  *
21431  * numberBox
21432  * 
21433  */
21434 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21435
21436 /**
21437  * @class Roo.bootstrap.dash.NumberBox
21438  * @extends Roo.bootstrap.Component
21439  * Bootstrap NumberBox class
21440  * @cfg {String} headline Box headline
21441  * @cfg {String} content Box content
21442  * @cfg {String} icon Box icon
21443  * @cfg {String} footer Footer text
21444  * @cfg {String} fhref Footer href
21445  * 
21446  * @constructor
21447  * Create a new NumberBox
21448  * @param {Object} config The config object
21449  */
21450
21451
21452 Roo.bootstrap.dash.NumberBox = function(config){
21453     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21454     
21455 };
21456
21457 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21458     
21459     headline : '',
21460     content : '',
21461     icon : '',
21462     footer : '',
21463     fhref : '',
21464     ficon : '',
21465     
21466     getAutoCreate : function(){
21467         
21468         var cfg = {
21469             tag : 'div',
21470             cls : 'small-box ',
21471             cn : [
21472                 {
21473                     tag : 'div',
21474                     cls : 'inner',
21475                     cn :[
21476                         {
21477                             tag : 'h3',
21478                             cls : 'roo-headline',
21479                             html : this.headline
21480                         },
21481                         {
21482                             tag : 'p',
21483                             cls : 'roo-content',
21484                             html : this.content
21485                         }
21486                     ]
21487                 }
21488             ]
21489         }
21490         
21491         if(this.icon){
21492             cfg.cn.push({
21493                 tag : 'div',
21494                 cls : 'icon',
21495                 cn :[
21496                     {
21497                         tag : 'i',
21498                         cls : 'ion ' + this.icon
21499                     }
21500                 ]
21501             });
21502         }
21503         
21504         if(this.footer){
21505             var footer = {
21506                 tag : 'a',
21507                 cls : 'small-box-footer',
21508                 href : this.fhref || '#',
21509                 html : this.footer
21510             };
21511             
21512             cfg.cn.push(footer);
21513             
21514         }
21515         
21516         return  cfg;
21517     },
21518
21519     onRender : function(ct,position){
21520         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21521
21522
21523        
21524                 
21525     },
21526
21527     setHeadline: function (value)
21528     {
21529         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21530     },
21531     
21532     setFooter: function (value, href)
21533     {
21534         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21535         
21536         if(href){
21537             this.el.select('a.small-box-footer',true).first().attr('href', href);
21538         }
21539         
21540     },
21541
21542     setContent: function (value)
21543     {
21544         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21545     },
21546
21547     initEvents: function() 
21548     {   
21549         
21550     }
21551     
21552 });
21553
21554  
21555 /*
21556  * - LGPL
21557  *
21558  * TabBox
21559  * 
21560  */
21561 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21562
21563 /**
21564  * @class Roo.bootstrap.dash.TabBox
21565  * @extends Roo.bootstrap.Component
21566  * Bootstrap TabBox class
21567  * @cfg {String} title Title of the TabBox
21568  * @cfg {String} icon Icon of the TabBox
21569  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21570  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21571  * 
21572  * @constructor
21573  * Create a new TabBox
21574  * @param {Object} config The config object
21575  */
21576
21577
21578 Roo.bootstrap.dash.TabBox = function(config){
21579     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21580     this.addEvents({
21581         // raw events
21582         /**
21583          * @event addpane
21584          * When a pane is added
21585          * @param {Roo.bootstrap.dash.TabPane} pane
21586          */
21587         "addpane" : true,
21588         /**
21589          * @event activatepane
21590          * When a pane is activated
21591          * @param {Roo.bootstrap.dash.TabPane} pane
21592          */
21593         "activatepane" : true
21594         
21595          
21596     });
21597     
21598     this.panes = [];
21599 };
21600
21601 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21602
21603     title : '',
21604     icon : false,
21605     showtabs : true,
21606     tabScrollable : false,
21607     
21608     getChildContainer : function()
21609     {
21610         return this.el.select('.tab-content', true).first();
21611     },
21612     
21613     getAutoCreate : function(){
21614         
21615         var header = {
21616             tag: 'li',
21617             cls: 'pull-left header',
21618             html: this.title,
21619             cn : []
21620         };
21621         
21622         if(this.icon){
21623             header.cn.push({
21624                 tag: 'i',
21625                 cls: 'fa ' + this.icon
21626             });
21627         }
21628         
21629         var h = {
21630             tag: 'ul',
21631             cls: 'nav nav-tabs pull-right',
21632             cn: [
21633                 header
21634             ]
21635         };
21636         
21637         if(this.tabScrollable){
21638             h = {
21639                 tag: 'div',
21640                 cls: 'tab-header',
21641                 cn: [
21642                     {
21643                         tag: 'ul',
21644                         cls: 'nav nav-tabs pull-right',
21645                         cn: [
21646                             header
21647                         ]
21648                     }
21649                 ]
21650             }
21651         }
21652         
21653         var cfg = {
21654             tag: 'div',
21655             cls: 'nav-tabs-custom',
21656             cn: [
21657                 h,
21658                 {
21659                     tag: 'div',
21660                     cls: 'tab-content no-padding',
21661                     cn: []
21662                 }
21663             ]
21664         }
21665
21666         return  cfg;
21667     },
21668     initEvents : function()
21669     {
21670         //Roo.log('add add pane handler');
21671         this.on('addpane', this.onAddPane, this);
21672     },
21673      /**
21674      * Updates the box title
21675      * @param {String} html to set the title to.
21676      */
21677     setTitle : function(value)
21678     {
21679         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21680     },
21681     onAddPane : function(pane)
21682     {
21683         this.panes.push(pane);
21684         //Roo.log('addpane');
21685         //Roo.log(pane);
21686         // tabs are rendere left to right..
21687         if(!this.showtabs){
21688             return;
21689         }
21690         
21691         var ctr = this.el.select('.nav-tabs', true).first();
21692          
21693          
21694         var existing = ctr.select('.nav-tab',true);
21695         var qty = existing.getCount();;
21696         
21697         
21698         var tab = ctr.createChild({
21699             tag : 'li',
21700             cls : 'nav-tab' + (qty ? '' : ' active'),
21701             cn : [
21702                 {
21703                     tag : 'a',
21704                     href:'#',
21705                     html : pane.title
21706                 }
21707             ]
21708         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21709         pane.tab = tab;
21710         
21711         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21712         if (!qty) {
21713             pane.el.addClass('active');
21714         }
21715         
21716                 
21717     },
21718     onTabClick : function(ev,un,ob,pane)
21719     {
21720         //Roo.log('tab - prev default');
21721         ev.preventDefault();
21722         
21723         
21724         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21725         pane.tab.addClass('active');
21726         //Roo.log(pane.title);
21727         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21728         // technically we should have a deactivate event.. but maybe add later.
21729         // and it should not de-activate the selected tab...
21730         this.fireEvent('activatepane', pane);
21731         pane.el.addClass('active');
21732         pane.fireEvent('activate');
21733         
21734         
21735     },
21736     
21737     getActivePane : function()
21738     {
21739         var r = false;
21740         Roo.each(this.panes, function(p) {
21741             if(p.el.hasClass('active')){
21742                 r = p;
21743                 return false;
21744             }
21745             
21746             return;
21747         });
21748         
21749         return r;
21750     }
21751     
21752     
21753 });
21754
21755  
21756 /*
21757  * - LGPL
21758  *
21759  * Tab pane
21760  * 
21761  */
21762 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21763 /**
21764  * @class Roo.bootstrap.TabPane
21765  * @extends Roo.bootstrap.Component
21766  * Bootstrap TabPane class
21767  * @cfg {Boolean} active (false | true) Default false
21768  * @cfg {String} title title of panel
21769
21770  * 
21771  * @constructor
21772  * Create a new TabPane
21773  * @param {Object} config The config object
21774  */
21775
21776 Roo.bootstrap.dash.TabPane = function(config){
21777     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21778     
21779     this.addEvents({
21780         // raw events
21781         /**
21782          * @event activate
21783          * When a pane is activated
21784          * @param {Roo.bootstrap.dash.TabPane} pane
21785          */
21786         "activate" : true
21787          
21788     });
21789 };
21790
21791 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21792     
21793     active : false,
21794     title : '',
21795     
21796     // the tabBox that this is attached to.
21797     tab : false,
21798      
21799     getAutoCreate : function() 
21800     {
21801         var cfg = {
21802             tag: 'div',
21803             cls: 'tab-pane'
21804         }
21805         
21806         if(this.active){
21807             cfg.cls += ' active';
21808         }
21809         
21810         return cfg;
21811     },
21812     initEvents  : function()
21813     {
21814         //Roo.log('trigger add pane handler');
21815         this.parent().fireEvent('addpane', this)
21816     },
21817     
21818      /**
21819      * Updates the tab title 
21820      * @param {String} html to set the title to.
21821      */
21822     setTitle: function(str)
21823     {
21824         if (!this.tab) {
21825             return;
21826         }
21827         this.title = str;
21828         this.tab.select('a', true).first().dom.innerHTML = str;
21829         
21830     }
21831     
21832     
21833     
21834 });
21835
21836  
21837
21838
21839  /*
21840  * - LGPL
21841  *
21842  * menu
21843  * 
21844  */
21845 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21846
21847 /**
21848  * @class Roo.bootstrap.menu.Menu
21849  * @extends Roo.bootstrap.Component
21850  * Bootstrap Menu class - container for Menu
21851  * @cfg {String} html Text of the menu
21852  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21853  * @cfg {String} icon Font awesome icon
21854  * @cfg {String} pos Menu align to (top | bottom) default bottom
21855  * 
21856  * 
21857  * @constructor
21858  * Create a new Menu
21859  * @param {Object} config The config object
21860  */
21861
21862
21863 Roo.bootstrap.menu.Menu = function(config){
21864     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21865     
21866     this.addEvents({
21867         /**
21868          * @event beforeshow
21869          * Fires before this menu is displayed
21870          * @param {Roo.bootstrap.menu.Menu} this
21871          */
21872         beforeshow : true,
21873         /**
21874          * @event beforehide
21875          * Fires before this menu is hidden
21876          * @param {Roo.bootstrap.menu.Menu} this
21877          */
21878         beforehide : true,
21879         /**
21880          * @event show
21881          * Fires after this menu is displayed
21882          * @param {Roo.bootstrap.menu.Menu} this
21883          */
21884         show : true,
21885         /**
21886          * @event hide
21887          * Fires after this menu is hidden
21888          * @param {Roo.bootstrap.menu.Menu} this
21889          */
21890         hide : true,
21891         /**
21892          * @event click
21893          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21894          * @param {Roo.bootstrap.menu.Menu} this
21895          * @param {Roo.EventObject} e
21896          */
21897         click : true
21898     });
21899     
21900 };
21901
21902 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21903     
21904     submenu : false,
21905     html : '',
21906     weight : 'default',
21907     icon : false,
21908     pos : 'bottom',
21909     
21910     
21911     getChildContainer : function() {
21912         if(this.isSubMenu){
21913             return this.el;
21914         }
21915         
21916         return this.el.select('ul.dropdown-menu', true).first();  
21917     },
21918     
21919     getAutoCreate : function()
21920     {
21921         var text = [
21922             {
21923                 tag : 'span',
21924                 cls : 'roo-menu-text',
21925                 html : this.html
21926             }
21927         ];
21928         
21929         if(this.icon){
21930             text.unshift({
21931                 tag : 'i',
21932                 cls : 'fa ' + this.icon
21933             })
21934         }
21935         
21936         
21937         var cfg = {
21938             tag : 'div',
21939             cls : 'btn-group',
21940             cn : [
21941                 {
21942                     tag : 'button',
21943                     cls : 'dropdown-button btn btn-' + this.weight,
21944                     cn : text
21945                 },
21946                 {
21947                     tag : 'button',
21948                     cls : 'dropdown-toggle btn btn-' + this.weight,
21949                     cn : [
21950                         {
21951                             tag : 'span',
21952                             cls : 'caret'
21953                         }
21954                     ]
21955                 },
21956                 {
21957                     tag : 'ul',
21958                     cls : 'dropdown-menu'
21959                 }
21960             ]
21961             
21962         };
21963         
21964         if(this.pos == 'top'){
21965             cfg.cls += ' dropup';
21966         }
21967         
21968         if(this.isSubMenu){
21969             cfg = {
21970                 tag : 'ul',
21971                 cls : 'dropdown-menu'
21972             }
21973         }
21974         
21975         return cfg;
21976     },
21977     
21978     onRender : function(ct, position)
21979     {
21980         this.isSubMenu = ct.hasClass('dropdown-submenu');
21981         
21982         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21983     },
21984     
21985     initEvents : function() 
21986     {
21987         if(this.isSubMenu){
21988             return;
21989         }
21990         
21991         this.hidden = true;
21992         
21993         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21994         this.triggerEl.on('click', this.onTriggerPress, this);
21995         
21996         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21997         this.buttonEl.on('click', this.onClick, this);
21998         
21999     },
22000     
22001     list : function()
22002     {
22003         if(this.isSubMenu){
22004             return this.el;
22005         }
22006         
22007         return this.el.select('ul.dropdown-menu', true).first();
22008     },
22009     
22010     onClick : function(e)
22011     {
22012         this.fireEvent("click", this, e);
22013     },
22014     
22015     onTriggerPress  : function(e)
22016     {   
22017         if (this.isVisible()) {
22018             this.hide();
22019         } else {
22020             this.show();
22021         }
22022     },
22023     
22024     isVisible : function(){
22025         return !this.hidden;
22026     },
22027     
22028     show : function()
22029     {
22030         this.fireEvent("beforeshow", this);
22031         
22032         this.hidden = false;
22033         this.el.addClass('open');
22034         
22035         Roo.get(document).on("mouseup", this.onMouseUp, this);
22036         
22037         this.fireEvent("show", this);
22038         
22039         
22040     },
22041     
22042     hide : function()
22043     {
22044         this.fireEvent("beforehide", this);
22045         
22046         this.hidden = true;
22047         this.el.removeClass('open');
22048         
22049         Roo.get(document).un("mouseup", this.onMouseUp);
22050         
22051         this.fireEvent("hide", this);
22052     },
22053     
22054     onMouseUp : function()
22055     {
22056         this.hide();
22057     }
22058     
22059 });
22060
22061  
22062  /*
22063  * - LGPL
22064  *
22065  * menu item
22066  * 
22067  */
22068 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22069
22070 /**
22071  * @class Roo.bootstrap.menu.Item
22072  * @extends Roo.bootstrap.Component
22073  * Bootstrap MenuItem class
22074  * @cfg {Boolean} submenu (true | false) default false
22075  * @cfg {String} html text of the item
22076  * @cfg {String} href the link
22077  * @cfg {Boolean} disable (true | false) default false
22078  * @cfg {Boolean} preventDefault (true | false) default true
22079  * @cfg {String} icon Font awesome icon
22080  * @cfg {String} pos Submenu align to (left | right) default right 
22081  * 
22082  * 
22083  * @constructor
22084  * Create a new Item
22085  * @param {Object} config The config object
22086  */
22087
22088
22089 Roo.bootstrap.menu.Item = function(config){
22090     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22091     this.addEvents({
22092         /**
22093          * @event mouseover
22094          * Fires when the mouse is hovering over this menu
22095          * @param {Roo.bootstrap.menu.Item} this
22096          * @param {Roo.EventObject} e
22097          */
22098         mouseover : true,
22099         /**
22100          * @event mouseout
22101          * Fires when the mouse exits this menu
22102          * @param {Roo.bootstrap.menu.Item} this
22103          * @param {Roo.EventObject} e
22104          */
22105         mouseout : true,
22106         // raw events
22107         /**
22108          * @event click
22109          * The raw click event for the entire grid.
22110          * @param {Roo.EventObject} e
22111          */
22112         click : true
22113     });
22114 };
22115
22116 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22117     
22118     submenu : false,
22119     href : '',
22120     html : '',
22121     preventDefault: true,
22122     disable : false,
22123     icon : false,
22124     pos : 'right',
22125     
22126     getAutoCreate : function()
22127     {
22128         var text = [
22129             {
22130                 tag : 'span',
22131                 cls : 'roo-menu-item-text',
22132                 html : this.html
22133             }
22134         ];
22135         
22136         if(this.icon){
22137             text.unshift({
22138                 tag : 'i',
22139                 cls : 'fa ' + this.icon
22140             })
22141         }
22142         
22143         var cfg = {
22144             tag : 'li',
22145             cn : [
22146                 {
22147                     tag : 'a',
22148                     href : this.href || '#',
22149                     cn : text
22150                 }
22151             ]
22152         };
22153         
22154         if(this.disable){
22155             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22156         }
22157         
22158         if(this.submenu){
22159             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22160             
22161             if(this.pos == 'left'){
22162                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22163             }
22164         }
22165         
22166         return cfg;
22167     },
22168     
22169     initEvents : function() 
22170     {
22171         this.el.on('mouseover', this.onMouseOver, this);
22172         this.el.on('mouseout', this.onMouseOut, this);
22173         
22174         this.el.select('a', true).first().on('click', this.onClick, this);
22175         
22176     },
22177     
22178     onClick : function(e)
22179     {
22180         if(this.preventDefault){
22181             e.preventDefault();
22182         }
22183         
22184         this.fireEvent("click", this, e);
22185     },
22186     
22187     onMouseOver : function(e)
22188     {
22189         if(this.submenu && this.pos == 'left'){
22190             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22191         }
22192         
22193         this.fireEvent("mouseover", this, e);
22194     },
22195     
22196     onMouseOut : function(e)
22197     {
22198         this.fireEvent("mouseout", this, e);
22199     }
22200 });
22201
22202  
22203
22204  /*
22205  * - LGPL
22206  *
22207  * menu separator
22208  * 
22209  */
22210 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22211
22212 /**
22213  * @class Roo.bootstrap.menu.Separator
22214  * @extends Roo.bootstrap.Component
22215  * Bootstrap Separator class
22216  * 
22217  * @constructor
22218  * Create a new Separator
22219  * @param {Object} config The config object
22220  */
22221
22222
22223 Roo.bootstrap.menu.Separator = function(config){
22224     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22225 };
22226
22227 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22228     
22229     getAutoCreate : function(){
22230         var cfg = {
22231             tag : 'li',
22232             cls: 'divider'
22233         };
22234         
22235         return cfg;
22236     }
22237    
22238 });
22239
22240  
22241
22242  /*
22243  * - LGPL
22244  *
22245  * Tooltip
22246  * 
22247  */
22248
22249 /**
22250  * @class Roo.bootstrap.Tooltip
22251  * Bootstrap Tooltip class
22252  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22253  * to determine which dom element triggers the tooltip.
22254  * 
22255  * It needs to add support for additional attributes like tooltip-position
22256  * 
22257  * @constructor
22258  * Create a new Toolti
22259  * @param {Object} config The config object
22260  */
22261
22262 Roo.bootstrap.Tooltip = function(config){
22263     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22264 };
22265
22266 Roo.apply(Roo.bootstrap.Tooltip, {
22267     /**
22268      * @function init initialize tooltip monitoring.
22269      * @static
22270      */
22271     currentEl : false,
22272     currentTip : false,
22273     currentRegion : false,
22274     
22275     //  init : delay?
22276     
22277     init : function()
22278     {
22279         Roo.get(document).on('mouseover', this.enter ,this);
22280         Roo.get(document).on('mouseout', this.leave, this);
22281          
22282         
22283         this.currentTip = new Roo.bootstrap.Tooltip();
22284     },
22285     
22286     enter : function(ev)
22287     {
22288         var dom = ev.getTarget();
22289         
22290         //Roo.log(['enter',dom]);
22291         var el = Roo.fly(dom);
22292         if (this.currentEl) {
22293             //Roo.log(dom);
22294             //Roo.log(this.currentEl);
22295             //Roo.log(this.currentEl.contains(dom));
22296             if (this.currentEl == el) {
22297                 return;
22298             }
22299             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22300                 return;
22301             }
22302
22303         }
22304         
22305         
22306         
22307         if (this.currentTip.el) {
22308             this.currentTip.el.hide(); // force hiding...
22309         }    
22310         //Roo.log(ev);
22311         var bindEl = el;
22312         
22313         // you can not look for children, as if el is the body.. then everythign is the child..
22314         if (!el.attr('tooltip')) { //
22315             if (!el.select("[tooltip]").elements.length) {
22316                 return;
22317             }
22318             // is the mouse over this child...?
22319             bindEl = el.select("[tooltip]").first();
22320             var xy = ev.getXY();
22321             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22322                 //Roo.log("not in region.");
22323                 return;
22324             }
22325             //Roo.log("child element over..");
22326             
22327         }
22328         this.currentEl = bindEl;
22329         this.currentTip.bind(bindEl);
22330         this.currentRegion = Roo.lib.Region.getRegion(dom);
22331         this.currentTip.enter();
22332         
22333     },
22334     leave : function(ev)
22335     {
22336         var dom = ev.getTarget();
22337         //Roo.log(['leave',dom]);
22338         if (!this.currentEl) {
22339             return;
22340         }
22341         
22342         
22343         if (dom != this.currentEl.dom) {
22344             return;
22345         }
22346         var xy = ev.getXY();
22347         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22348             return;
22349         }
22350         // only activate leave if mouse cursor is outside... bounding box..
22351         
22352         
22353         
22354         
22355         if (this.currentTip) {
22356             this.currentTip.leave();
22357         }
22358         //Roo.log('clear currentEl');
22359         this.currentEl = false;
22360         
22361         
22362     },
22363     alignment : {
22364         'left' : ['r-l', [-2,0], 'right'],
22365         'right' : ['l-r', [2,0], 'left'],
22366         'bottom' : ['t-b', [0,2], 'top'],
22367         'top' : [ 'b-t', [0,-2], 'bottom']
22368     }
22369     
22370 });
22371
22372
22373 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22374     
22375     
22376     bindEl : false,
22377     
22378     delay : null, // can be { show : 300 , hide: 500}
22379     
22380     timeout : null,
22381     
22382     hoverState : null, //???
22383     
22384     placement : 'bottom', 
22385     
22386     getAutoCreate : function(){
22387     
22388         var cfg = {
22389            cls : 'tooltip',
22390            role : 'tooltip',
22391            cn : [
22392                 {
22393                     cls : 'tooltip-arrow'
22394                 },
22395                 {
22396                     cls : 'tooltip-inner'
22397                 }
22398            ]
22399         };
22400         
22401         return cfg;
22402     },
22403     bind : function(el)
22404     {
22405         this.bindEl = el;
22406     },
22407       
22408     
22409     enter : function () {
22410        
22411         if (this.timeout != null) {
22412             clearTimeout(this.timeout);
22413         }
22414         
22415         this.hoverState = 'in';
22416          //Roo.log("enter - show");
22417         if (!this.delay || !this.delay.show) {
22418             this.show();
22419             return;
22420         }
22421         var _t = this;
22422         this.timeout = setTimeout(function () {
22423             if (_t.hoverState == 'in') {
22424                 _t.show();
22425             }
22426         }, this.delay.show);
22427     },
22428     leave : function()
22429     {
22430         clearTimeout(this.timeout);
22431     
22432         this.hoverState = 'out';
22433          if (!this.delay || !this.delay.hide) {
22434             this.hide();
22435             return;
22436         }
22437        
22438         var _t = this;
22439         this.timeout = setTimeout(function () {
22440             //Roo.log("leave - timeout");
22441             
22442             if (_t.hoverState == 'out') {
22443                 _t.hide();
22444                 Roo.bootstrap.Tooltip.currentEl = false;
22445             }
22446         }, delay);
22447     },
22448     
22449     show : function ()
22450     {
22451         if (!this.el) {
22452             this.render(document.body);
22453         }
22454         // set content.
22455         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22456         
22457         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22458         
22459         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22460         
22461         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22462         
22463         var placement = typeof this.placement == 'function' ?
22464             this.placement.call(this, this.el, on_el) :
22465             this.placement;
22466             
22467         var autoToken = /\s?auto?\s?/i;
22468         var autoPlace = autoToken.test(placement);
22469         if (autoPlace) {
22470             placement = placement.replace(autoToken, '') || 'top';
22471         }
22472         
22473         //this.el.detach()
22474         //this.el.setXY([0,0]);
22475         this.el.show();
22476         //this.el.dom.style.display='block';
22477         this.el.addClass(placement);
22478         
22479         //this.el.appendTo(on_el);
22480         
22481         var p = this.getPosition();
22482         var box = this.el.getBox();
22483         
22484         if (autoPlace) {
22485             // fixme..
22486         }
22487         var align = Roo.bootstrap.Tooltip.alignment[placement];
22488         this.el.alignTo(this.bindEl, align[0],align[1]);
22489         //var arrow = this.el.select('.arrow',true).first();
22490         //arrow.set(align[2], 
22491         
22492         this.el.addClass('in fade');
22493         this.hoverState = null;
22494         
22495         if (this.el.hasClass('fade')) {
22496             // fade it?
22497         }
22498         
22499     },
22500     hide : function()
22501     {
22502          
22503         if (!this.el) {
22504             return;
22505         }
22506         //this.el.setXY([0,0]);
22507         this.el.removeClass('in');
22508         //this.el.hide();
22509         
22510     }
22511     
22512 });
22513  
22514
22515  /*
22516  * - LGPL
22517  *
22518  * Location Picker
22519  * 
22520  */
22521
22522 /**
22523  * @class Roo.bootstrap.LocationPicker
22524  * @extends Roo.bootstrap.Component
22525  * Bootstrap LocationPicker class
22526  * @cfg {Number} latitude Position when init default 0
22527  * @cfg {Number} longitude Position when init default 0
22528  * @cfg {Number} zoom default 15
22529  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22530  * @cfg {Boolean} mapTypeControl default false
22531  * @cfg {Boolean} disableDoubleClickZoom default false
22532  * @cfg {Boolean} scrollwheel default true
22533  * @cfg {Boolean} streetViewControl default false
22534  * @cfg {Number} radius default 0
22535  * @cfg {String} locationName
22536  * @cfg {Boolean} draggable default true
22537  * @cfg {Boolean} enableAutocomplete default false
22538  * @cfg {Boolean} enableReverseGeocode default true
22539  * @cfg {String} markerTitle
22540  * 
22541  * @constructor
22542  * Create a new LocationPicker
22543  * @param {Object} config The config object
22544  */
22545
22546
22547 Roo.bootstrap.LocationPicker = function(config){
22548     
22549     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22550     
22551     this.addEvents({
22552         /**
22553          * @event initial
22554          * Fires when the picker initialized.
22555          * @param {Roo.bootstrap.LocationPicker} this
22556          * @param {Google Location} location
22557          */
22558         initial : true,
22559         /**
22560          * @event positionchanged
22561          * Fires when the picker position changed.
22562          * @param {Roo.bootstrap.LocationPicker} this
22563          * @param {Google Location} location
22564          */
22565         positionchanged : true,
22566         /**
22567          * @event resize
22568          * Fires when the map resize.
22569          * @param {Roo.bootstrap.LocationPicker} this
22570          */
22571         resize : true,
22572         /**
22573          * @event show
22574          * Fires when the map show.
22575          * @param {Roo.bootstrap.LocationPicker} this
22576          */
22577         show : true,
22578         /**
22579          * @event hide
22580          * Fires when the map hide.
22581          * @param {Roo.bootstrap.LocationPicker} this
22582          */
22583         hide : true,
22584         /**
22585          * @event mapClick
22586          * Fires when click the map.
22587          * @param {Roo.bootstrap.LocationPicker} this
22588          * @param {Map event} e
22589          */
22590         mapClick : true,
22591         /**
22592          * @event mapRightClick
22593          * Fires when right click the map.
22594          * @param {Roo.bootstrap.LocationPicker} this
22595          * @param {Map event} e
22596          */
22597         mapRightClick : true,
22598         /**
22599          * @event markerClick
22600          * Fires when click the marker.
22601          * @param {Roo.bootstrap.LocationPicker} this
22602          * @param {Map event} e
22603          */
22604         markerClick : true,
22605         /**
22606          * @event markerRightClick
22607          * Fires when right click the marker.
22608          * @param {Roo.bootstrap.LocationPicker} this
22609          * @param {Map event} e
22610          */
22611         markerRightClick : true,
22612         /**
22613          * @event OverlayViewDraw
22614          * Fires when OverlayView Draw
22615          * @param {Roo.bootstrap.LocationPicker} this
22616          */
22617         OverlayViewDraw : true,
22618         /**
22619          * @event OverlayViewOnAdd
22620          * Fires when OverlayView Draw
22621          * @param {Roo.bootstrap.LocationPicker} this
22622          */
22623         OverlayViewOnAdd : true,
22624         /**
22625          * @event OverlayViewOnRemove
22626          * Fires when OverlayView Draw
22627          * @param {Roo.bootstrap.LocationPicker} this
22628          */
22629         OverlayViewOnRemove : true,
22630         /**
22631          * @event OverlayViewShow
22632          * Fires when OverlayView Draw
22633          * @param {Roo.bootstrap.LocationPicker} this
22634          * @param {Pixel} cpx
22635          */
22636         OverlayViewShow : true,
22637         /**
22638          * @event OverlayViewHide
22639          * Fires when OverlayView Draw
22640          * @param {Roo.bootstrap.LocationPicker} this
22641          */
22642         OverlayViewHide : true
22643     });
22644         
22645 };
22646
22647 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22648     
22649     gMapContext: false,
22650     
22651     latitude: 0,
22652     longitude: 0,
22653     zoom: 15,
22654     mapTypeId: false,
22655     mapTypeControl: false,
22656     disableDoubleClickZoom: false,
22657     scrollwheel: true,
22658     streetViewControl: false,
22659     radius: 0,
22660     locationName: '',
22661     draggable: true,
22662     enableAutocomplete: false,
22663     enableReverseGeocode: true,
22664     markerTitle: '',
22665     
22666     getAutoCreate: function()
22667     {
22668
22669         var cfg = {
22670             tag: 'div',
22671             cls: 'roo-location-picker'
22672         };
22673         
22674         return cfg
22675     },
22676     
22677     initEvents: function(ct, position)
22678     {       
22679         if(!this.el.getWidth() || this.isApplied()){
22680             return;
22681         }
22682         
22683         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22684         
22685         this.initial();
22686     },
22687     
22688     initial: function()
22689     {
22690         if(!this.mapTypeId){
22691             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22692         }
22693         
22694         this.gMapContext = this.GMapContext();
22695         
22696         this.initOverlayView();
22697         
22698         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22699         
22700         var _this = this;
22701                 
22702         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22703             _this.setPosition(_this.gMapContext.marker.position);
22704         });
22705         
22706         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22707             _this.fireEvent('mapClick', this, event);
22708             
22709         });
22710
22711         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22712             _this.fireEvent('mapRightClick', this, event);
22713             
22714         });
22715         
22716         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22717             _this.fireEvent('markerClick', this, event);
22718             
22719         });
22720
22721         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22722             _this.fireEvent('markerRightClick', this, event);
22723             
22724         });
22725         
22726         this.setPosition(this.gMapContext.location);
22727         
22728         this.fireEvent('initial', this, this.gMapContext.location);
22729     },
22730     
22731     initOverlayView: function()
22732     {
22733         var _this = this;
22734         
22735         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22736             
22737             draw: function()
22738             {
22739                 _this.fireEvent('OverlayViewDraw', _this);
22740             },
22741             
22742             onAdd: function()
22743             {
22744                 _this.fireEvent('OverlayViewOnAdd', _this);
22745             },
22746             
22747             onRemove: function()
22748             {
22749                 _this.fireEvent('OverlayViewOnRemove', _this);
22750             },
22751             
22752             show: function(cpx)
22753             {
22754                 _this.fireEvent('OverlayViewShow', _this, cpx);
22755             },
22756             
22757             hide: function()
22758             {
22759                 _this.fireEvent('OverlayViewHide', _this);
22760             }
22761             
22762         });
22763     },
22764     
22765     fromLatLngToContainerPixel: function(event)
22766     {
22767         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22768     },
22769     
22770     isApplied: function() 
22771     {
22772         return this.getGmapContext() == false ? false : true;
22773     },
22774     
22775     getGmapContext: function() 
22776     {
22777         return this.gMapContext
22778     },
22779     
22780     GMapContext: function() 
22781     {
22782         var position = new google.maps.LatLng(this.latitude, this.longitude);
22783         
22784         var _map = new google.maps.Map(this.el.dom, {
22785             center: position,
22786             zoom: this.zoom,
22787             mapTypeId: this.mapTypeId,
22788             mapTypeControl: this.mapTypeControl,
22789             disableDoubleClickZoom: this.disableDoubleClickZoom,
22790             scrollwheel: this.scrollwheel,
22791             streetViewControl: this.streetViewControl,
22792             locationName: this.locationName,
22793             draggable: this.draggable,
22794             enableAutocomplete: this.enableAutocomplete,
22795             enableReverseGeocode: this.enableReverseGeocode
22796         });
22797         
22798         var _marker = new google.maps.Marker({
22799             position: position,
22800             map: _map,
22801             title: this.markerTitle,
22802             draggable: this.draggable
22803         });
22804         
22805         return {
22806             map: _map,
22807             marker: _marker,
22808             circle: null,
22809             location: position,
22810             radius: this.radius,
22811             locationName: this.locationName,
22812             addressComponents: {
22813                 formatted_address: null,
22814                 addressLine1: null,
22815                 addressLine2: null,
22816                 streetName: null,
22817                 streetNumber: null,
22818                 city: null,
22819                 district: null,
22820                 state: null,
22821                 stateOrProvince: null
22822             },
22823             settings: this,
22824             domContainer: this.el.dom,
22825             geodecoder: new google.maps.Geocoder()
22826         };
22827     },
22828     
22829     drawCircle: function(center, radius, options) 
22830     {
22831         if (this.gMapContext.circle != null) {
22832             this.gMapContext.circle.setMap(null);
22833         }
22834         if (radius > 0) {
22835             radius *= 1;
22836             options = Roo.apply({}, options, {
22837                 strokeColor: "#0000FF",
22838                 strokeOpacity: .35,
22839                 strokeWeight: 2,
22840                 fillColor: "#0000FF",
22841                 fillOpacity: .2
22842             });
22843             
22844             options.map = this.gMapContext.map;
22845             options.radius = radius;
22846             options.center = center;
22847             this.gMapContext.circle = new google.maps.Circle(options);
22848             return this.gMapContext.circle;
22849         }
22850         
22851         return null;
22852     },
22853     
22854     setPosition: function(location) 
22855     {
22856         this.gMapContext.location = location;
22857         this.gMapContext.marker.setPosition(location);
22858         this.gMapContext.map.panTo(location);
22859         this.drawCircle(location, this.gMapContext.radius, {});
22860         
22861         var _this = this;
22862         
22863         if (this.gMapContext.settings.enableReverseGeocode) {
22864             this.gMapContext.geodecoder.geocode({
22865                 latLng: this.gMapContext.location
22866             }, function(results, status) {
22867                 
22868                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22869                     _this.gMapContext.locationName = results[0].formatted_address;
22870                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22871                     
22872                     _this.fireEvent('positionchanged', this, location);
22873                 }
22874             });
22875             
22876             return;
22877         }
22878         
22879         this.fireEvent('positionchanged', this, location);
22880     },
22881     
22882     resize: function()
22883     {
22884         google.maps.event.trigger(this.gMapContext.map, "resize");
22885         
22886         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22887         
22888         this.fireEvent('resize', this);
22889     },
22890     
22891     setPositionByLatLng: function(latitude, longitude)
22892     {
22893         this.setPosition(new google.maps.LatLng(latitude, longitude));
22894     },
22895     
22896     getCurrentPosition: function() 
22897     {
22898         return {
22899             latitude: this.gMapContext.location.lat(),
22900             longitude: this.gMapContext.location.lng()
22901         };
22902     },
22903     
22904     getAddressName: function() 
22905     {
22906         return this.gMapContext.locationName;
22907     },
22908     
22909     getAddressComponents: function() 
22910     {
22911         return this.gMapContext.addressComponents;
22912     },
22913     
22914     address_component_from_google_geocode: function(address_components) 
22915     {
22916         var result = {};
22917         
22918         for (var i = 0; i < address_components.length; i++) {
22919             var component = address_components[i];
22920             if (component.types.indexOf("postal_code") >= 0) {
22921                 result.postalCode = component.short_name;
22922             } else if (component.types.indexOf("street_number") >= 0) {
22923                 result.streetNumber = component.short_name;
22924             } else if (component.types.indexOf("route") >= 0) {
22925                 result.streetName = component.short_name;
22926             } else if (component.types.indexOf("neighborhood") >= 0) {
22927                 result.city = component.short_name;
22928             } else if (component.types.indexOf("locality") >= 0) {
22929                 result.city = component.short_name;
22930             } else if (component.types.indexOf("sublocality") >= 0) {
22931                 result.district = component.short_name;
22932             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22933                 result.stateOrProvince = component.short_name;
22934             } else if (component.types.indexOf("country") >= 0) {
22935                 result.country = component.short_name;
22936             }
22937         }
22938         
22939         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22940         result.addressLine2 = "";
22941         return result;
22942     },
22943     
22944     setZoomLevel: function(zoom)
22945     {
22946         this.gMapContext.map.setZoom(zoom);
22947     },
22948     
22949     show: function()
22950     {
22951         if(!this.el){
22952             return;
22953         }
22954         
22955         this.el.show();
22956         
22957         this.resize();
22958         
22959         this.fireEvent('show', this);
22960     },
22961     
22962     hide: function()
22963     {
22964         if(!this.el){
22965             return;
22966         }
22967         
22968         this.el.hide();
22969         
22970         this.fireEvent('hide', this);
22971     }
22972     
22973 });
22974
22975 Roo.apply(Roo.bootstrap.LocationPicker, {
22976     
22977     OverlayView : function(map, options)
22978     {
22979         options = options || {};
22980         
22981         this.setMap(map);
22982     }
22983     
22984     
22985 });/*
22986  * - LGPL
22987  *
22988  * Alert
22989  * 
22990  */
22991
22992 /**
22993  * @class Roo.bootstrap.Alert
22994  * @extends Roo.bootstrap.Component
22995  * Bootstrap Alert class
22996  * @cfg {String} title The title of alert
22997  * @cfg {String} html The content of alert
22998  * @cfg {String} weight (  success | info | warning | danger )
22999  * @cfg {String} faicon font-awesomeicon
23000  * 
23001  * @constructor
23002  * Create a new alert
23003  * @param {Object} config The config object
23004  */
23005
23006
23007 Roo.bootstrap.Alert = function(config){
23008     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23009     
23010 };
23011
23012 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23013     
23014     title: '',
23015     html: '',
23016     weight: false,
23017     faicon: false,
23018     
23019     getAutoCreate : function()
23020     {
23021         
23022         var cfg = {
23023             tag : 'div',
23024             cls : 'alert',
23025             cn : [
23026                 {
23027                     tag : 'i',
23028                     cls : 'roo-alert-icon'
23029                     
23030                 },
23031                 {
23032                     tag : 'b',
23033                     cls : 'roo-alert-title',
23034                     html : this.title
23035                 },
23036                 {
23037                     tag : 'span',
23038                     cls : 'roo-alert-text',
23039                     html : this.html
23040                 }
23041             ]
23042         };
23043         
23044         if(this.faicon){
23045             cfg.cn[0].cls += ' fa ' + this.faicon;
23046         }
23047         
23048         if(this.weight){
23049             cfg.cls += ' alert-' + this.weight;
23050         }
23051         
23052         return cfg;
23053     },
23054     
23055     initEvents: function() 
23056     {
23057         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23058     },
23059     
23060     setTitle : function(str)
23061     {
23062         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23063     },
23064     
23065     setText : function(str)
23066     {
23067         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23068     },
23069     
23070     setWeight : function(weight)
23071     {
23072         if(this.weight){
23073             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23074         }
23075         
23076         this.weight = weight;
23077         
23078         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23079     },
23080     
23081     setIcon : function(icon)
23082     {
23083         if(this.faicon){
23084             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23085         }
23086         
23087         this.faicon = icon
23088         
23089         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23090     },
23091     
23092     hide: function() 
23093     {
23094         this.el.hide();   
23095     },
23096     
23097     show: function() 
23098     {  
23099         this.el.show();   
23100     }
23101     
23102 });
23103
23104