sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * @cfg {String} xsUrl xs image source
1330  * @cfg {String} smUrl sm image source
1331  * @cfg {String} mdUrl md image source
1332  * @cfg {String} lgUrl lg image source
1333  * 
1334  * @constructor
1335  * Create a new Input
1336  * @param {Object} config The config object
1337  */
1338
1339 Roo.bootstrap.Img = function(config){
1340     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1341     
1342     this.addEvents({
1343         // img events
1344         /**
1345          * @event click
1346          * The img click event for the img.
1347          * @param {Roo.EventObject} e
1348          */
1349         "click" : true
1350     });
1351 };
1352
1353 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1354     
1355     imgResponsive: true,
1356     border: '',
1357     src: '',
1358     href: false,
1359     target: false,
1360     xsUrl: '',
1361     smUrl: '',
1362     mdUrl: '',
1363     lgUrl: '',
1364
1365     getAutoCreate : function()
1366     {   
1367         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1368             return this.createSingleImg();
1369         }
1370         
1371         var cfg = {
1372             tag: 'div',
1373             cls: 'roo-image-responsive-group',
1374             cn: []
1375         }
1376         var _this = this;
1377         
1378         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1379             
1380             if(!_this[size + 'Url']){
1381                 return;
1382             }
1383             
1384             var img = {
1385                 tag: 'img',
1386                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1387                 html: _this.html || cfg.html,
1388                 src: _this[size + 'Url']
1389             }
1390             
1391             img.cls += ' roo-image-responsive-' + size;
1392             
1393             var s = ['xs', 'sm', 'md', 'lg'];
1394             
1395             s.splice(s.indexOf(size), 1);
1396             
1397             Roo.each(s, function(ss){
1398                 img.cls += ' hidden-' + ss;
1399             });
1400             
1401             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1402                 cfg.cls += ' img-' + _this.border;
1403             }
1404             
1405             if(_this.alt){
1406                 cfg.alt = _this.alt;
1407             }
1408             
1409             if(_this.href){
1410                 var a = {
1411                     tag: 'a',
1412                     href: _this.href,
1413                     cn: [
1414                         img
1415                     ]
1416                 }
1417
1418                 if(this.target){
1419                     a.target = _this.target;
1420                 }
1421             }
1422             
1423             cfg.cn.push((_this.href) ? a : img);
1424             
1425         });
1426         
1427         return cfg;
1428     },
1429     
1430     createSingleImg : function()
1431     {
1432         var cfg = {
1433             tag: 'img',
1434             cls: (this.imgResponsive) ? 'img-responsive' : '',
1435             html : null
1436         }
1437         
1438         cfg.html = this.html || cfg.html;
1439         
1440         cfg.src = this.src || cfg.src;
1441         
1442         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1443             cfg.cls += ' img-' + this.border;
1444         }
1445         
1446         if(this.alt){
1447             cfg.alt = this.alt;
1448         }
1449         
1450         if(this.href){
1451             var a = {
1452                 tag: 'a',
1453                 href: this.href,
1454                 cn: [
1455                     cfg
1456                 ]
1457             }
1458             
1459             if(this.target){
1460                 a.target = this.target;
1461             }
1462             
1463         }
1464         
1465         return (this.href) ? a : cfg;
1466     },
1467     
1468     initEvents: function() 
1469     {
1470         if(!this.href){
1471             this.el.on('click', this.onClick, this);
1472         }
1473         
1474     },
1475     
1476     onClick : function(e)
1477     {
1478         Roo.log('img onclick');
1479         this.fireEvent('click', this, e);
1480     }
1481    
1482 });
1483
1484  /*
1485  * - LGPL
1486  *
1487  * image
1488  * 
1489  */
1490
1491
1492 /**
1493  * @class Roo.bootstrap.Link
1494  * @extends Roo.bootstrap.Component
1495  * Bootstrap Link Class
1496  * @cfg {String} alt image alternative text
1497  * @cfg {String} href a tag href
1498  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1499  * @cfg {String} html the content of the link.
1500  * @cfg {String} anchor name for the anchor link
1501
1502  * @cfg {Boolean} preventDefault (true | false) default false
1503
1504  * 
1505  * @constructor
1506  * Create a new Input
1507  * @param {Object} config The config object
1508  */
1509
1510 Roo.bootstrap.Link = function(config){
1511     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1512     
1513     this.addEvents({
1514         // img events
1515         /**
1516          * @event click
1517          * The img click event for the img.
1518          * @param {Roo.EventObject} e
1519          */
1520         "click" : true
1521     });
1522 };
1523
1524 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1525     
1526     href: false,
1527     target: false,
1528     preventDefault: false,
1529     anchor : false,
1530     alt : false,
1531
1532     getAutoCreate : function()
1533     {
1534         
1535         var cfg = {
1536             tag: 'a'
1537         };
1538         // anchor's do not require html/href...
1539         if (this.anchor === false) {
1540             cfg.html = this.html || '';
1541             cfg.href = this.href || '#';
1542         } else {
1543             cfg.name = this.anchor;
1544             if (this.html !== false) {
1545                 cfg.html = this.html;
1546             }
1547             if (this.href !== false) {
1548                 cfg.href = this.href;
1549             }
1550         }
1551         
1552         if(this.alt !== false){
1553             cfg.alt = this.alt;
1554         }
1555         
1556         
1557         if(this.target !== false) {
1558             cfg.target = this.target;
1559         }
1560         
1561         return cfg;
1562     },
1563     
1564     initEvents: function() {
1565         
1566         if(!this.href || this.preventDefault){
1567             this.el.on('click', this.onClick, this);
1568         }
1569     },
1570     
1571     onClick : function(e)
1572     {
1573         if(this.preventDefault){
1574             e.preventDefault();
1575         }
1576         //Roo.log('img onclick');
1577         this.fireEvent('click', this, e);
1578     }
1579    
1580 });
1581
1582  /*
1583  * - LGPL
1584  *
1585  * header
1586  * 
1587  */
1588
1589 /**
1590  * @class Roo.bootstrap.Header
1591  * @extends Roo.bootstrap.Component
1592  * Bootstrap Header class
1593  * @cfg {String} html content of header
1594  * @cfg {Number} level (1|2|3|4|5|6) default 1
1595  * 
1596  * @constructor
1597  * Create a new Header
1598  * @param {Object} config The config object
1599  */
1600
1601
1602 Roo.bootstrap.Header  = function(config){
1603     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1604 };
1605
1606 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1607     
1608     //href : false,
1609     html : false,
1610     level : 1,
1611     
1612     
1613     
1614     getAutoCreate : function(){
1615         
1616         
1617         
1618         var cfg = {
1619             tag: 'h' + (1 *this.level),
1620             html: this.html || ''
1621         } ;
1622         
1623         return cfg;
1624     }
1625    
1626 });
1627
1628  
1629
1630  /*
1631  * Based on:
1632  * Ext JS Library 1.1.1
1633  * Copyright(c) 2006-2007, Ext JS, LLC.
1634  *
1635  * Originally Released Under LGPL - original licence link has changed is not relivant.
1636  *
1637  * Fork - LGPL
1638  * <script type="text/javascript">
1639  */
1640  
1641 /**
1642  * @class Roo.bootstrap.MenuMgr
1643  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1644  * @singleton
1645  */
1646 Roo.bootstrap.MenuMgr = function(){
1647    var menus, active, groups = {}, attached = false, lastShow = new Date();
1648
1649    // private - called when first menu is created
1650    function init(){
1651        menus = {};
1652        active = new Roo.util.MixedCollection();
1653        Roo.get(document).addKeyListener(27, function(){
1654            if(active.length > 0){
1655                hideAll();
1656            }
1657        });
1658    }
1659
1660    // private
1661    function hideAll(){
1662        if(active && active.length > 0){
1663            var c = active.clone();
1664            c.each(function(m){
1665                m.hide();
1666            });
1667        }
1668    }
1669
1670    // private
1671    function onHide(m){
1672        active.remove(m);
1673        if(active.length < 1){
1674            Roo.get(document).un("mouseup", onMouseDown);
1675             
1676            attached = false;
1677        }
1678    }
1679
1680    // private
1681    function onShow(m){
1682        var last = active.last();
1683        lastShow = new Date();
1684        active.add(m);
1685        if(!attached){
1686           Roo.get(document).on("mouseup", onMouseDown);
1687            
1688            attached = true;
1689        }
1690        if(m.parentMenu){
1691           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1692           m.parentMenu.activeChild = m;
1693        }else if(last && last.isVisible()){
1694           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1695        }
1696    }
1697
1698    // private
1699    function onBeforeHide(m){
1700        if(m.activeChild){
1701            m.activeChild.hide();
1702        }
1703        if(m.autoHideTimer){
1704            clearTimeout(m.autoHideTimer);
1705            delete m.autoHideTimer;
1706        }
1707    }
1708
1709    // private
1710    function onBeforeShow(m){
1711        var pm = m.parentMenu;
1712        if(!pm && !m.allowOtherMenus){
1713            hideAll();
1714        }else if(pm && pm.activeChild && active != m){
1715            pm.activeChild.hide();
1716        }
1717    }
1718
1719    // private this should really trigger on mouseup..
1720    function onMouseDown(e){
1721         Roo.log("on Mouse Up");
1722         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1723             Roo.log("hideAll");
1724             hideAll();
1725             e.stopEvent();
1726         }
1727         
1728         
1729    }
1730
1731    // private
1732    function onBeforeCheck(mi, state){
1733        if(state){
1734            var g = groups[mi.group];
1735            for(var i = 0, l = g.length; i < l; i++){
1736                if(g[i] != mi){
1737                    g[i].setChecked(false);
1738                }
1739            }
1740        }
1741    }
1742
1743    return {
1744
1745        /**
1746         * Hides all menus that are currently visible
1747         */
1748        hideAll : function(){
1749             hideAll();  
1750        },
1751
1752        // private
1753        register : function(menu){
1754            if(!menus){
1755                init();
1756            }
1757            menus[menu.id] = menu;
1758            menu.on("beforehide", onBeforeHide);
1759            menu.on("hide", onHide);
1760            menu.on("beforeshow", onBeforeShow);
1761            menu.on("show", onShow);
1762            var g = menu.group;
1763            if(g && menu.events["checkchange"]){
1764                if(!groups[g]){
1765                    groups[g] = [];
1766                }
1767                groups[g].push(menu);
1768                menu.on("checkchange", onCheck);
1769            }
1770        },
1771
1772         /**
1773          * Returns a {@link Roo.menu.Menu} object
1774          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1775          * be used to generate and return a new Menu instance.
1776          */
1777        get : function(menu){
1778            if(typeof menu == "string"){ // menu id
1779                return menus[menu];
1780            }else if(menu.events){  // menu instance
1781                return menu;
1782            }
1783            /*else if(typeof menu.length == 'number'){ // array of menu items?
1784                return new Roo.bootstrap.Menu({items:menu});
1785            }else{ // otherwise, must be a config
1786                return new Roo.bootstrap.Menu(menu);
1787            }
1788            */
1789            return false;
1790        },
1791
1792        // private
1793        unregister : function(menu){
1794            delete menus[menu.id];
1795            menu.un("beforehide", onBeforeHide);
1796            menu.un("hide", onHide);
1797            menu.un("beforeshow", onBeforeShow);
1798            menu.un("show", onShow);
1799            var g = menu.group;
1800            if(g && menu.events["checkchange"]){
1801                groups[g].remove(menu);
1802                menu.un("checkchange", onCheck);
1803            }
1804        },
1805
1806        // private
1807        registerCheckable : function(menuItem){
1808            var g = menuItem.group;
1809            if(g){
1810                if(!groups[g]){
1811                    groups[g] = [];
1812                }
1813                groups[g].push(menuItem);
1814                menuItem.on("beforecheckchange", onBeforeCheck);
1815            }
1816        },
1817
1818        // private
1819        unregisterCheckable : function(menuItem){
1820            var g = menuItem.group;
1821            if(g){
1822                groups[g].remove(menuItem);
1823                menuItem.un("beforecheckchange", onBeforeCheck);
1824            }
1825        }
1826    };
1827 }();/*
1828  * - LGPL
1829  *
1830  * menu
1831  * 
1832  */
1833
1834 /**
1835  * @class Roo.bootstrap.Menu
1836  * @extends Roo.bootstrap.Component
1837  * Bootstrap Menu class - container for MenuItems
1838  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1839  * 
1840  * @constructor
1841  * Create a new Menu
1842  * @param {Object} config The config object
1843  */
1844
1845
1846 Roo.bootstrap.Menu = function(config){
1847     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1848     if (this.registerMenu) {
1849         Roo.bootstrap.MenuMgr.register(this);
1850     }
1851     this.addEvents({
1852         /**
1853          * @event beforeshow
1854          * Fires before this menu is displayed
1855          * @param {Roo.menu.Menu} this
1856          */
1857         beforeshow : true,
1858         /**
1859          * @event beforehide
1860          * Fires before this menu is hidden
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforehide : true,
1864         /**
1865          * @event show
1866          * Fires after this menu is displayed
1867          * @param {Roo.menu.Menu} this
1868          */
1869         show : true,
1870         /**
1871          * @event hide
1872          * Fires after this menu is hidden
1873          * @param {Roo.menu.Menu} this
1874          */
1875         hide : true,
1876         /**
1877          * @event click
1878          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1879          * @param {Roo.menu.Menu} this
1880          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1881          * @param {Roo.EventObject} e
1882          */
1883         click : true,
1884         /**
1885          * @event mouseover
1886          * Fires when the mouse is hovering over this menu
1887          * @param {Roo.menu.Menu} this
1888          * @param {Roo.EventObject} e
1889          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1890          */
1891         mouseover : true,
1892         /**
1893          * @event mouseout
1894          * Fires when the mouse exits this menu
1895          * @param {Roo.menu.Menu} this
1896          * @param {Roo.EventObject} e
1897          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1898          */
1899         mouseout : true,
1900         /**
1901          * @event itemclick
1902          * Fires when a menu item contained in this menu is clicked
1903          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         itemclick: true
1907     });
1908     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1909 };
1910
1911 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1912     
1913    /// html : false,
1914     //align : '',
1915     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1916     type: false,
1917     /**
1918      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1919      */
1920     registerMenu : true,
1921     
1922     menuItems :false, // stores the menu items..
1923     
1924     hidden:true,
1925     
1926     parentMenu : false,
1927     
1928     getChildContainer : function() {
1929         return this.el;  
1930     },
1931     
1932     getAutoCreate : function(){
1933          
1934         //if (['right'].indexOf(this.align)!==-1) {
1935         //    cfg.cn[1].cls += ' pull-right'
1936         //}
1937         
1938         
1939         var cfg = {
1940             tag : 'ul',
1941             cls : 'dropdown-menu' ,
1942             style : 'z-index:1000'
1943             
1944         }
1945         
1946         if (this.type === 'submenu') {
1947             cfg.cls = 'submenu active';
1948         }
1949         if (this.type === 'treeview') {
1950             cfg.cls = 'treeview-menu';
1951         }
1952         
1953         return cfg;
1954     },
1955     initEvents : function() {
1956         
1957        // Roo.log("ADD event");
1958        // Roo.log(this.triggerEl.dom);
1959         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1960         
1961         this.triggerEl.addClass('dropdown-toggle');
1962         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1963
1964         this.el.on("mouseover", this.onMouseOver, this);
1965         this.el.on("mouseout", this.onMouseOut, this);
1966         
1967         
1968     },
1969     findTargetItem : function(e){
1970         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1971         if(!t){
1972             return false;
1973         }
1974         //Roo.log(t);         Roo.log(t.id);
1975         if(t && t.id){
1976             //Roo.log(this.menuitems);
1977             return this.menuitems.get(t.id);
1978             
1979             //return this.items.get(t.menuItemId);
1980         }
1981         
1982         return false;
1983     },
1984     onClick : function(e){
1985         Roo.log("menu.onClick");
1986         var t = this.findTargetItem(e);
1987         if(!t || t.isContainer){
1988             return;
1989         }
1990         Roo.log(e);
1991         /*
1992         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1993             if(t == this.activeItem && t.shouldDeactivate(e)){
1994                 this.activeItem.deactivate();
1995                 delete this.activeItem;
1996                 return;
1997             }
1998             if(t.canActivate){
1999                 this.setActiveItem(t, true);
2000             }
2001             return;
2002             
2003             
2004         }
2005         */
2006        
2007         Roo.log('pass click event');
2008         
2009         t.onClick(e);
2010         
2011         this.fireEvent("click", this, t, e);
2012         
2013         this.hide();
2014     },
2015      onMouseOver : function(e){
2016         var t  = this.findTargetItem(e);
2017         //Roo.log(t);
2018         //if(t){
2019         //    if(t.canActivate && !t.disabled){
2020         //        this.setActiveItem(t, true);
2021         //    }
2022         //}
2023         
2024         this.fireEvent("mouseover", this, e, t);
2025     },
2026     isVisible : function(){
2027         return !this.hidden;
2028     },
2029      onMouseOut : function(e){
2030         var t  = this.findTargetItem(e);
2031         
2032         //if(t ){
2033         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2034         //        this.activeItem.deactivate();
2035         //        delete this.activeItem;
2036         //    }
2037         //}
2038         this.fireEvent("mouseout", this, e, t);
2039     },
2040     
2041     
2042     /**
2043      * Displays this menu relative to another element
2044      * @param {String/HTMLElement/Roo.Element} element The element to align to
2045      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2046      * the element (defaults to this.defaultAlign)
2047      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2048      */
2049     show : function(el, pos, parentMenu){
2050         this.parentMenu = parentMenu;
2051         if(!this.el){
2052             this.render();
2053         }
2054         this.fireEvent("beforeshow", this);
2055         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2056     },
2057      /**
2058      * Displays this menu at a specific xy position
2059      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2060      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2061      */
2062     showAt : function(xy, parentMenu, /* private: */_e){
2063         this.parentMenu = parentMenu;
2064         if(!this.el){
2065             this.render();
2066         }
2067         if(_e !== false){
2068             this.fireEvent("beforeshow", this);
2069             //xy = this.el.adjustForConstraints(xy);
2070         }
2071         
2072         //this.el.show();
2073         this.hideMenuItems();
2074         this.hidden = false;
2075         this.triggerEl.addClass('open');
2076         
2077         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2078             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2079         }
2080         
2081         this.el.setXY(xy);
2082         this.focus();
2083         this.fireEvent("show", this);
2084     },
2085     
2086     focus : function(){
2087         return;
2088         if(!this.hidden){
2089             this.doFocus.defer(50, this);
2090         }
2091     },
2092
2093     doFocus : function(){
2094         if(!this.hidden){
2095             this.focusEl.focus();
2096         }
2097     },
2098
2099     /**
2100      * Hides this menu and optionally all parent menus
2101      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2102      */
2103     hide : function(deep){
2104         
2105         this.hideMenuItems();
2106         if(this.el && this.isVisible()){
2107             this.fireEvent("beforehide", this);
2108             if(this.activeItem){
2109                 this.activeItem.deactivate();
2110                 this.activeItem = null;
2111             }
2112             this.triggerEl.removeClass('open');;
2113             this.hidden = true;
2114             this.fireEvent("hide", this);
2115         }
2116         if(deep === true && this.parentMenu){
2117             this.parentMenu.hide(true);
2118         }
2119     },
2120     
2121     onTriggerPress  : function(e)
2122     {
2123         
2124         Roo.log('trigger press');
2125         //Roo.log(e.getTarget());
2126        // Roo.log(this.triggerEl.dom);
2127         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2128             return;
2129         }
2130         
2131         if (this.isVisible()) {
2132             Roo.log('hide');
2133             this.hide();
2134         } else {
2135             Roo.log('show');
2136             this.show(this.triggerEl, false, false);
2137         }
2138         
2139         e.stopEvent();
2140     },
2141     
2142          
2143        
2144     
2145     hideMenuItems : function()
2146     {
2147         //$(backdrop).remove()
2148         Roo.select('.open',true).each(function(aa) {
2149             
2150             aa.removeClass('open');
2151           //var parent = getParent($(this))
2152           //var relatedTarget = { relatedTarget: this }
2153           
2154            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2155           //if (e.isDefaultPrevented()) return
2156            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2157         })
2158     },
2159     addxtypeChild : function (tree, cntr) {
2160         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2161           
2162         this.menuitems.add(comp);
2163         return comp;
2164
2165     },
2166     getEl : function()
2167     {
2168         Roo.log(this.el);
2169         return this.el;
2170     }
2171 });
2172
2173  
2174  /*
2175  * - LGPL
2176  *
2177  * menu item
2178  * 
2179  */
2180
2181
2182 /**
2183  * @class Roo.bootstrap.MenuItem
2184  * @extends Roo.bootstrap.Component
2185  * Bootstrap MenuItem class
2186  * @cfg {String} html the menu label
2187  * @cfg {String} href the link
2188  * @cfg {Boolean} preventDefault (true | false) default true
2189  * @cfg {Boolean} isContainer (true | false) default false
2190  * 
2191  * 
2192  * @constructor
2193  * Create a new MenuItem
2194  * @param {Object} config The config object
2195  */
2196
2197
2198 Roo.bootstrap.MenuItem = function(config){
2199     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2200     this.addEvents({
2201         // raw events
2202         /**
2203          * @event click
2204          * The raw click event for the entire grid.
2205          * @param {Roo.bootstrap.MenuItem} this
2206          * @param {Roo.EventObject} e
2207          */
2208         "click" : true
2209     });
2210 };
2211
2212 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2213     
2214     href : false,
2215     html : false,
2216     preventDefault: true,
2217     isContainer : false,
2218     
2219     getAutoCreate : function(){
2220         
2221         if(this.isContainer){
2222             return {
2223                 tag: 'li',
2224                 cls: 'dropdown-menu-item'
2225             };
2226         }
2227         
2228         var cfg= {
2229             tag: 'li',
2230             cls: 'dropdown-menu-item',
2231             cn: [
2232                     {
2233                         tag : 'a',
2234                         href : '#',
2235                         html : 'Link'
2236                     }
2237                 ]
2238         };
2239         if (this.parent().type == 'treeview') {
2240             cfg.cls = 'treeview-menu';
2241         }
2242         
2243         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2244         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2245         return cfg;
2246     },
2247     
2248     initEvents: function() {
2249         
2250         //this.el.select('a').on('click', this.onClick, this);
2251         
2252     },
2253     onClick : function(e)
2254     {
2255         Roo.log('item on click ');
2256         //if(this.preventDefault){
2257         //    e.preventDefault();
2258         //}
2259         //this.parent().hideMenuItems();
2260         
2261         this.fireEvent('click', this, e);
2262     },
2263     getEl : function()
2264     {
2265         return this.el;
2266     }
2267 });
2268
2269  
2270
2271  /*
2272  * - LGPL
2273  *
2274  * menu separator
2275  * 
2276  */
2277
2278
2279 /**
2280  * @class Roo.bootstrap.MenuSeparator
2281  * @extends Roo.bootstrap.Component
2282  * Bootstrap MenuSeparator class
2283  * 
2284  * @constructor
2285  * Create a new MenuItem
2286  * @param {Object} config The config object
2287  */
2288
2289
2290 Roo.bootstrap.MenuSeparator = function(config){
2291     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2292 };
2293
2294 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2295     
2296     getAutoCreate : function(){
2297         var cfg = {
2298             cls: 'divider',
2299             tag : 'li'
2300         };
2301         
2302         return cfg;
2303     }
2304    
2305 });
2306
2307  
2308
2309  
2310 /*
2311 * Licence: LGPL
2312 */
2313
2314 /**
2315  * @class Roo.bootstrap.Modal
2316  * @extends Roo.bootstrap.Component
2317  * Bootstrap Modal class
2318  * @cfg {String} title Title of dialog
2319  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2320  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2321  * @cfg {Boolean} specificTitle default false
2322  * @cfg {Array} buttons Array of buttons or standard button set..
2323  * @cfg {String} buttonPosition (left|right|center) default right
2324  * @cfg {Boolean} animate default true
2325  * @cfg {Boolean} allow_close default true
2326  * 
2327  * @constructor
2328  * Create a new Modal Dialog
2329  * @param {Object} config The config object
2330  */
2331
2332 Roo.bootstrap.Modal = function(config){
2333     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event btnclick
2338          * The raw btnclick event for the button
2339          * @param {Roo.EventObject} e
2340          */
2341         "btnclick" : true
2342     });
2343     this.buttons = this.buttons || [];
2344      
2345     if (this.tmpl) {
2346         this.tmpl = Roo.factory(this.tmpl);
2347     }
2348     
2349 };
2350
2351 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2352     
2353     title : 'test dialog',
2354    
2355     buttons : false,
2356     
2357     // set on load...
2358      
2359     html: false,
2360     
2361     tmp: false,
2362     
2363     specificTitle: false,
2364     
2365     buttonPosition: 'right',
2366     
2367     allow_close : true,
2368     
2369     animate : true,
2370     
2371     
2372      // private
2373     bodyEl:  false,
2374     footerEl:  false,
2375     titleEl:  false,
2376     closeEl:  false,
2377     
2378     
2379     onRender : function(ct, position)
2380     {
2381         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2382      
2383         if(!this.el){
2384             var cfg = Roo.apply({},  this.getAutoCreate());
2385             cfg.id = Roo.id();
2386             //if(!cfg.name){
2387             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2388             //}
2389             //if (!cfg.name.length) {
2390             //    delete cfg.name;
2391            // }
2392             if (this.cls) {
2393                 cfg.cls += ' ' + this.cls;
2394             }
2395             if (this.style) {
2396                 cfg.style = this.style;
2397             }
2398             this.el = Roo.get(document.body).createChild(cfg, position);
2399         }
2400         //var type = this.el.dom.type;
2401         
2402         
2403         
2404         
2405         if(this.tabIndex !== undefined){
2406             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2407         }
2408         
2409         
2410         this.bodyEl = this.el.select('.modal-body',true).first();
2411         this.closeEl = this.el.select('.modal-header .close', true).first();
2412         this.footerEl = this.el.select('.modal-footer',true).first();
2413         this.titleEl = this.el.select('.modal-title',true).first();
2414         
2415         
2416          
2417         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2418         this.maskEl.enableDisplayMode("block");
2419         this.maskEl.hide();
2420         //this.el.addClass("x-dlg-modal");
2421     
2422         if (this.buttons.length) {
2423             Roo.each(this.buttons, function(bb) {
2424                 b = Roo.apply({}, bb);
2425                 b.xns = b.xns || Roo.bootstrap;
2426                 b.xtype = b.xtype || 'Button';
2427                 if (typeof(b.listeners) == 'undefined') {
2428                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2429                 }
2430                 
2431                 var btn = Roo.factory(b);
2432                 
2433                 btn.onRender(this.el.select('.modal-footer div').first());
2434                 
2435             },this);
2436         }
2437         // render the children.
2438         var nitems = [];
2439         
2440         if(typeof(this.items) != 'undefined'){
2441             var items = this.items;
2442             delete this.items;
2443
2444             for(var i =0;i < items.length;i++) {
2445                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2446             }
2447         }
2448         
2449         this.items = nitems;
2450         
2451         // where are these used - they used to be body/close/footer
2452         
2453        
2454         this.initEvents();
2455         //this.el.addClass([this.fieldClass, this.cls]);
2456         
2457     },
2458     
2459     getAutoCreate : function(){
2460         
2461         
2462         var bdy = {
2463                 cls : 'modal-body',
2464                 html : this.html || ''
2465         };
2466         
2467         var title = {
2468             tag: 'h4',
2469             cls : 'modal-title',
2470             html : this.title
2471         };
2472         
2473         if(this.specificTitle){
2474             title = this.title;
2475             
2476         };
2477         
2478         var header = [];
2479         if (this.allow_close) {
2480             header.push({
2481                 tag: 'button',
2482                 cls : 'close',
2483                 html : '&times'
2484             });
2485         }
2486         header.push(title);
2487         
2488         var modal = {
2489             cls: "modal",
2490             style : 'display: none',
2491             cn : [
2492                 {
2493                     cls: "modal-dialog",
2494                     cn : [
2495                         {
2496                             cls : "modal-content",
2497                             cn : [
2498                                 {
2499                                     cls : 'modal-header',
2500                                     cn : header
2501                                 },
2502                                 bdy,
2503                                 {
2504                                     cls : 'modal-footer',
2505                                     cn : [
2506                                         {
2507                                             tag: 'div',
2508                                             cls: 'btn-' + this.buttonPosition
2509                                         }
2510                                     ]
2511                                     
2512                                 }
2513                                 
2514                                 
2515                             ]
2516                             
2517                         }
2518                     ]
2519                         
2520                 }
2521             ]
2522         };
2523         
2524         if(this.animate){
2525             modal.cls += ' fade';
2526         }
2527         
2528         return modal;
2529           
2530     },
2531     getChildContainer : function() {
2532          
2533          return this.bodyEl;
2534         
2535     },
2536     getButtonContainer : function() {
2537          return this.el.select('.modal-footer div',true).first();
2538         
2539     },
2540     initEvents : function()
2541     {
2542         if (this.allow_close) {
2543             this.closeEl.on('click', this.hide, this);
2544         }
2545
2546     },
2547     show : function() {
2548         
2549         if (!this.rendered) {
2550             this.render();
2551         }
2552         
2553         this.el.setStyle('display', 'block');
2554         
2555         if(this.animate){
2556             var _this = this;
2557             (function(){ _this.el.addClass('in'); }).defer(50);
2558         }else{
2559             this.el.addClass('in');
2560         }
2561         
2562         // not sure how we can show data in here.. 
2563         //if (this.tmpl) {
2564         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2565         //}
2566         
2567         Roo.get(document.body).addClass("x-body-masked");
2568         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2569         this.maskEl.show();
2570         this.el.setStyle('zIndex', '10001');
2571        
2572         this.fireEvent('show', this);
2573         
2574         
2575     },
2576     hide : function()
2577     {
2578         this.maskEl.hide();
2579         Roo.get(document.body).removeClass("x-body-masked");
2580         this.el.removeClass('in');
2581         
2582         if(this.animate){
2583             var _this = this;
2584             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2585         }else{
2586             this.el.setStyle('display', 'none');
2587         }
2588         
2589         this.fireEvent('hide', this);
2590     },
2591     
2592     addButton : function(str, cb)
2593     {
2594          
2595         
2596         var b = Roo.apply({}, { html : str } );
2597         b.xns = b.xns || Roo.bootstrap;
2598         b.xtype = b.xtype || 'Button';
2599         if (typeof(b.listeners) == 'undefined') {
2600             b.listeners = { click : cb.createDelegate(this)  };
2601         }
2602         
2603         var btn = Roo.factory(b);
2604            
2605         btn.onRender(this.el.select('.modal-footer div').first());
2606         
2607         return btn;   
2608        
2609     },
2610     
2611     setDefaultButton : function(btn)
2612     {
2613         //this.el.select('.modal-footer').()
2614     },
2615     resizeTo: function(w,h)
2616     {
2617         // skip..
2618     },
2619     setContentSize  : function(w, h)
2620     {
2621         
2622     },
2623     onButtonClick: function(btn,e)
2624     {
2625         //Roo.log([a,b,c]);
2626         this.fireEvent('btnclick', btn.name, e);
2627     },
2628      /**
2629      * Set the title of the Dialog
2630      * @param {String} str new Title
2631      */
2632     setTitle: function(str) {
2633         this.titleEl.dom.innerHTML = str;    
2634     },
2635     /**
2636      * Set the body of the Dialog
2637      * @param {String} str new Title
2638      */
2639     setBody: function(str) {
2640         this.bodyEl.dom.innerHTML = str;    
2641     },
2642     /**
2643      * Set the body of the Dialog using the template
2644      * @param {Obj} data - apply this data to the template and replace the body contents.
2645      */
2646     applyBody: function(obj)
2647     {
2648         if (!this.tmpl) {
2649             Roo.log("Error - using apply Body without a template");
2650             //code
2651         }
2652         this.tmpl.overwrite(this.bodyEl, obj);
2653     }
2654     
2655 });
2656
2657
2658 Roo.apply(Roo.bootstrap.Modal,  {
2659     /**
2660          * Button config that displays a single OK button
2661          * @type Object
2662          */
2663         OK :  [{
2664             name : 'ok',
2665             weight : 'primary',
2666             html : 'OK'
2667         }], 
2668         /**
2669          * Button config that displays Yes and No buttons
2670          * @type Object
2671          */
2672         YESNO : [
2673             {
2674                 name  : 'no',
2675                 html : 'No'
2676             },
2677             {
2678                 name  :'yes',
2679                 weight : 'primary',
2680                 html : 'Yes'
2681             }
2682         ],
2683         
2684         /**
2685          * Button config that displays OK and Cancel buttons
2686          * @type Object
2687          */
2688         OKCANCEL : [
2689             {
2690                name : 'cancel',
2691                 html : 'Cancel'
2692             },
2693             {
2694                 name : 'ok',
2695                 weight : 'primary',
2696                 html : 'OK'
2697             }
2698         ],
2699         /**
2700          * Button config that displays Yes, No and Cancel buttons
2701          * @type Object
2702          */
2703         YESNOCANCEL : [
2704             {
2705                 name : 'yes',
2706                 weight : 'primary',
2707                 html : 'Yes'
2708             },
2709             {
2710                 name : 'no',
2711                 html : 'No'
2712             },
2713             {
2714                 name : 'cancel',
2715                 html : 'Cancel'
2716             }
2717         ]
2718 });
2719  
2720  /*
2721  * - LGPL
2722  *
2723  * messagebox - can be used as a replace
2724  * 
2725  */
2726 /**
2727  * @class Roo.MessageBox
2728  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2729  * Example usage:
2730  *<pre><code>
2731 // Basic alert:
2732 Roo.Msg.alert('Status', 'Changes saved successfully.');
2733
2734 // Prompt for user data:
2735 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2736     if (btn == 'ok'){
2737         // process text value...
2738     }
2739 });
2740
2741 // Show a dialog using config options:
2742 Roo.Msg.show({
2743    title:'Save Changes?',
2744    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2745    buttons: Roo.Msg.YESNOCANCEL,
2746    fn: processResult,
2747    animEl: 'elId'
2748 });
2749 </code></pre>
2750  * @singleton
2751  */
2752 Roo.bootstrap.MessageBox = function(){
2753     var dlg, opt, mask, waitTimer;
2754     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2755     var buttons, activeTextEl, bwidth;
2756
2757     
2758     // private
2759     var handleButton = function(button){
2760         dlg.hide();
2761         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2762     };
2763
2764     // private
2765     var handleHide = function(){
2766         if(opt && opt.cls){
2767             dlg.el.removeClass(opt.cls);
2768         }
2769         //if(waitTimer){
2770         //    Roo.TaskMgr.stop(waitTimer);
2771         //    waitTimer = null;
2772         //}
2773     };
2774
2775     // private
2776     var updateButtons = function(b){
2777         var width = 0;
2778         if(!b){
2779             buttons["ok"].hide();
2780             buttons["cancel"].hide();
2781             buttons["yes"].hide();
2782             buttons["no"].hide();
2783             //dlg.footer.dom.style.display = 'none';
2784             return width;
2785         }
2786         dlg.footerEl.dom.style.display = '';
2787         for(var k in buttons){
2788             if(typeof buttons[k] != "function"){
2789                 if(b[k]){
2790                     buttons[k].show();
2791                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2792                     width += buttons[k].el.getWidth()+15;
2793                 }else{
2794                     buttons[k].hide();
2795                 }
2796             }
2797         }
2798         return width;
2799     };
2800
2801     // private
2802     var handleEsc = function(d, k, e){
2803         if(opt && opt.closable !== false){
2804             dlg.hide();
2805         }
2806         if(e){
2807             e.stopEvent();
2808         }
2809     };
2810
2811     return {
2812         /**
2813          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2814          * @return {Roo.BasicDialog} The BasicDialog element
2815          */
2816         getDialog : function(){
2817            if(!dlg){
2818                 dlg = new Roo.bootstrap.Modal( {
2819                     //draggable: true,
2820                     //resizable:false,
2821                     //constraintoviewport:false,
2822                     //fixedcenter:true,
2823                     //collapsible : false,
2824                     //shim:true,
2825                     //modal: true,
2826                   //  width:400,
2827                   //  height:100,
2828                     //buttonAlign:"center",
2829                     closeClick : function(){
2830                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2831                             handleButton("no");
2832                         }else{
2833                             handleButton("cancel");
2834                         }
2835                     }
2836                 });
2837                 dlg.render();
2838                 dlg.on("hide", handleHide);
2839                 mask = dlg.mask;
2840                 //dlg.addKeyListener(27, handleEsc);
2841                 buttons = {};
2842                 this.buttons = buttons;
2843                 var bt = this.buttonText;
2844                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2845                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2846                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2847                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2848                 Roo.log(buttons)
2849                 bodyEl = dlg.bodyEl.createChild({
2850
2851                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2852                         '<textarea class="roo-mb-textarea"></textarea>' +
2853                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2854                 });
2855                 msgEl = bodyEl.dom.firstChild;
2856                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2857                 textboxEl.enableDisplayMode();
2858                 textboxEl.addKeyListener([10,13], function(){
2859                     if(dlg.isVisible() && opt && opt.buttons){
2860                         if(opt.buttons.ok){
2861                             handleButton("ok");
2862                         }else if(opt.buttons.yes){
2863                             handleButton("yes");
2864                         }
2865                     }
2866                 });
2867                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2868                 textareaEl.enableDisplayMode();
2869                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2870                 progressEl.enableDisplayMode();
2871                 var pf = progressEl.dom.firstChild;
2872                 if (pf) {
2873                     pp = Roo.get(pf.firstChild);
2874                     pp.setHeight(pf.offsetHeight);
2875                 }
2876                 
2877             }
2878             return dlg;
2879         },
2880
2881         /**
2882          * Updates the message box body text
2883          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2884          * the XHTML-compliant non-breaking space character '&amp;#160;')
2885          * @return {Roo.MessageBox} This message box
2886          */
2887         updateText : function(text){
2888             if(!dlg.isVisible() && !opt.width){
2889                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2890             }
2891             msgEl.innerHTML = text || '&#160;';
2892       
2893             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2894             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2895             var w = Math.max(
2896                     Math.min(opt.width || cw , this.maxWidth), 
2897                     Math.max(opt.minWidth || this.minWidth, bwidth)
2898             );
2899             if(opt.prompt){
2900                 activeTextEl.setWidth(w);
2901             }
2902             if(dlg.isVisible()){
2903                 dlg.fixedcenter = false;
2904             }
2905             // to big, make it scroll. = But as usual stupid IE does not support
2906             // !important..
2907             
2908             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2909                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2910                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2911             } else {
2912                 bodyEl.dom.style.height = '';
2913                 bodyEl.dom.style.overflowY = '';
2914             }
2915             if (cw > w) {
2916                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2917             } else {
2918                 bodyEl.dom.style.overflowX = '';
2919             }
2920             
2921             dlg.setContentSize(w, bodyEl.getHeight());
2922             if(dlg.isVisible()){
2923                 dlg.fixedcenter = true;
2924             }
2925             return this;
2926         },
2927
2928         /**
2929          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2930          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2931          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2932          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2933          * @return {Roo.MessageBox} This message box
2934          */
2935         updateProgress : function(value, text){
2936             if(text){
2937                 this.updateText(text);
2938             }
2939             if (pp) { // weird bug on my firefox - for some reason this is not defined
2940                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2941             }
2942             return this;
2943         },        
2944
2945         /**
2946          * Returns true if the message box is currently displayed
2947          * @return {Boolean} True if the message box is visible, else false
2948          */
2949         isVisible : function(){
2950             return dlg && dlg.isVisible();  
2951         },
2952
2953         /**
2954          * Hides the message box if it is displayed
2955          */
2956         hide : function(){
2957             if(this.isVisible()){
2958                 dlg.hide();
2959             }  
2960         },
2961
2962         /**
2963          * Displays a new message box, or reinitializes an existing message box, based on the config options
2964          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2965          * The following config object properties are supported:
2966          * <pre>
2967 Property    Type             Description
2968 ----------  ---------------  ------------------------------------------------------------------------------------
2969 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2970                                    closes (defaults to undefined)
2971 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2972                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2973 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2974                                    progress and wait dialogs will ignore this property and always hide the
2975                                    close button as they can only be closed programmatically.
2976 cls               String           A custom CSS class to apply to the message box element
2977 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2978                                    displayed (defaults to 75)
2979 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2980                                    function will be btn (the name of the button that was clicked, if applicable,
2981                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2982                                    Progress and wait dialogs will ignore this option since they do not respond to
2983                                    user actions and can only be closed programmatically, so any required function
2984                                    should be called by the same code after it closes the dialog.
2985 icon              String           A CSS class that provides a background image to be used as an icon for
2986                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2987 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2988 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2989 modal             Boolean          False to allow user interaction with the page while the message box is
2990                                    displayed (defaults to true)
2991 msg               String           A string that will replace the existing message box body text (defaults
2992                                    to the XHTML-compliant non-breaking space character '&#160;')
2993 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2994 progress          Boolean          True to display a progress bar (defaults to false)
2995 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2996 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2997 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2998 title             String           The title text
2999 value             String           The string value to set into the active textbox element if displayed
3000 wait              Boolean          True to display a progress bar (defaults to false)
3001 width             Number           The width of the dialog in pixels
3002 </pre>
3003          *
3004          * Example usage:
3005          * <pre><code>
3006 Roo.Msg.show({
3007    title: 'Address',
3008    msg: 'Please enter your address:',
3009    width: 300,
3010    buttons: Roo.MessageBox.OKCANCEL,
3011    multiline: true,
3012    fn: saveAddress,
3013    animEl: 'addAddressBtn'
3014 });
3015 </code></pre>
3016          * @param {Object} config Configuration options
3017          * @return {Roo.MessageBox} This message box
3018          */
3019         show : function(options)
3020         {
3021             
3022             // this causes nightmares if you show one dialog after another
3023             // especially on callbacks..
3024              
3025             if(this.isVisible()){
3026                 
3027                 this.hide();
3028                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3029                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3030                 Roo.log("New Dialog Message:" +  options.msg )
3031                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3032                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3033                 
3034             }
3035             var d = this.getDialog();
3036             opt = options;
3037             d.setTitle(opt.title || "&#160;");
3038             d.closeEl.setDisplayed(opt.closable !== false);
3039             activeTextEl = textboxEl;
3040             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3041             if(opt.prompt){
3042                 if(opt.multiline){
3043                     textboxEl.hide();
3044                     textareaEl.show();
3045                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3046                         opt.multiline : this.defaultTextHeight);
3047                     activeTextEl = textareaEl;
3048                 }else{
3049                     textboxEl.show();
3050                     textareaEl.hide();
3051                 }
3052             }else{
3053                 textboxEl.hide();
3054                 textareaEl.hide();
3055             }
3056             progressEl.setDisplayed(opt.progress === true);
3057             this.updateProgress(0);
3058             activeTextEl.dom.value = opt.value || "";
3059             if(opt.prompt){
3060                 dlg.setDefaultButton(activeTextEl);
3061             }else{
3062                 var bs = opt.buttons;
3063                 var db = null;
3064                 if(bs && bs.ok){
3065                     db = buttons["ok"];
3066                 }else if(bs && bs.yes){
3067                     db = buttons["yes"];
3068                 }
3069                 dlg.setDefaultButton(db);
3070             }
3071             bwidth = updateButtons(opt.buttons);
3072             this.updateText(opt.msg);
3073             if(opt.cls){
3074                 d.el.addClass(opt.cls);
3075             }
3076             d.proxyDrag = opt.proxyDrag === true;
3077             d.modal = opt.modal !== false;
3078             d.mask = opt.modal !== false ? mask : false;
3079             if(!d.isVisible()){
3080                 // force it to the end of the z-index stack so it gets a cursor in FF
3081                 document.body.appendChild(dlg.el.dom);
3082                 d.animateTarget = null;
3083                 d.show(options.animEl);
3084             }
3085             return this;
3086         },
3087
3088         /**
3089          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3090          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3091          * and closing the message box when the process is complete.
3092          * @param {String} title The title bar text
3093          * @param {String} msg The message box body text
3094          * @return {Roo.MessageBox} This message box
3095          */
3096         progress : function(title, msg){
3097             this.show({
3098                 title : title,
3099                 msg : msg,
3100                 buttons: false,
3101                 progress:true,
3102                 closable:false,
3103                 minWidth: this.minProgressWidth,
3104                 modal : true
3105             });
3106             return this;
3107         },
3108
3109         /**
3110          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3111          * If a callback function is passed it will be called after the user clicks the button, and the
3112          * id of the button that was clicked will be passed as the only parameter to the callback
3113          * (could also be the top-right close button).
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          * @return {Roo.MessageBox} This message box
3119          */
3120         alert : function(title, msg, fn, scope){
3121             this.show({
3122                 title : title,
3123                 msg : msg,
3124                 buttons: this.OK,
3125                 fn: fn,
3126                 scope : scope,
3127                 modal : true
3128             });
3129             return this;
3130         },
3131
3132         /**
3133          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3134          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3135          * You are responsible for closing the message box when the process is complete.
3136          * @param {String} msg The message box body text
3137          * @param {String} title (optional) The title bar text
3138          * @return {Roo.MessageBox} This message box
3139          */
3140         wait : function(msg, title){
3141             this.show({
3142                 title : title,
3143                 msg : msg,
3144                 buttons: false,
3145                 closable:false,
3146                 progress:true,
3147                 modal:true,
3148                 width:300,
3149                 wait:true
3150             });
3151             waitTimer = Roo.TaskMgr.start({
3152                 run: function(i){
3153                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3154                 },
3155                 interval: 1000
3156             });
3157             return this;
3158         },
3159
3160         /**
3161          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3162          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3163          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3164          * @param {String} title The title bar text
3165          * @param {String} msg The message box body text
3166          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3167          * @param {Object} scope (optional) The scope of the callback function
3168          * @return {Roo.MessageBox} This message box
3169          */
3170         confirm : function(title, msg, fn, scope){
3171             this.show({
3172                 title : title,
3173                 msg : msg,
3174                 buttons: this.YESNO,
3175                 fn: fn,
3176                 scope : scope,
3177                 modal : true
3178             });
3179             return this;
3180         },
3181
3182         /**
3183          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3184          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3185          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3186          * (could also be the top-right close button) and the text that was entered will be passed as the two
3187          * parameters to the callback.
3188          * @param {String} title The title bar text
3189          * @param {String} msg The message box body text
3190          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3191          * @param {Object} scope (optional) The scope of the callback function
3192          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3193          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3194          * @return {Roo.MessageBox} This message box
3195          */
3196         prompt : function(title, msg, fn, scope, multiline){
3197             this.show({
3198                 title : title,
3199                 msg : msg,
3200                 buttons: this.OKCANCEL,
3201                 fn: fn,
3202                 minWidth:250,
3203                 scope : scope,
3204                 prompt:true,
3205                 multiline: multiline,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Button config that displays a single OK button
3213          * @type Object
3214          */
3215         OK : {ok:true},
3216         /**
3217          * Button config that displays Yes and No buttons
3218          * @type Object
3219          */
3220         YESNO : {yes:true, no:true},
3221         /**
3222          * Button config that displays OK and Cancel buttons
3223          * @type Object
3224          */
3225         OKCANCEL : {ok:true, cancel:true},
3226         /**
3227          * Button config that displays Yes, No and Cancel buttons
3228          * @type Object
3229          */
3230         YESNOCANCEL : {yes:true, no:true, cancel:true},
3231
3232         /**
3233          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3234          * @type Number
3235          */
3236         defaultTextHeight : 75,
3237         /**
3238          * The maximum width in pixels of the message box (defaults to 600)
3239          * @type Number
3240          */
3241         maxWidth : 600,
3242         /**
3243          * The minimum width in pixels of the message box (defaults to 100)
3244          * @type Number
3245          */
3246         minWidth : 100,
3247         /**
3248          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3249          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3250          * @type Number
3251          */
3252         minProgressWidth : 250,
3253         /**
3254          * An object containing the default button text strings that can be overriden for localized language support.
3255          * Supported properties are: ok, cancel, yes and no.
3256          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3257          * @type Object
3258          */
3259         buttonText : {
3260             ok : "OK",
3261             cancel : "Cancel",
3262             yes : "Yes",
3263             no : "No"
3264         }
3265     };
3266 }();
3267
3268 /**
3269  * Shorthand for {@link Roo.MessageBox}
3270  */
3271 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3272 Roo.Msg = Roo.Msg || Roo.MessageBox;
3273 /*
3274  * - LGPL
3275  *
3276  * navbar
3277  * 
3278  */
3279
3280 /**
3281  * @class Roo.bootstrap.Navbar
3282  * @extends Roo.bootstrap.Component
3283  * Bootstrap Navbar class
3284
3285  * @constructor
3286  * Create a new Navbar
3287  * @param {Object} config The config object
3288  */
3289
3290
3291 Roo.bootstrap.Navbar = function(config){
3292     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3293     
3294 };
3295
3296 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3297     
3298     
3299    
3300     // private
3301     navItems : false,
3302     loadMask : false,
3303     
3304     
3305     getAutoCreate : function(){
3306         
3307         
3308         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3309         
3310     },
3311     
3312     initEvents :function ()
3313     {
3314         //Roo.log(this.el.select('.navbar-toggle',true));
3315         this.el.select('.navbar-toggle',true).on('click', function() {
3316            // Roo.log('click');
3317             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3318         }, this);
3319         
3320         var mark = {
3321             tag: "div",
3322             cls:"x-dlg-mask"
3323         }
3324         
3325         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3326         
3327         var size = this.el.getSize();
3328         this.maskEl.setSize(size.width, size.height);
3329         this.maskEl.enableDisplayMode("block");
3330         this.maskEl.hide();
3331         
3332         if(this.loadMask){
3333             this.maskEl.show();
3334         }
3335     },
3336     
3337     
3338     getChildContainer : function()
3339     {
3340         if (this.el.select('.collapse').getCount()) {
3341             return this.el.select('.collapse',true).first();
3342         }
3343         
3344         return this.el;
3345     },
3346     
3347     mask : function()
3348     {
3349         this.maskEl.show();
3350     },
3351     
3352     unmask : function()
3353     {
3354         this.maskEl.hide();
3355     } 
3356     
3357     
3358     
3359     
3360 });
3361
3362
3363
3364  
3365
3366  /*
3367  * - LGPL
3368  *
3369  * navbar
3370  * 
3371  */
3372
3373 /**
3374  * @class Roo.bootstrap.NavSimplebar
3375  * @extends Roo.bootstrap.Navbar
3376  * Bootstrap Sidebar class
3377  *
3378  * @cfg {Boolean} inverse is inverted color
3379  * 
3380  * @cfg {String} type (nav | pills | tabs)
3381  * @cfg {Boolean} arrangement stacked | justified
3382  * @cfg {String} align (left | right) alignment
3383  * 
3384  * @cfg {Boolean} main (true|false) main nav bar? default false
3385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3386  * 
3387  * @cfg {String} tag (header|footer|nav|div) default is nav 
3388
3389  * 
3390  * 
3391  * 
3392  * @constructor
3393  * Create a new Sidebar
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.NavSimplebar = function(config){
3399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3403     
3404     inverse: false,
3405     
3406     type: false,
3407     arrangement: '',
3408     align : false,
3409     
3410     
3411     
3412     main : false,
3413     
3414     
3415     tag : false,
3416     
3417     
3418     getAutoCreate : function(){
3419         
3420         
3421         var cfg = {
3422             tag : this.tag || 'div',
3423             cls : 'navbar'
3424         };
3425           
3426         
3427         cfg.cn = [
3428             {
3429                 cls: 'nav',
3430                 tag : 'ul'
3431             }
3432         ];
3433         
3434          
3435         this.type = this.type || 'nav';
3436         if (['tabs','pills'].indexOf(this.type)!==-1) {
3437             cfg.cn[0].cls += ' nav-' + this.type
3438         
3439         
3440         } else {
3441             if (this.type!=='nav') {
3442                 Roo.log('nav type must be nav/tabs/pills')
3443             }
3444             cfg.cn[0].cls += ' navbar-nav'
3445         }
3446         
3447         
3448         
3449         
3450         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3451             cfg.cn[0].cls += ' nav-' + this.arrangement;
3452         }
3453         
3454         
3455         if (this.align === 'right') {
3456             cfg.cn[0].cls += ' navbar-right';
3457         }
3458         
3459         if (this.inverse) {
3460             cfg.cls += ' navbar-inverse';
3461             
3462         }
3463         
3464         
3465         return cfg;
3466     
3467         
3468     }
3469     
3470     
3471     
3472 });
3473
3474
3475
3476  
3477
3478  
3479        /*
3480  * - LGPL
3481  *
3482  * navbar
3483  * 
3484  */
3485
3486 /**
3487  * @class Roo.bootstrap.NavHeaderbar
3488  * @extends Roo.bootstrap.NavSimplebar
3489  * Bootstrap Sidebar class
3490  *
3491  * @cfg {String} brand what is brand
3492  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3493  * @cfg {String} brand_href href of the brand
3494  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3495  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3496  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3497  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3498  * 
3499  * @constructor
3500  * Create a new Sidebar
3501  * @param {Object} config The config object
3502  */
3503
3504
3505 Roo.bootstrap.NavHeaderbar = function(config){
3506     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3507       
3508 };
3509
3510 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3511     
3512     position: '',
3513     brand: '',
3514     brand_href: false,
3515     srButton : true,
3516     autohide : false,
3517     desktopCenter : false,
3518    
3519     
3520     getAutoCreate : function(){
3521         
3522         var   cfg = {
3523             tag: this.nav || 'nav',
3524             cls: 'navbar',
3525             role: 'navigation',
3526             cn: []
3527         };
3528         
3529         var cn = cfg.cn;
3530         if (this.desktopCenter) {
3531             cn.push({cls : 'container', cn : []});
3532             cn = cn[0].cn;
3533         }
3534         
3535         if(this.srButton){
3536             cn.push({
3537                 tag: 'div',
3538                 cls: 'navbar-header',
3539                 cn: [
3540                     {
3541                         tag: 'button',
3542                         type: 'button',
3543                         cls: 'navbar-toggle',
3544                         'data-toggle': 'collapse',
3545                         cn: [
3546                             {
3547                                 tag: 'span',
3548                                 cls: 'sr-only',
3549                                 html: 'Toggle navigation'
3550                             },
3551                             {
3552                                 tag: 'span',
3553                                 cls: 'icon-bar'
3554                             },
3555                             {
3556                                 tag: 'span',
3557                                 cls: 'icon-bar'
3558                             },
3559                             {
3560                                 tag: 'span',
3561                                 cls: 'icon-bar'
3562                             }
3563                         ]
3564                     }
3565                 ]
3566             });
3567         }
3568         
3569         cn.push({
3570             tag: 'div',
3571             cls: 'collapse navbar-collapse',
3572             cn : []
3573         });
3574         
3575         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3576         
3577         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3578             cfg.cls += ' navbar-' + this.position;
3579             
3580             // tag can override this..
3581             
3582             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3583         }
3584         
3585         if (this.brand !== '') {
3586             cn[0].cn.push({
3587                 tag: 'a',
3588                 href: this.brand_href ? this.brand_href : '#',
3589                 cls: 'navbar-brand',
3590                 cn: [
3591                 this.brand
3592                 ]
3593             });
3594         }
3595         
3596         if(this.main){
3597             cfg.cls += ' main-nav';
3598         }
3599         
3600         
3601         return cfg;
3602
3603         
3604     },
3605     getHeaderChildContainer : function()
3606     {
3607         if (this.el.select('.navbar-header').getCount()) {
3608             return this.el.select('.navbar-header',true).first();
3609         }
3610         
3611         return this.getChildContainer();
3612     },
3613     
3614     
3615     initEvents : function()
3616     {
3617         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3618         
3619         if (this.autohide) {
3620             
3621             var prevScroll = 0;
3622             var ft = this.el;
3623             
3624             Roo.get(document).on('scroll',function(e) {
3625                 var ns = Roo.get(document).getScroll().top;
3626                 var os = prevScroll;
3627                 prevScroll = ns;
3628                 
3629                 if(ns > os){
3630                     ft.removeClass('slideDown');
3631                     ft.addClass('slideUp');
3632                     return;
3633                 }
3634                 ft.removeClass('slideUp');
3635                 ft.addClass('slideDown');
3636                  
3637               
3638           },this);
3639         }
3640     }    
3641     
3642 });
3643
3644
3645
3646  
3647
3648  /*
3649  * - LGPL
3650  *
3651  * navbar
3652  * 
3653  */
3654
3655 /**
3656  * @class Roo.bootstrap.NavSidebar
3657  * @extends Roo.bootstrap.Navbar
3658  * Bootstrap Sidebar class
3659  * 
3660  * @constructor
3661  * Create a new Sidebar
3662  * @param {Object} config The config object
3663  */
3664
3665
3666 Roo.bootstrap.NavSidebar = function(config){
3667     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3668 };
3669
3670 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3671     
3672     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3673     
3674     getAutoCreate : function(){
3675         
3676         
3677         return  {
3678             tag: 'div',
3679             cls: 'sidebar sidebar-nav'
3680         };
3681     
3682         
3683     }
3684     
3685     
3686     
3687 });
3688
3689
3690
3691  
3692
3693  /*
3694  * - LGPL
3695  *
3696  * nav group
3697  * 
3698  */
3699
3700 /**
3701  * @class Roo.bootstrap.NavGroup
3702  * @extends Roo.bootstrap.Component
3703  * Bootstrap NavGroup class
3704  * @cfg {String} align (left|right)
3705  * @cfg {Boolean} inverse
3706  * @cfg {String} type (nav|pills|tab) default nav
3707  * @cfg {String} navId - reference Id for navbar.
3708
3709  * 
3710  * @constructor
3711  * Create a new nav group
3712  * @param {Object} config The config object
3713  */
3714
3715 Roo.bootstrap.NavGroup = function(config){
3716     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3717     this.navItems = [];
3718    
3719     Roo.bootstrap.NavGroup.register(this);
3720      this.addEvents({
3721         /**
3722              * @event changed
3723              * Fires when the active item changes
3724              * @param {Roo.bootstrap.NavGroup} this
3725              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3726              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3727          */
3728         'changed': true
3729      });
3730     
3731 };
3732
3733 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3734     
3735     align: '',
3736     inverse: false,
3737     form: false,
3738     type: 'nav',
3739     navId : '',
3740     // private
3741     
3742     navItems : false, 
3743     
3744     getAutoCreate : function()
3745     {
3746         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3747         
3748         cfg = {
3749             tag : 'ul',
3750             cls: 'nav' 
3751         }
3752         
3753         if (['tabs','pills'].indexOf(this.type)!==-1) {
3754             cfg.cls += ' nav-' + this.type
3755         } else {
3756             if (this.type!=='nav') {
3757                 Roo.log('nav type must be nav/tabs/pills')
3758             }
3759             cfg.cls += ' navbar-nav'
3760         }
3761         
3762         if (this.parent().sidebar) {
3763             cfg = {
3764                 tag: 'ul',
3765                 cls: 'dashboard-menu sidebar-menu'
3766             }
3767             
3768             return cfg;
3769         }
3770         
3771         if (this.form === true) {
3772             cfg = {
3773                 tag: 'form',
3774                 cls: 'navbar-form'
3775             }
3776             
3777             if (this.align === 'right') {
3778                 cfg.cls += ' navbar-right';
3779             } else {
3780                 cfg.cls += ' navbar-left';
3781             }
3782         }
3783         
3784         if (this.align === 'right') {
3785             cfg.cls += ' navbar-right';
3786         }
3787         
3788         if (this.inverse) {
3789             cfg.cls += ' navbar-inverse';
3790             
3791         }
3792         
3793         
3794         return cfg;
3795     },
3796     /**
3797     * sets the active Navigation item
3798     * @param {Roo.bootstrap.NavItem} the new current navitem
3799     */
3800     setActiveItem : function(item)
3801     {
3802         var prev = false;
3803         Roo.each(this.navItems, function(v){
3804             if (v == item) {
3805                 return ;
3806             }
3807             if (v.isActive()) {
3808                 v.setActive(false, true);
3809                 prev = v;
3810                 
3811             }
3812             
3813         });
3814
3815         item.setActive(true, true);
3816         this.fireEvent('changed', this, item, prev);
3817         
3818         
3819     },
3820     /**
3821     * gets the active Navigation item
3822     * @return {Roo.bootstrap.NavItem} the current navitem
3823     */
3824     getActive : function()
3825     {
3826         
3827         var prev = false;
3828         Roo.each(this.navItems, function(v){
3829             
3830             if (v.isActive()) {
3831                 prev = v;
3832                 
3833             }
3834             
3835         });
3836         return prev;
3837     },
3838     
3839     indexOfNav : function()
3840     {
3841         
3842         var prev = false;
3843         Roo.each(this.navItems, function(v,i){
3844             
3845             if (v.isActive()) {
3846                 prev = i;
3847                 
3848             }
3849             
3850         });
3851         return prev;
3852     },
3853     /**
3854     * adds a Navigation item
3855     * @param {Roo.bootstrap.NavItem} the navitem to add
3856     */
3857     addItem : function(cfg)
3858     {
3859         var cn = new Roo.bootstrap.NavItem(cfg);
3860         this.register(cn);
3861         cn.parentId = this.id;
3862         cn.onRender(this.el, null);
3863         return cn;
3864     },
3865     /**
3866     * register a Navigation item
3867     * @param {Roo.bootstrap.NavItem} the navitem to add
3868     */
3869     register : function(item)
3870     {
3871         this.navItems.push( item);
3872         item.navId = this.navId;
3873     
3874     },
3875     
3876     /**
3877     * clear all the Navigation item
3878     */
3879    
3880     clearAll : function()
3881     {
3882         this.navItems = [];
3883         this.el.dom.innerHTML = '';
3884     },
3885     
3886     getNavItem: function(tabId)
3887     {
3888         var ret = false;
3889         Roo.each(this.navItems, function(e) {
3890             if (e.tabId == tabId) {
3891                ret =  e;
3892                return false;
3893             }
3894             return true;
3895             
3896         });
3897         return ret;
3898     },
3899     
3900     setActiveNext : function()
3901     {
3902         var i = this.indexOfNav(this.getActive());
3903         if (i > this.navItems.length) {
3904             return;
3905         }
3906         this.setActiveItem(this.navItems[i+1]);
3907     },
3908     setActivePrev : function()
3909     {
3910         var i = this.indexOfNav(this.getActive());
3911         if (i  < 1) {
3912             return;
3913         }
3914         this.setActiveItem(this.navItems[i-1]);
3915     },
3916     clearWasActive : function(except) {
3917         Roo.each(this.navItems, function(e) {
3918             if (e.tabId != except.tabId && e.was_active) {
3919                e.was_active = false;
3920                return false;
3921             }
3922             return true;
3923             
3924         });
3925     },
3926     getWasActive : function ()
3927     {
3928         var r = false;
3929         Roo.each(this.navItems, function(e) {
3930             if (e.was_active) {
3931                r = e;
3932                return false;
3933             }
3934             return true;
3935             
3936         });
3937         return r;
3938     }
3939     
3940     
3941 });
3942
3943  
3944 Roo.apply(Roo.bootstrap.NavGroup, {
3945     
3946     groups: {},
3947      /**
3948     * register a Navigation Group
3949     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3950     */
3951     register : function(navgrp)
3952     {
3953         this.groups[navgrp.navId] = navgrp;
3954         
3955     },
3956     /**
3957     * fetch a Navigation Group based on the navigation ID
3958     * @param {string} the navgroup to add
3959     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3960     */
3961     get: function(navId) {
3962         if (typeof(this.groups[navId]) == 'undefined') {
3963             return false;
3964             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3965         }
3966         return this.groups[navId] ;
3967     }
3968     
3969     
3970     
3971 });
3972
3973  /*
3974  * - LGPL
3975  *
3976  * row
3977  * 
3978  */
3979
3980 /**
3981  * @class Roo.bootstrap.NavItem
3982  * @extends Roo.bootstrap.Component
3983  * Bootstrap Navbar.NavItem class
3984  * @cfg {String} href  link to
3985  * @cfg {String} html content of button
3986  * @cfg {String} badge text inside badge
3987  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3988  * @cfg {String} glyphicon name of glyphicon
3989  * @cfg {String} icon name of font awesome icon
3990  * @cfg {Boolean} active Is item active
3991  * @cfg {Boolean} disabled Is item disabled
3992  
3993  * @cfg {Boolean} preventDefault (true | false) default false
3994  * @cfg {String} tabId the tab that this item activates.
3995  * @cfg {String} tagtype (a|span) render as a href or span?
3996  * @cfg {Boolean} animateRef (true|false) link to element default false  
3997   
3998  * @constructor
3999  * Create a new Navbar Item
4000  * @param {Object} config The config object
4001  */
4002 Roo.bootstrap.NavItem = function(config){
4003     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4004     this.addEvents({
4005         // raw events
4006         /**
4007          * @event click
4008          * The raw click event for the entire grid.
4009          * @param {Roo.EventObject} e
4010          */
4011         "click" : true,
4012          /**
4013             * @event changed
4014             * Fires when the active item active state changes
4015             * @param {Roo.bootstrap.NavItem} this
4016             * @param {boolean} state the new state
4017              
4018          */
4019         'changed': true,
4020         /**
4021             * @event scrollto
4022             * Fires when scroll to element
4023             * @param {Roo.bootstrap.NavItem} this
4024             * @param {Object} options
4025             * @param {Roo.EventObject} e
4026              
4027          */
4028         'scrollto': true
4029     });
4030    
4031 };
4032
4033 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4034     
4035     href: false,
4036     html: '',
4037     badge: '',
4038     icon: false,
4039     glyphicon: false,
4040     active: false,
4041     preventDefault : false,
4042     tabId : false,
4043     tagtype : 'a',
4044     disabled : false,
4045     animateRef : false,
4046     was_active : false,
4047     
4048     getAutoCreate : function(){
4049          
4050         var cfg = {
4051             tag: 'li',
4052             cls: 'nav-item'
4053             
4054         }
4055         if (this.active) {
4056             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4057         }
4058         if (this.disabled) {
4059             cfg.cls += ' disabled';
4060         }
4061         
4062         if (this.href || this.html || this.glyphicon || this.icon) {
4063             cfg.cn = [
4064                 {
4065                     tag: this.tagtype,
4066                     href : this.href || "#",
4067                     html: this.html || ''
4068                 }
4069             ];
4070             
4071             if (this.icon) {
4072                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4073             }
4074
4075             if(this.glyphicon) {
4076                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4077             }
4078             
4079             if (this.menu) {
4080                 
4081                 cfg.cn[0].html += " <span class='caret'></span>";
4082              
4083             }
4084             
4085             if (this.badge !== '') {
4086                  
4087                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4088             }
4089         }
4090         
4091         
4092         
4093         return cfg;
4094     },
4095     initEvents: function() 
4096     {
4097         if (typeof (this.menu) != 'undefined') {
4098             this.menu.parentType = this.xtype;
4099             this.menu.triggerEl = this.el;
4100             this.menu = this.addxtype(Roo.apply({}, this.menu));
4101         }
4102         
4103         this.el.select('a',true).on('click', this.onClick, this);
4104         
4105         if(this.tagtype == 'span'){
4106             this.el.select('span',true).on('click', this.onClick, this);
4107         }
4108        
4109         // at this point parent should be available..
4110         this.parent().register(this);
4111     },
4112     
4113     onClick : function(e)
4114     {
4115         if(
4116                 this.preventDefault || 
4117                 this.href == '#' 
4118         ){
4119             
4120             e.preventDefault();
4121         }
4122         
4123         if (this.disabled) {
4124             return;
4125         }
4126         
4127         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4128         if (tg && tg.transition) {
4129             Roo.log("waiting for the transitionend");
4130             return;
4131         }
4132         
4133         
4134         
4135         //Roo.log("fire event clicked");
4136         if(this.fireEvent('click', this, e) === false){
4137             return;
4138         };
4139         
4140         if(this.tagtype == 'span'){
4141             return;
4142         }
4143         
4144         //Roo.log(this.href);
4145         var ael = this.el.select('a',true).first();
4146         //Roo.log(ael);
4147         
4148         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4149             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4150             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4151                 return; // ignore... - it's a 'hash' to another page.
4152             }
4153             
4154             e.preventDefault();
4155             this.scrollToElement(e);
4156         }
4157         
4158         
4159         var p =  this.parent();
4160    
4161         if (['tabs','pills'].indexOf(p.type)!==-1) {
4162             if (typeof(p.setActiveItem) !== 'undefined') {
4163                 p.setActiveItem(this);
4164             }
4165         }
4166         
4167         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4168         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4169             // remove the collapsed menu expand...
4170             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4171         }
4172     },
4173     
4174     isActive: function () {
4175         return this.active
4176     },
4177     setActive : function(state, fire, is_was_active)
4178     {
4179         if (this.active && !state & this.navId) {
4180             this.was_active = true;
4181             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4182             if (nv) {
4183                 nv.clearWasActive(this);
4184             }
4185             
4186         }
4187         this.active = state;
4188         
4189         if (!state ) {
4190             this.el.removeClass('active');
4191         } else if (!this.el.hasClass('active')) {
4192             this.el.addClass('active');
4193         }
4194         if (fire) {
4195             this.fireEvent('changed', this, state);
4196         }
4197         
4198         // show a panel if it's registered and related..
4199         
4200         if (!this.navId || !this.tabId || !state || is_was_active) {
4201             return;
4202         }
4203         
4204         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4205         if (!tg) {
4206             return;
4207         }
4208         var pan = tg.getPanelByName(this.tabId);
4209         if (!pan) {
4210             return;
4211         }
4212         // if we can not flip to new panel - go back to old nav highlight..
4213         if (false == tg.showPanel(pan)) {
4214             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4215             if (nv) {
4216                 var onav = nv.getWasActive();
4217                 if (onav) {
4218                     onav.setActive(true, false, true);
4219                 }
4220             }
4221             
4222         }
4223         
4224         
4225         
4226     },
4227      // this should not be here...
4228     setDisabled : function(state)
4229     {
4230         this.disabled = state;
4231         if (!state ) {
4232             this.el.removeClass('disabled');
4233         } else if (!this.el.hasClass('disabled')) {
4234             this.el.addClass('disabled');
4235         }
4236         
4237     },
4238     
4239     /**
4240      * Fetch the element to display the tooltip on.
4241      * @return {Roo.Element} defaults to this.el
4242      */
4243     tooltipEl : function()
4244     {
4245         return this.el.select('' + this.tagtype + '', true).first();
4246     },
4247     
4248     scrollToElement : function(e)
4249     {
4250         var c = document.body;
4251         
4252         /*
4253          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4254          */
4255         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4256             c = document.documentElement;
4257         }
4258         
4259         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4260         
4261         if(!target){
4262             return;
4263         }
4264
4265         var o = target.calcOffsetsTo(c);
4266         
4267         var options = {
4268             target : target,
4269             value : o[1]
4270         }
4271         
4272         this.fireEvent('scrollto', this, options, e);
4273         
4274         Roo.get(c).scrollTo('top', options.value, true);
4275         
4276         return;
4277     }
4278 });
4279  
4280
4281  /*
4282  * - LGPL
4283  *
4284  * sidebar item
4285  *
4286  *  li
4287  *    <span> icon </span>
4288  *    <span> text </span>
4289  *    <span>badge </span>
4290  */
4291
4292 /**
4293  * @class Roo.bootstrap.NavSidebarItem
4294  * @extends Roo.bootstrap.NavItem
4295  * Bootstrap Navbar.NavSidebarItem class
4296  * @constructor
4297  * Create a new Navbar Button
4298  * @param {Object} config The config object
4299  */
4300 Roo.bootstrap.NavSidebarItem = function(config){
4301     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4302     this.addEvents({
4303         // raw events
4304         /**
4305          * @event click
4306          * The raw click event for the entire grid.
4307          * @param {Roo.EventObject} e
4308          */
4309         "click" : true,
4310          /**
4311             * @event changed
4312             * Fires when the active item active state changes
4313             * @param {Roo.bootstrap.NavSidebarItem} this
4314             * @param {boolean} state the new state
4315              
4316          */
4317         'changed': true
4318     });
4319    
4320 };
4321
4322 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4323     
4324     
4325     getAutoCreate : function(){
4326         
4327         
4328         var a = {
4329                 tag: 'a',
4330                 href : this.href || '#',
4331                 cls: '',
4332                 html : '',
4333                 cn : []
4334         };
4335         var cfg = {
4336             tag: 'li',
4337             cls: '',
4338             cn: [ a ]
4339         }
4340         var span = {
4341             tag: 'span',
4342             html : this.html || ''
4343         }
4344         
4345         
4346         if (this.active) {
4347             cfg.cls += ' active';
4348         }
4349         
4350         // left icon..
4351         if (this.glyphicon || this.icon) {
4352             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4353             a.cn.push({ tag : 'i', cls : c }) ;
4354         }
4355         // html..
4356         a.cn.push(span);
4357         // then badge..
4358         if (this.badge !== '') {
4359             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4360         }
4361         // fi
4362         if (this.menu) {
4363             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4364             a.cls += 'dropdown-toggle treeview' ;
4365             
4366         }
4367         
4368         
4369         
4370         return cfg;
4371          
4372            
4373     }
4374    
4375      
4376  
4377 });
4378  
4379
4380  /*
4381  * - LGPL
4382  *
4383  * row
4384  * 
4385  */
4386
4387 /**
4388  * @class Roo.bootstrap.Row
4389  * @extends Roo.bootstrap.Component
4390  * Bootstrap Row class (contains columns...)
4391  * 
4392  * @constructor
4393  * Create a new Row
4394  * @param {Object} config The config object
4395  */
4396
4397 Roo.bootstrap.Row = function(config){
4398     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4399 };
4400
4401 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4402     
4403     getAutoCreate : function(){
4404        return {
4405             cls: 'row clearfix'
4406        };
4407     }
4408     
4409     
4410 });
4411
4412  
4413
4414  /*
4415  * - LGPL
4416  *
4417  * element
4418  * 
4419  */
4420
4421 /**
4422  * @class Roo.bootstrap.Element
4423  * @extends Roo.bootstrap.Component
4424  * Bootstrap Element class
4425  * @cfg {String} html contents of the element
4426  * @cfg {String} tag tag of the element
4427  * @cfg {String} cls class of the element
4428  * @cfg {Boolean} preventDefault (true|false) default false
4429  * @cfg {Boolean} clickable (true|false) default false
4430  * 
4431  * @constructor
4432  * Create a new Element
4433  * @param {Object} config The config object
4434  */
4435
4436 Roo.bootstrap.Element = function(config){
4437     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4438     
4439     this.addEvents({
4440         // raw events
4441         /**
4442          * @event click
4443          * When a element is chick
4444          * @param {Roo.bootstrap.Element} this
4445          * @param {Roo.EventObject} e
4446          */
4447         "click" : true
4448     });
4449 };
4450
4451 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4452     
4453     tag: 'div',
4454     cls: '',
4455     html: '',
4456     preventDefault: false, 
4457     clickable: false,
4458     
4459     getAutoCreate : function(){
4460         
4461         var cfg = {
4462             tag: this.tag,
4463             cls: this.cls,
4464             html: this.html
4465         }
4466         
4467         return cfg;
4468     },
4469     
4470     initEvents: function() 
4471     {
4472         Roo.bootstrap.Element.superclass.initEvents.call(this);
4473         
4474         if(this.clickable){
4475             this.el.on('click', this.onClick, this);
4476         }
4477         
4478     },
4479     
4480     onClick : function(e)
4481     {
4482         if(this.preventDefault){
4483             e.preventDefault();
4484         }
4485         
4486         this.fireEvent('click', this, e);
4487     },
4488     
4489     getValue : function()
4490     {
4491         return this.el.dom.innerHTML;
4492     },
4493     
4494     setValue : function(value)
4495     {
4496         this.el.dom.innerHTML = value;
4497     }
4498    
4499 });
4500
4501  
4502
4503  /*
4504  * - LGPL
4505  *
4506  * pagination
4507  * 
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.Pagination
4512  * @extends Roo.bootstrap.Component
4513  * Bootstrap Pagination class
4514  * @cfg {String} size xs | sm | md | lg
4515  * @cfg {Boolean} inverse false | true
4516  * 
4517  * @constructor
4518  * Create a new Pagination
4519  * @param {Object} config The config object
4520  */
4521
4522 Roo.bootstrap.Pagination = function(config){
4523     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4524 };
4525
4526 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4527     
4528     cls: false,
4529     size: false,
4530     inverse: false,
4531     
4532     getAutoCreate : function(){
4533         var cfg = {
4534             tag: 'ul',
4535                 cls: 'pagination'
4536         };
4537         if (this.inverse) {
4538             cfg.cls += ' inverse';
4539         }
4540         if (this.html) {
4541             cfg.html=this.html;
4542         }
4543         if (this.cls) {
4544             cfg.cls += " " + this.cls;
4545         }
4546         return cfg;
4547     }
4548    
4549 });
4550
4551  
4552
4553  /*
4554  * - LGPL
4555  *
4556  * Pagination item
4557  * 
4558  */
4559
4560
4561 /**
4562  * @class Roo.bootstrap.PaginationItem
4563  * @extends Roo.bootstrap.Component
4564  * Bootstrap PaginationItem class
4565  * @cfg {String} html text
4566  * @cfg {String} href the link
4567  * @cfg {Boolean} preventDefault (true | false) default true
4568  * @cfg {Boolean} active (true | false) default false
4569  * @cfg {Boolean} disabled default false
4570  * 
4571  * 
4572  * @constructor
4573  * Create a new PaginationItem
4574  * @param {Object} config The config object
4575  */
4576
4577
4578 Roo.bootstrap.PaginationItem = function(config){
4579     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4580     this.addEvents({
4581         // raw events
4582         /**
4583          * @event click
4584          * The raw click event for the entire grid.
4585          * @param {Roo.EventObject} e
4586          */
4587         "click" : true
4588     });
4589 };
4590
4591 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4592     
4593     href : false,
4594     html : false,
4595     preventDefault: true,
4596     active : false,
4597     cls : false,
4598     disabled: false,
4599     
4600     getAutoCreate : function(){
4601         var cfg= {
4602             tag: 'li',
4603             cn: [
4604                 {
4605                     tag : 'a',
4606                     href : this.href ? this.href : '#',
4607                     html : this.html ? this.html : ''
4608                 }
4609             ]
4610         };
4611         
4612         if(this.cls){
4613             cfg.cls = this.cls;
4614         }
4615         
4616         if(this.disabled){
4617             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4618         }
4619         
4620         if(this.active){
4621             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4622         }
4623         
4624         return cfg;
4625     },
4626     
4627     initEvents: function() {
4628         
4629         this.el.on('click', this.onClick, this);
4630         
4631     },
4632     onClick : function(e)
4633     {
4634         Roo.log('PaginationItem on click ');
4635         if(this.preventDefault){
4636             e.preventDefault();
4637         }
4638         
4639         if(this.disabled){
4640             return;
4641         }
4642         
4643         this.fireEvent('click', this, e);
4644     }
4645    
4646 });
4647
4648  
4649
4650  /*
4651  * - LGPL
4652  *
4653  * slider
4654  * 
4655  */
4656
4657
4658 /**
4659  * @class Roo.bootstrap.Slider
4660  * @extends Roo.bootstrap.Component
4661  * Bootstrap Slider class
4662  *    
4663  * @constructor
4664  * Create a new Slider
4665  * @param {Object} config The config object
4666  */
4667
4668 Roo.bootstrap.Slider = function(config){
4669     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4670 };
4671
4672 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4673     
4674     getAutoCreate : function(){
4675         
4676         var cfg = {
4677             tag: 'div',
4678             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4679             cn: [
4680                 {
4681                     tag: 'a',
4682                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4683                 }
4684             ]
4685         }
4686         
4687         return cfg;
4688     }
4689    
4690 });
4691
4692  /*
4693  * Based on:
4694  * Ext JS Library 1.1.1
4695  * Copyright(c) 2006-2007, Ext JS, LLC.
4696  *
4697  * Originally Released Under LGPL - original licence link has changed is not relivant.
4698  *
4699  * Fork - LGPL
4700  * <script type="text/javascript">
4701  */
4702  
4703
4704 /**
4705  * @class Roo.grid.ColumnModel
4706  * @extends Roo.util.Observable
4707  * This is the default implementation of a ColumnModel used by the Grid. It defines
4708  * the columns in the grid.
4709  * <br>Usage:<br>
4710  <pre><code>
4711  var colModel = new Roo.grid.ColumnModel([
4712         {header: "Ticker", width: 60, sortable: true, locked: true},
4713         {header: "Company Name", width: 150, sortable: true},
4714         {header: "Market Cap.", width: 100, sortable: true},
4715         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4716         {header: "Employees", width: 100, sortable: true, resizable: false}
4717  ]);
4718  </code></pre>
4719  * <p>
4720  
4721  * The config options listed for this class are options which may appear in each
4722  * individual column definition.
4723  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4724  * @constructor
4725  * @param {Object} config An Array of column config objects. See this class's
4726  * config objects for details.
4727 */
4728 Roo.grid.ColumnModel = function(config){
4729         /**
4730      * The config passed into the constructor
4731      */
4732     this.config = config;
4733     this.lookup = {};
4734
4735     // if no id, create one
4736     // if the column does not have a dataIndex mapping,
4737     // map it to the order it is in the config
4738     for(var i = 0, len = config.length; i < len; i++){
4739         var c = config[i];
4740         if(typeof c.dataIndex == "undefined"){
4741             c.dataIndex = i;
4742         }
4743         if(typeof c.renderer == "string"){
4744             c.renderer = Roo.util.Format[c.renderer];
4745         }
4746         if(typeof c.id == "undefined"){
4747             c.id = Roo.id();
4748         }
4749         if(c.editor && c.editor.xtype){
4750             c.editor  = Roo.factory(c.editor, Roo.grid);
4751         }
4752         if(c.editor && c.editor.isFormField){
4753             c.editor = new Roo.grid.GridEditor(c.editor);
4754         }
4755         this.lookup[c.id] = c;
4756     }
4757
4758     /**
4759      * The width of columns which have no width specified (defaults to 100)
4760      * @type Number
4761      */
4762     this.defaultWidth = 100;
4763
4764     /**
4765      * Default sortable of columns which have no sortable specified (defaults to false)
4766      * @type Boolean
4767      */
4768     this.defaultSortable = false;
4769
4770     this.addEvents({
4771         /**
4772              * @event widthchange
4773              * Fires when the width of a column changes.
4774              * @param {ColumnModel} this
4775              * @param {Number} columnIndex The column index
4776              * @param {Number} newWidth The new width
4777              */
4778             "widthchange": true,
4779         /**
4780              * @event headerchange
4781              * Fires when the text of a header changes.
4782              * @param {ColumnModel} this
4783              * @param {Number} columnIndex The column index
4784              * @param {Number} newText The new header text
4785              */
4786             "headerchange": true,
4787         /**
4788              * @event hiddenchange
4789              * Fires when a column is hidden or "unhidden".
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Boolean} hidden true if hidden, false otherwise
4793              */
4794             "hiddenchange": true,
4795             /**
4796          * @event columnmoved
4797          * Fires when a column is moved.
4798          * @param {ColumnModel} this
4799          * @param {Number} oldIndex
4800          * @param {Number} newIndex
4801          */
4802         "columnmoved" : true,
4803         /**
4804          * @event columlockchange
4805          * Fires when a column's locked state is changed
4806          * @param {ColumnModel} this
4807          * @param {Number} colIndex
4808          * @param {Boolean} locked true if locked
4809          */
4810         "columnlockchange" : true
4811     });
4812     Roo.grid.ColumnModel.superclass.constructor.call(this);
4813 };
4814 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4815     /**
4816      * @cfg {String} header The header text to display in the Grid view.
4817      */
4818     /**
4819      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4820      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4821      * specified, the column's index is used as an index into the Record's data Array.
4822      */
4823     /**
4824      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4825      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4826      */
4827     /**
4828      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4829      * Defaults to the value of the {@link #defaultSortable} property.
4830      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4831      */
4832     /**
4833      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4834      */
4835     /**
4836      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4837      */
4838     /**
4839      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4840      */
4841     /**
4842      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4843      */
4844     /**
4845      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4846      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4847      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4848      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4849      */
4850        /**
4851      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4852      */
4853     /**
4854      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4855      */
4856     /**
4857      * @cfg {String} cursor (Optional)
4858      */
4859     /**
4860      * @cfg {String} tooltip (Optional)
4861      */
4862     /**
4863      * Returns the id of the column at the specified index.
4864      * @param {Number} index The column index
4865      * @return {String} the id
4866      */
4867     getColumnId : function(index){
4868         return this.config[index].id;
4869     },
4870
4871     /**
4872      * Returns the column for a specified id.
4873      * @param {String} id The column id
4874      * @return {Object} the column
4875      */
4876     getColumnById : function(id){
4877         return this.lookup[id];
4878     },
4879
4880     
4881     /**
4882      * Returns the column for a specified dataIndex.
4883      * @param {String} dataIndex The column dataIndex
4884      * @return {Object|Boolean} the column or false if not found
4885      */
4886     getColumnByDataIndex: function(dataIndex){
4887         var index = this.findColumnIndex(dataIndex);
4888         return index > -1 ? this.config[index] : false;
4889     },
4890     
4891     /**
4892      * Returns the index for a specified column id.
4893      * @param {String} id The column id
4894      * @return {Number} the index, or -1 if not found
4895      */
4896     getIndexById : function(id){
4897         for(var i = 0, len = this.config.length; i < len; i++){
4898             if(this.config[i].id == id){
4899                 return i;
4900             }
4901         }
4902         return -1;
4903     },
4904     
4905     /**
4906      * Returns the index for a specified column dataIndex.
4907      * @param {String} dataIndex The column dataIndex
4908      * @return {Number} the index, or -1 if not found
4909      */
4910     
4911     findColumnIndex : function(dataIndex){
4912         for(var i = 0, len = this.config.length; i < len; i++){
4913             if(this.config[i].dataIndex == dataIndex){
4914                 return i;
4915             }
4916         }
4917         return -1;
4918     },
4919     
4920     
4921     moveColumn : function(oldIndex, newIndex){
4922         var c = this.config[oldIndex];
4923         this.config.splice(oldIndex, 1);
4924         this.config.splice(newIndex, 0, c);
4925         this.dataMap = null;
4926         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4927     },
4928
4929     isLocked : function(colIndex){
4930         return this.config[colIndex].locked === true;
4931     },
4932
4933     setLocked : function(colIndex, value, suppressEvent){
4934         if(this.isLocked(colIndex) == value){
4935             return;
4936         }
4937         this.config[colIndex].locked = value;
4938         if(!suppressEvent){
4939             this.fireEvent("columnlockchange", this, colIndex, value);
4940         }
4941     },
4942
4943     getTotalLockedWidth : function(){
4944         var totalWidth = 0;
4945         for(var i = 0; i < this.config.length; i++){
4946             if(this.isLocked(i) && !this.isHidden(i)){
4947                 this.totalWidth += this.getColumnWidth(i);
4948             }
4949         }
4950         return totalWidth;
4951     },
4952
4953     getLockedCount : function(){
4954         for(var i = 0, len = this.config.length; i < len; i++){
4955             if(!this.isLocked(i)){
4956                 return i;
4957             }
4958         }
4959     },
4960
4961     /**
4962      * Returns the number of columns.
4963      * @return {Number}
4964      */
4965     getColumnCount : function(visibleOnly){
4966         if(visibleOnly === true){
4967             var c = 0;
4968             for(var i = 0, len = this.config.length; i < len; i++){
4969                 if(!this.isHidden(i)){
4970                     c++;
4971                 }
4972             }
4973             return c;
4974         }
4975         return this.config.length;
4976     },
4977
4978     /**
4979      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4980      * @param {Function} fn
4981      * @param {Object} scope (optional)
4982      * @return {Array} result
4983      */
4984     getColumnsBy : function(fn, scope){
4985         var r = [];
4986         for(var i = 0, len = this.config.length; i < len; i++){
4987             var c = this.config[i];
4988             if(fn.call(scope||this, c, i) === true){
4989                 r[r.length] = c;
4990             }
4991         }
4992         return r;
4993     },
4994
4995     /**
4996      * Returns true if the specified column is sortable.
4997      * @param {Number} col The column index
4998      * @return {Boolean}
4999      */
5000     isSortable : function(col){
5001         if(typeof this.config[col].sortable == "undefined"){
5002             return this.defaultSortable;
5003         }
5004         return this.config[col].sortable;
5005     },
5006
5007     /**
5008      * Returns the rendering (formatting) function defined for the column.
5009      * @param {Number} col The column index.
5010      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5011      */
5012     getRenderer : function(col){
5013         if(!this.config[col].renderer){
5014             return Roo.grid.ColumnModel.defaultRenderer;
5015         }
5016         return this.config[col].renderer;
5017     },
5018
5019     /**
5020      * Sets the rendering (formatting) function for a column.
5021      * @param {Number} col The column index
5022      * @param {Function} fn The function to use to process the cell's raw data
5023      * to return HTML markup for the grid view. The render function is called with
5024      * the following parameters:<ul>
5025      * <li>Data value.</li>
5026      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5027      * <li>css A CSS style string to apply to the table cell.</li>
5028      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5029      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5030      * <li>Row index</li>
5031      * <li>Column index</li>
5032      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5033      */
5034     setRenderer : function(col, fn){
5035         this.config[col].renderer = fn;
5036     },
5037
5038     /**
5039      * Returns the width for the specified column.
5040      * @param {Number} col The column index
5041      * @return {Number}
5042      */
5043     getColumnWidth : function(col){
5044         return this.config[col].width * 1 || this.defaultWidth;
5045     },
5046
5047     /**
5048      * Sets the width for a column.
5049      * @param {Number} col The column index
5050      * @param {Number} width The new width
5051      */
5052     setColumnWidth : function(col, width, suppressEvent){
5053         this.config[col].width = width;
5054         this.totalWidth = null;
5055         if(!suppressEvent){
5056              this.fireEvent("widthchange", this, col, width);
5057         }
5058     },
5059
5060     /**
5061      * Returns the total width of all columns.
5062      * @param {Boolean} includeHidden True to include hidden column widths
5063      * @return {Number}
5064      */
5065     getTotalWidth : function(includeHidden){
5066         if(!this.totalWidth){
5067             this.totalWidth = 0;
5068             for(var i = 0, len = this.config.length; i < len; i++){
5069                 if(includeHidden || !this.isHidden(i)){
5070                     this.totalWidth += this.getColumnWidth(i);
5071                 }
5072             }
5073         }
5074         return this.totalWidth;
5075     },
5076
5077     /**
5078      * Returns the header for the specified column.
5079      * @param {Number} col The column index
5080      * @return {String}
5081      */
5082     getColumnHeader : function(col){
5083         return this.config[col].header;
5084     },
5085
5086     /**
5087      * Sets the header for a column.
5088      * @param {Number} col The column index
5089      * @param {String} header The new header
5090      */
5091     setColumnHeader : function(col, header){
5092         this.config[col].header = header;
5093         this.fireEvent("headerchange", this, col, header);
5094     },
5095
5096     /**
5097      * Returns the tooltip for the specified column.
5098      * @param {Number} col The column index
5099      * @return {String}
5100      */
5101     getColumnTooltip : function(col){
5102             return this.config[col].tooltip;
5103     },
5104     /**
5105      * Sets the tooltip for a column.
5106      * @param {Number} col The column index
5107      * @param {String} tooltip The new tooltip
5108      */
5109     setColumnTooltip : function(col, tooltip){
5110             this.config[col].tooltip = tooltip;
5111     },
5112
5113     /**
5114      * Returns the dataIndex for the specified column.
5115      * @param {Number} col The column index
5116      * @return {Number}
5117      */
5118     getDataIndex : function(col){
5119         return this.config[col].dataIndex;
5120     },
5121
5122     /**
5123      * Sets the dataIndex for a column.
5124      * @param {Number} col The column index
5125      * @param {Number} dataIndex The new dataIndex
5126      */
5127     setDataIndex : function(col, dataIndex){
5128         this.config[col].dataIndex = dataIndex;
5129     },
5130
5131     
5132     
5133     /**
5134      * Returns true if the cell is editable.
5135      * @param {Number} colIndex The column index
5136      * @param {Number} rowIndex The row index
5137      * @return {Boolean}
5138      */
5139     isCellEditable : function(colIndex, rowIndex){
5140         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5141     },
5142
5143     /**
5144      * Returns the editor defined for the cell/column.
5145      * return false or null to disable editing.
5146      * @param {Number} colIndex The column index
5147      * @param {Number} rowIndex The row index
5148      * @return {Object}
5149      */
5150     getCellEditor : function(colIndex, rowIndex){
5151         return this.config[colIndex].editor;
5152     },
5153
5154     /**
5155      * Sets if a column is editable.
5156      * @param {Number} col The column index
5157      * @param {Boolean} editable True if the column is editable
5158      */
5159     setEditable : function(col, editable){
5160         this.config[col].editable = editable;
5161     },
5162
5163
5164     /**
5165      * Returns true if the column is hidden.
5166      * @param {Number} colIndex The column index
5167      * @return {Boolean}
5168      */
5169     isHidden : function(colIndex){
5170         return this.config[colIndex].hidden;
5171     },
5172
5173
5174     /**
5175      * Returns true if the column width cannot be changed
5176      */
5177     isFixed : function(colIndex){
5178         return this.config[colIndex].fixed;
5179     },
5180
5181     /**
5182      * Returns true if the column can be resized
5183      * @return {Boolean}
5184      */
5185     isResizable : function(colIndex){
5186         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5187     },
5188     /**
5189      * Sets if a column is hidden.
5190      * @param {Number} colIndex The column index
5191      * @param {Boolean} hidden True if the column is hidden
5192      */
5193     setHidden : function(colIndex, hidden){
5194         this.config[colIndex].hidden = hidden;
5195         this.totalWidth = null;
5196         this.fireEvent("hiddenchange", this, colIndex, hidden);
5197     },
5198
5199     /**
5200      * Sets the editor for a column.
5201      * @param {Number} col The column index
5202      * @param {Object} editor The editor object
5203      */
5204     setEditor : function(col, editor){
5205         this.config[col].editor = editor;
5206     }
5207 });
5208
5209 Roo.grid.ColumnModel.defaultRenderer = function(value){
5210         if(typeof value == "string" && value.length < 1){
5211             return "&#160;";
5212         }
5213         return value;
5214 };
5215
5216 // Alias for backwards compatibility
5217 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5218 /*
5219  * Based on:
5220  * Ext JS Library 1.1.1
5221  * Copyright(c) 2006-2007, Ext JS, LLC.
5222  *
5223  * Originally Released Under LGPL - original licence link has changed is not relivant.
5224  *
5225  * Fork - LGPL
5226  * <script type="text/javascript">
5227  */
5228  
5229 /**
5230  * @class Roo.LoadMask
5231  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5232  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5233  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5234  * element's UpdateManager load indicator and will be destroyed after the initial load.
5235  * @constructor
5236  * Create a new LoadMask
5237  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5238  * @param {Object} config The config object
5239  */
5240 Roo.LoadMask = function(el, config){
5241     this.el = Roo.get(el);
5242     Roo.apply(this, config);
5243     if(this.store){
5244         this.store.on('beforeload', this.onBeforeLoad, this);
5245         this.store.on('load', this.onLoad, this);
5246         this.store.on('loadexception', this.onLoadException, this);
5247         this.removeMask = false;
5248     }else{
5249         var um = this.el.getUpdateManager();
5250         um.showLoadIndicator = false; // disable the default indicator
5251         um.on('beforeupdate', this.onBeforeLoad, this);
5252         um.on('update', this.onLoad, this);
5253         um.on('failure', this.onLoad, this);
5254         this.removeMask = true;
5255     }
5256 };
5257
5258 Roo.LoadMask.prototype = {
5259     /**
5260      * @cfg {Boolean} removeMask
5261      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5262      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5263      */
5264     /**
5265      * @cfg {String} msg
5266      * The text to display in a centered loading message box (defaults to 'Loading...')
5267      */
5268     msg : 'Loading...',
5269     /**
5270      * @cfg {String} msgCls
5271      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5272      */
5273     msgCls : 'x-mask-loading',
5274
5275     /**
5276      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5277      * @type Boolean
5278      */
5279     disabled: false,
5280
5281     /**
5282      * Disables the mask to prevent it from being displayed
5283      */
5284     disable : function(){
5285        this.disabled = true;
5286     },
5287
5288     /**
5289      * Enables the mask so that it can be displayed
5290      */
5291     enable : function(){
5292         this.disabled = false;
5293     },
5294     
5295     onLoadException : function()
5296     {
5297         Roo.log(arguments);
5298         
5299         if (typeof(arguments[3]) != 'undefined') {
5300             Roo.MessageBox.alert("Error loading",arguments[3]);
5301         } 
5302         /*
5303         try {
5304             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5305                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5306             }   
5307         } catch(e) {
5308             
5309         }
5310         */
5311     
5312         
5313         
5314         this.el.unmask(this.removeMask);
5315     },
5316     // private
5317     onLoad : function()
5318     {
5319         this.el.unmask(this.removeMask);
5320     },
5321
5322     // private
5323     onBeforeLoad : function(){
5324         if(!this.disabled){
5325             this.el.mask(this.msg, this.msgCls);
5326         }
5327     },
5328
5329     // private
5330     destroy : function(){
5331         if(this.store){
5332             this.store.un('beforeload', this.onBeforeLoad, this);
5333             this.store.un('load', this.onLoad, this);
5334             this.store.un('loadexception', this.onLoadException, this);
5335         }else{
5336             var um = this.el.getUpdateManager();
5337             um.un('beforeupdate', this.onBeforeLoad, this);
5338             um.un('update', this.onLoad, this);
5339             um.un('failure', this.onLoad, this);
5340         }
5341     }
5342 };/*
5343  * - LGPL
5344  *
5345  * table
5346  * 
5347  */
5348
5349 /**
5350  * @class Roo.bootstrap.Table
5351  * @extends Roo.bootstrap.Component
5352  * Bootstrap Table class
5353  * @cfg {String} cls table class
5354  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5355  * @cfg {String} bgcolor Specifies the background color for a table
5356  * @cfg {Number} border Specifies whether the table cells should have borders or not
5357  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5358  * @cfg {Number} cellspacing Specifies the space between cells
5359  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5360  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5361  * @cfg {String} sortable Specifies that the table should be sortable
5362  * @cfg {String} summary Specifies a summary of the content of a table
5363  * @cfg {Number} width Specifies the width of a table
5364  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5365  * 
5366  * @cfg {boolean} striped Should the rows be alternative striped
5367  * @cfg {boolean} bordered Add borders to the table
5368  * @cfg {boolean} hover Add hover highlighting
5369  * @cfg {boolean} condensed Format condensed
5370  * @cfg {boolean} responsive Format condensed
5371  * @cfg {Boolean} loadMask (true|false) default false
5372  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5373  * @cfg {Boolean} thead (true|false) generate thead, default true
5374  * @cfg {Boolean} RowSelection (true|false) default false
5375  * @cfg {Boolean} CellSelection (true|false) default false
5376  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5377  
5378  * 
5379  * @constructor
5380  * Create a new Table
5381  * @param {Object} config The config object
5382  */
5383
5384 Roo.bootstrap.Table = function(config){
5385     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5386     
5387     if (this.sm) {
5388         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5389         this.sm = this.selModel;
5390         this.sm.xmodule = this.xmodule || false;
5391     }
5392     if (this.cm && typeof(this.cm.config) == 'undefined') {
5393         this.colModel = new Roo.grid.ColumnModel(this.cm);
5394         this.cm = this.colModel;
5395         this.cm.xmodule = this.xmodule || false;
5396     }
5397     if (this.store) {
5398         this.store= Roo.factory(this.store, Roo.data);
5399         this.ds = this.store;
5400         this.ds.xmodule = this.xmodule || false;
5401          
5402     }
5403     if (this.footer && this.store) {
5404         this.footer.dataSource = this.ds;
5405         this.footer = Roo.factory(this.footer);
5406     }
5407     
5408     /** @private */
5409     this.addEvents({
5410         /**
5411          * @event cellclick
5412          * Fires when a cell is clicked
5413          * @param {Roo.bootstrap.Table} this
5414          * @param {Roo.Element} el
5415          * @param {Number} rowIndex
5416          * @param {Number} columnIndex
5417          * @param {Roo.EventObject} e
5418          */
5419         "cellclick" : true,
5420         /**
5421          * @event celldblclick
5422          * Fires when a cell is double clicked
5423          * @param {Roo.bootstrap.Table} this
5424          * @param {Roo.Element} el
5425          * @param {Number} rowIndex
5426          * @param {Number} columnIndex
5427          * @param {Roo.EventObject} e
5428          */
5429         "celldblclick" : true,
5430         /**
5431          * @event rowclick
5432          * Fires when a row is clicked
5433          * @param {Roo.bootstrap.Table} this
5434          * @param {Roo.Element} el
5435          * @param {Number} rowIndex
5436          * @param {Roo.EventObject} e
5437          */
5438         "rowclick" : true,
5439         /**
5440          * @event rowdblclick
5441          * Fires when a row is double clicked
5442          * @param {Roo.bootstrap.Table} this
5443          * @param {Roo.Element} el
5444          * @param {Number} rowIndex
5445          * @param {Roo.EventObject} e
5446          */
5447         "rowdblclick" : true,
5448         /**
5449          * @event mouseover
5450          * Fires when a mouseover occur
5451          * @param {Roo.bootstrap.Table} this
5452          * @param {Roo.Element} el
5453          * @param {Number} rowIndex
5454          * @param {Number} columnIndex
5455          * @param {Roo.EventObject} e
5456          */
5457         "mouseover" : true,
5458         /**
5459          * @event mouseout
5460          * Fires when a mouseout occur
5461          * @param {Roo.bootstrap.Table} this
5462          * @param {Roo.Element} el
5463          * @param {Number} rowIndex
5464          * @param {Number} columnIndex
5465          * @param {Roo.EventObject} e
5466          */
5467         "mouseout" : true,
5468         /**
5469          * @event rowclass
5470          * Fires when a row is rendered, so you can change add a style to it.
5471          * @param {Roo.bootstrap.Table} this
5472          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5473          */
5474         'rowclass' : true,
5475           /**
5476          * @event rowsrendered
5477          * Fires when all the  rows have been rendered
5478          * @param {Roo.bootstrap.Table} this
5479          */
5480         'rowsrendered' : true
5481         
5482     });
5483 };
5484
5485 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5486     
5487     cls: false,
5488     align: false,
5489     bgcolor: false,
5490     border: false,
5491     cellpadding: false,
5492     cellspacing: false,
5493     frame: false,
5494     rules: false,
5495     sortable: false,
5496     summary: false,
5497     width: false,
5498     striped : false,
5499     bordered: false,
5500     hover:  false,
5501     condensed : false,
5502     responsive : false,
5503     sm : false,
5504     cm : false,
5505     store : false,
5506     loadMask : false,
5507     tfoot : true,
5508     thead : true,
5509     RowSelection : false,
5510     CellSelection : false,
5511     layout : false,
5512     
5513     // Roo.Element - the tbody
5514     mainBody: false, 
5515     
5516     getAutoCreate : function(){
5517         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5518         
5519         cfg = {
5520             tag: 'table',
5521             cls : 'table',
5522             cn : []
5523         }
5524             
5525         if (this.striped) {
5526             cfg.cls += ' table-striped';
5527         }
5528         
5529         if (this.hover) {
5530             cfg.cls += ' table-hover';
5531         }
5532         if (this.bordered) {
5533             cfg.cls += ' table-bordered';
5534         }
5535         if (this.condensed) {
5536             cfg.cls += ' table-condensed';
5537         }
5538         if (this.responsive) {
5539             cfg.cls += ' table-responsive';
5540         }
5541         
5542         if (this.cls) {
5543             cfg.cls+=  ' ' +this.cls;
5544         }
5545         
5546         // this lot should be simplifed...
5547         
5548         if (this.align) {
5549             cfg.align=this.align;
5550         }
5551         if (this.bgcolor) {
5552             cfg.bgcolor=this.bgcolor;
5553         }
5554         if (this.border) {
5555             cfg.border=this.border;
5556         }
5557         if (this.cellpadding) {
5558             cfg.cellpadding=this.cellpadding;
5559         }
5560         if (this.cellspacing) {
5561             cfg.cellspacing=this.cellspacing;
5562         }
5563         if (this.frame) {
5564             cfg.frame=this.frame;
5565         }
5566         if (this.rules) {
5567             cfg.rules=this.rules;
5568         }
5569         if (this.sortable) {
5570             cfg.sortable=this.sortable;
5571         }
5572         if (this.summary) {
5573             cfg.summary=this.summary;
5574         }
5575         if (this.width) {
5576             cfg.width=this.width;
5577         }
5578         if (this.layout) {
5579             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5580         }
5581         
5582         if(this.store || this.cm){
5583             if(this.thead){
5584                 cfg.cn.push(this.renderHeader());
5585             }
5586             
5587             cfg.cn.push(this.renderBody());
5588             
5589             if(this.tfoot){
5590                 cfg.cn.push(this.renderFooter());
5591             }
5592             
5593             cfg.cls+=  ' TableGrid';
5594         }
5595         
5596         return { cn : [ cfg ] };
5597     },
5598     
5599     initEvents : function()
5600     {   
5601         if(!this.store || !this.cm){
5602             return;
5603         }
5604         
5605         //Roo.log('initEvents with ds!!!!');
5606         
5607         this.mainBody = this.el.select('tbody', true).first();
5608         
5609         
5610         var _this = this;
5611         
5612         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5613             e.on('click', _this.sort, _this);
5614         });
5615         
5616         this.el.on("click", this.onClick, this);
5617         this.el.on("dblclick", this.onDblClick, this);
5618         
5619         // why is this done????? = it breaks dialogs??
5620         //this.parent().el.setStyle('position', 'relative');
5621         
5622         
5623         if (this.footer) {
5624             this.footer.parentId = this.id;
5625             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5626         }
5627         
5628         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5629         
5630         this.store.on('load', this.onLoad, this);
5631         this.store.on('beforeload', this.onBeforeLoad, this);
5632         this.store.on('update', this.onUpdate, this);
5633         this.store.on('add', this.onAdd, this);
5634         
5635     },
5636     
5637     onMouseover : function(e, el)
5638     {
5639         var cell = Roo.get(el);
5640         
5641         if(!cell){
5642             return;
5643         }
5644         
5645         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5646             cell = cell.findParent('td', false, true);
5647         }
5648         
5649         var row = cell.findParent('tr', false, true);
5650         var cellIndex = cell.dom.cellIndex;
5651         var rowIndex = row.dom.rowIndex - 1; // start from 0
5652         
5653         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5654         
5655     },
5656     
5657     onMouseout : function(e, el)
5658     {
5659         var cell = Roo.get(el);
5660         
5661         if(!cell){
5662             return;
5663         }
5664         
5665         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5666             cell = cell.findParent('td', false, true);
5667         }
5668         
5669         var row = cell.findParent('tr', false, true);
5670         var cellIndex = cell.dom.cellIndex;
5671         var rowIndex = row.dom.rowIndex - 1; // start from 0
5672         
5673         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5674         
5675     },
5676     
5677     onClick : function(e, el)
5678     {
5679         var cell = Roo.get(el);
5680         
5681         if(!cell || (!this.CellSelection && !this.RowSelection)){
5682             return;
5683         }
5684         
5685         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5686             cell = cell.findParent('td', false, true);
5687         }
5688         
5689         if(!cell || typeof(cell) == 'undefined'){
5690             return;
5691         }
5692         
5693         var row = cell.findParent('tr', false, true);
5694         
5695         if(!row || typeof(row) == 'undefined'){
5696             return;
5697         }
5698         
5699         var cellIndex = cell.dom.cellIndex;
5700         var rowIndex = this.getRowIndex(row);
5701         
5702         if(this.CellSelection){
5703             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5704         }
5705         
5706         if(this.RowSelection){
5707             this.fireEvent('rowclick', this, row, rowIndex, e);
5708         }
5709         
5710         
5711     },
5712     
5713     onDblClick : function(e,el)
5714     {
5715         var cell = Roo.get(el);
5716         
5717         if(!cell || (!this.CellSelection && !this.RowSelection)){
5718             return;
5719         }
5720         
5721         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5722             cell = cell.findParent('td', false, true);
5723         }
5724         
5725         if(!cell || typeof(cell) == 'undefined'){
5726             return;
5727         }
5728         
5729         var row = cell.findParent('tr', false, true);
5730         
5731         if(!row || typeof(row) == 'undefined'){
5732             return;
5733         }
5734         
5735         var cellIndex = cell.dom.cellIndex;
5736         var rowIndex = this.getRowIndex(row);
5737         
5738         if(this.CellSelection){
5739             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5740         }
5741         
5742         if(this.RowSelection){
5743             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5744         }
5745     },
5746     
5747     sort : function(e,el)
5748     {
5749         var col = Roo.get(el);
5750         
5751         if(!col.hasClass('sortable')){
5752             return;
5753         }
5754         
5755         var sort = col.attr('sort');
5756         var dir = 'ASC';
5757         
5758         if(col.hasClass('glyphicon-arrow-up')){
5759             dir = 'DESC';
5760         }
5761         
5762         this.store.sortInfo = {field : sort, direction : dir};
5763         
5764         if (this.footer) {
5765             Roo.log("calling footer first");
5766             this.footer.onClick('first');
5767         } else {
5768         
5769             this.store.load({ params : { start : 0 } });
5770         }
5771     },
5772     
5773     renderHeader : function()
5774     {
5775         var header = {
5776             tag: 'thead',
5777             cn : []
5778         };
5779         
5780         var cm = this.cm;
5781         
5782         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5783             
5784             var config = cm.config[i];
5785                     
5786             var c = {
5787                 tag: 'th',
5788                 style : '',
5789                 html: cm.getColumnHeader(i)
5790             };
5791             
5792             if(typeof(config.tooltip) != 'undefined'){
5793                 c.tooltip = config.tooltip;
5794             }
5795             
5796             if(typeof(config.colspan) != 'undefined'){
5797                 c.colspan = config.colspan;
5798             }
5799             
5800             if(typeof(config.hidden) != 'undefined' && config.hidden){
5801                 c.style += ' display:none;';
5802             }
5803             
5804             if(typeof(config.dataIndex) != 'undefined'){
5805                 c.sort = config.dataIndex;
5806             }
5807             
5808             if(typeof(config.sortable) != 'undefined' && config.sortable){
5809                 c.cls = 'sortable';
5810             }
5811             
5812             if(typeof(config.align) != 'undefined' && config.align.length){
5813                 c.style += ' text-align:' + config.align + ';';
5814             }
5815             
5816             if(typeof(config.width) != 'undefined'){
5817                 c.style += ' width:' + config.width + 'px;';
5818             }
5819             
5820             if(typeof(config.cls) != 'undefined'){
5821                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5822             }
5823             
5824             header.cn.push(c)
5825         }
5826         
5827         return header;
5828     },
5829     
5830     renderBody : function()
5831     {
5832         var body = {
5833             tag: 'tbody',
5834             cn : [
5835                 {
5836                     tag: 'tr',
5837                     cn : [
5838                         {
5839                             tag : 'td',
5840                             colspan :  this.cm.getColumnCount()
5841                         }
5842                     ]
5843                 }
5844             ]
5845         };
5846         
5847         return body;
5848     },
5849     
5850     renderFooter : function()
5851     {
5852         var footer = {
5853             tag: 'tfoot',
5854             cn : [
5855                 {
5856                     tag: 'tr',
5857                     cn : [
5858                         {
5859                             tag : 'td',
5860                             colspan :  this.cm.getColumnCount()
5861                         }
5862                     ]
5863                 }
5864             ]
5865         };
5866         
5867         return footer;
5868     },
5869     
5870     
5871     
5872     onLoad : function()
5873     {
5874         Roo.log('ds onload');
5875         this.clear();
5876         
5877         var _this = this;
5878         var cm = this.cm;
5879         var ds = this.store;
5880         
5881         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5882             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5883             
5884             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5885                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5886             }
5887             
5888             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5889                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5890             }
5891         });
5892         
5893         var tbody =  this.mainBody;
5894               
5895         if(ds.getCount() > 0){
5896             ds.data.each(function(d,rowIndex){
5897                 var row =  this.renderRow(cm, ds, rowIndex);
5898                 
5899                 tbody.createChild(row);
5900                 
5901                 var _this = this;
5902                 
5903                 if(row.cellObjects.length){
5904                     Roo.each(row.cellObjects, function(r){
5905                         _this.renderCellObject(r);
5906                     })
5907                 }
5908                 
5909             }, this);
5910         }
5911         
5912         Roo.each(this.el.select('tbody td', true).elements, function(e){
5913             e.on('mouseover', _this.onMouseover, _this);
5914         });
5915         
5916         Roo.each(this.el.select('tbody td', true).elements, function(e){
5917             e.on('mouseout', _this.onMouseout, _this);
5918         });
5919         this.fireEvent('rowsrendered', this);
5920         //if(this.loadMask){
5921         //    this.maskEl.hide();
5922         //}
5923     },
5924     
5925     
5926     onUpdate : function(ds,record)
5927     {
5928         this.refreshRow(record);
5929     },
5930     
5931     onRemove : function(ds, record, index, isUpdate){
5932         if(isUpdate !== true){
5933             this.fireEvent("beforerowremoved", this, index, record);
5934         }
5935         var bt = this.mainBody.dom;
5936         
5937         var rows = this.el.select('tbody > tr', true).elements;
5938         
5939         if(typeof(rows[index]) != 'undefined'){
5940             bt.removeChild(rows[index].dom);
5941         }
5942         
5943 //        if(bt.rows[index]){
5944 //            bt.removeChild(bt.rows[index]);
5945 //        }
5946         
5947         if(isUpdate !== true){
5948             //this.stripeRows(index);
5949             //this.syncRowHeights(index, index);
5950             //this.layout();
5951             this.fireEvent("rowremoved", this, index, record);
5952         }
5953     },
5954     
5955     onAdd : function(ds, records, rowIndex)
5956     {
5957         //Roo.log('on Add called');
5958         // - note this does not handle multiple adding very well..
5959         var bt = this.mainBody.dom;
5960         for (var i =0 ; i < records.length;i++) {
5961             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5962             //Roo.log(records[i]);
5963             //Roo.log(this.store.getAt(rowIndex+i));
5964             this.insertRow(this.store, rowIndex + i, false);
5965             return;
5966         }
5967         
5968     },
5969     
5970     
5971     refreshRow : function(record){
5972         var ds = this.store, index;
5973         if(typeof record == 'number'){
5974             index = record;
5975             record = ds.getAt(index);
5976         }else{
5977             index = ds.indexOf(record);
5978         }
5979         this.insertRow(ds, index, true);
5980         this.onRemove(ds, record, index+1, true);
5981         //this.syncRowHeights(index, index);
5982         //this.layout();
5983         this.fireEvent("rowupdated", this, index, record);
5984     },
5985     
5986     insertRow : function(dm, rowIndex, isUpdate){
5987         
5988         if(!isUpdate){
5989             this.fireEvent("beforerowsinserted", this, rowIndex);
5990         }
5991             //var s = this.getScrollState();
5992         var row = this.renderRow(this.cm, this.store, rowIndex);
5993         // insert before rowIndex..
5994         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5995         
5996         var _this = this;
5997                 
5998         if(row.cellObjects.length){
5999             Roo.each(row.cellObjects, function(r){
6000                 _this.renderCellObject(r);
6001             })
6002         }
6003             
6004         if(!isUpdate){
6005             this.fireEvent("rowsinserted", this, rowIndex);
6006             //this.syncRowHeights(firstRow, lastRow);
6007             //this.stripeRows(firstRow);
6008             //this.layout();
6009         }
6010         
6011     },
6012     
6013     
6014     getRowDom : function(rowIndex)
6015     {
6016         var rows = this.el.select('tbody > tr', true).elements;
6017         
6018         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6019         
6020     },
6021     // returns the object tree for a tr..
6022   
6023     
6024     renderRow : function(cm, ds, rowIndex) 
6025     {
6026         
6027         var d = ds.getAt(rowIndex);
6028         
6029         var row = {
6030             tag : 'tr',
6031             cn : []
6032         };
6033             
6034         var cellObjects = [];
6035         
6036         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6037             var config = cm.config[i];
6038             
6039             var renderer = cm.getRenderer(i);
6040             var value = '';
6041             var id = false;
6042             
6043             if(typeof(renderer) !== 'undefined'){
6044                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6045             }
6046             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6047             // and are rendered into the cells after the row is rendered - using the id for the element.
6048             
6049             if(typeof(value) === 'object'){
6050                 id = Roo.id();
6051                 cellObjects.push({
6052                     container : id,
6053                     cfg : value 
6054                 })
6055             }
6056             
6057             var rowcfg = {
6058                 record: d,
6059                 rowIndex : rowIndex,
6060                 colIndex : i,
6061                 rowClass : ''
6062             }
6063
6064             this.fireEvent('rowclass', this, rowcfg);
6065             
6066             var td = {
6067                 tag: 'td',
6068                 cls : rowcfg.rowClass,
6069                 style: '',
6070                 html: (typeof(value) === 'object') ? '' : value
6071             };
6072             
6073             if (id) {
6074                 td.id = id;
6075             }
6076             
6077             if(typeof(config.colspan) != 'undefined'){
6078                 td.colspan = config.colspan;
6079             }
6080             
6081             if(typeof(config.hidden) != 'undefined' && config.hidden){
6082                 td.style += ' display:none;';
6083             }
6084             
6085             if(typeof(config.align) != 'undefined' && config.align.length){
6086                 td.style += ' text-align:' + config.align + ';';
6087             }
6088             
6089             if(typeof(config.width) != 'undefined'){
6090                 td.style += ' width:' +  config.width + 'px;';
6091             }
6092             
6093             if(typeof(config.cursor) != 'undefined'){
6094                 td.style += ' cursor:' +  config.cursor + ';';
6095             }
6096             
6097             if(typeof(config.cls) != 'undefined'){
6098                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6099             }
6100              
6101             row.cn.push(td);
6102            
6103         }
6104         
6105         row.cellObjects = cellObjects;
6106         
6107         return row;
6108           
6109     },
6110     
6111     
6112     
6113     onBeforeLoad : function()
6114     {
6115         //Roo.log('ds onBeforeLoad');
6116         
6117         //this.clear();
6118         
6119         //if(this.loadMask){
6120         //    this.maskEl.show();
6121         //}
6122     },
6123      /**
6124      * Remove all rows
6125      */
6126     clear : function()
6127     {
6128         this.el.select('tbody', true).first().dom.innerHTML = '';
6129     },
6130     /**
6131      * Show or hide a row.
6132      * @param {Number} rowIndex to show or hide
6133      * @param {Boolean} state hide
6134      */
6135     setRowVisibility : function(rowIndex, state)
6136     {
6137         var bt = this.mainBody.dom;
6138         
6139         var rows = this.el.select('tbody > tr', true).elements;
6140         
6141         if(typeof(rows[rowIndex]) == 'undefined'){
6142             return;
6143         }
6144         rows[rowIndex].dom.style.display = state ? '' : 'none';
6145     },
6146     
6147     
6148     getSelectionModel : function(){
6149         if(!this.selModel){
6150             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6151         }
6152         return this.selModel;
6153     },
6154     /*
6155      * Render the Roo.bootstrap object from renderder
6156      */
6157     renderCellObject : function(r)
6158     {
6159         var _this = this;
6160         
6161         var t = r.cfg.render(r.container);
6162         
6163         if(r.cfg.cn){
6164             Roo.each(r.cfg.cn, function(c){
6165                 var child = {
6166                     container: t.getChildContainer(),
6167                     cfg: c
6168                 }
6169                 _this.renderCellObject(child);
6170             })
6171         }
6172     },
6173     
6174     getRowIndex : function(row)
6175     {
6176         var rowIndex = -1;
6177         
6178         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6179             if(el != row){
6180                 return;
6181             }
6182             
6183             rowIndex = index;
6184         });
6185         
6186         return rowIndex;
6187     }
6188    
6189 });
6190
6191  
6192
6193  /*
6194  * - LGPL
6195  *
6196  * table cell
6197  * 
6198  */
6199
6200 /**
6201  * @class Roo.bootstrap.TableCell
6202  * @extends Roo.bootstrap.Component
6203  * Bootstrap TableCell class
6204  * @cfg {String} html cell contain text
6205  * @cfg {String} cls cell class
6206  * @cfg {String} tag cell tag (td|th) default td
6207  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6208  * @cfg {String} align Aligns the content in a cell
6209  * @cfg {String} axis Categorizes cells
6210  * @cfg {String} bgcolor Specifies the background color of a cell
6211  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6212  * @cfg {Number} colspan Specifies the number of columns a cell should span
6213  * @cfg {String} headers Specifies one or more header cells a cell is related to
6214  * @cfg {Number} height Sets the height of a cell
6215  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6216  * @cfg {Number} rowspan Sets the number of rows a cell should span
6217  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6218  * @cfg {String} valign Vertical aligns the content in a cell
6219  * @cfg {Number} width Specifies the width of a cell
6220  * 
6221  * @constructor
6222  * Create a new TableCell
6223  * @param {Object} config The config object
6224  */
6225
6226 Roo.bootstrap.TableCell = function(config){
6227     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6228 };
6229
6230 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6231     
6232     html: false,
6233     cls: false,
6234     tag: false,
6235     abbr: false,
6236     align: false,
6237     axis: false,
6238     bgcolor: false,
6239     charoff: false,
6240     colspan: false,
6241     headers: false,
6242     height: false,
6243     nowrap: false,
6244     rowspan: false,
6245     scope: false,
6246     valign: false,
6247     width: false,
6248     
6249     
6250     getAutoCreate : function(){
6251         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6252         
6253         cfg = {
6254             tag: 'td'
6255         }
6256         
6257         if(this.tag){
6258             cfg.tag = this.tag;
6259         }
6260         
6261         if (this.html) {
6262             cfg.html=this.html
6263         }
6264         if (this.cls) {
6265             cfg.cls=this.cls
6266         }
6267         if (this.abbr) {
6268             cfg.abbr=this.abbr
6269         }
6270         if (this.align) {
6271             cfg.align=this.align
6272         }
6273         if (this.axis) {
6274             cfg.axis=this.axis
6275         }
6276         if (this.bgcolor) {
6277             cfg.bgcolor=this.bgcolor
6278         }
6279         if (this.charoff) {
6280             cfg.charoff=this.charoff
6281         }
6282         if (this.colspan) {
6283             cfg.colspan=this.colspan
6284         }
6285         if (this.headers) {
6286             cfg.headers=this.headers
6287         }
6288         if (this.height) {
6289             cfg.height=this.height
6290         }
6291         if (this.nowrap) {
6292             cfg.nowrap=this.nowrap
6293         }
6294         if (this.rowspan) {
6295             cfg.rowspan=this.rowspan
6296         }
6297         if (this.scope) {
6298             cfg.scope=this.scope
6299         }
6300         if (this.valign) {
6301             cfg.valign=this.valign
6302         }
6303         if (this.width) {
6304             cfg.width=this.width
6305         }
6306         
6307         
6308         return cfg;
6309     }
6310    
6311 });
6312
6313  
6314
6315  /*
6316  * - LGPL
6317  *
6318  * table row
6319  * 
6320  */
6321
6322 /**
6323  * @class Roo.bootstrap.TableRow
6324  * @extends Roo.bootstrap.Component
6325  * Bootstrap TableRow class
6326  * @cfg {String} cls row class
6327  * @cfg {String} align Aligns the content in a table row
6328  * @cfg {String} bgcolor Specifies a background color for a table row
6329  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6330  * @cfg {String} valign Vertical aligns the content in a table row
6331  * 
6332  * @constructor
6333  * Create a new TableRow
6334  * @param {Object} config The config object
6335  */
6336
6337 Roo.bootstrap.TableRow = function(config){
6338     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6339 };
6340
6341 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6342     
6343     cls: false,
6344     align: false,
6345     bgcolor: false,
6346     charoff: false,
6347     valign: false,
6348     
6349     getAutoCreate : function(){
6350         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6351         
6352         cfg = {
6353             tag: 'tr'
6354         }
6355             
6356         if(this.cls){
6357             cfg.cls = this.cls;
6358         }
6359         if(this.align){
6360             cfg.align = this.align;
6361         }
6362         if(this.bgcolor){
6363             cfg.bgcolor = this.bgcolor;
6364         }
6365         if(this.charoff){
6366             cfg.charoff = this.charoff;
6367         }
6368         if(this.valign){
6369             cfg.valign = this.valign;
6370         }
6371         
6372         return cfg;
6373     }
6374    
6375 });
6376
6377  
6378
6379  /*
6380  * - LGPL
6381  *
6382  * table body
6383  * 
6384  */
6385
6386 /**
6387  * @class Roo.bootstrap.TableBody
6388  * @extends Roo.bootstrap.Component
6389  * Bootstrap TableBody class
6390  * @cfg {String} cls element class
6391  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6392  * @cfg {String} align Aligns the content inside the element
6393  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6394  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6395  * 
6396  * @constructor
6397  * Create a new TableBody
6398  * @param {Object} config The config object
6399  */
6400
6401 Roo.bootstrap.TableBody = function(config){
6402     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6403 };
6404
6405 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6406     
6407     cls: false,
6408     tag: false,
6409     align: false,
6410     charoff: false,
6411     valign: false,
6412     
6413     getAutoCreate : function(){
6414         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6415         
6416         cfg = {
6417             tag: 'tbody'
6418         }
6419             
6420         if (this.cls) {
6421             cfg.cls=this.cls
6422         }
6423         if(this.tag){
6424             cfg.tag = this.tag;
6425         }
6426         
6427         if(this.align){
6428             cfg.align = this.align;
6429         }
6430         if(this.charoff){
6431             cfg.charoff = this.charoff;
6432         }
6433         if(this.valign){
6434             cfg.valign = this.valign;
6435         }
6436         
6437         return cfg;
6438     }
6439     
6440     
6441 //    initEvents : function()
6442 //    {
6443 //        
6444 //        if(!this.store){
6445 //            return;
6446 //        }
6447 //        
6448 //        this.store = Roo.factory(this.store, Roo.data);
6449 //        this.store.on('load', this.onLoad, this);
6450 //        
6451 //        this.store.load();
6452 //        
6453 //    },
6454 //    
6455 //    onLoad: function () 
6456 //    {   
6457 //        this.fireEvent('load', this);
6458 //    }
6459 //    
6460 //   
6461 });
6462
6463  
6464
6465  /*
6466  * Based on:
6467  * Ext JS Library 1.1.1
6468  * Copyright(c) 2006-2007, Ext JS, LLC.
6469  *
6470  * Originally Released Under LGPL - original licence link has changed is not relivant.
6471  *
6472  * Fork - LGPL
6473  * <script type="text/javascript">
6474  */
6475
6476 // as we use this in bootstrap.
6477 Roo.namespace('Roo.form');
6478  /**
6479  * @class Roo.form.Action
6480  * Internal Class used to handle form actions
6481  * @constructor
6482  * @param {Roo.form.BasicForm} el The form element or its id
6483  * @param {Object} config Configuration options
6484  */
6485
6486  
6487  
6488 // define the action interface
6489 Roo.form.Action = function(form, options){
6490     this.form = form;
6491     this.options = options || {};
6492 };
6493 /**
6494  * Client Validation Failed
6495  * @const 
6496  */
6497 Roo.form.Action.CLIENT_INVALID = 'client';
6498 /**
6499  * Server Validation Failed
6500  * @const 
6501  */
6502 Roo.form.Action.SERVER_INVALID = 'server';
6503  /**
6504  * Connect to Server Failed
6505  * @const 
6506  */
6507 Roo.form.Action.CONNECT_FAILURE = 'connect';
6508 /**
6509  * Reading Data from Server Failed
6510  * @const 
6511  */
6512 Roo.form.Action.LOAD_FAILURE = 'load';
6513
6514 Roo.form.Action.prototype = {
6515     type : 'default',
6516     failureType : undefined,
6517     response : undefined,
6518     result : undefined,
6519
6520     // interface method
6521     run : function(options){
6522
6523     },
6524
6525     // interface method
6526     success : function(response){
6527
6528     },
6529
6530     // interface method
6531     handleResponse : function(response){
6532
6533     },
6534
6535     // default connection failure
6536     failure : function(response){
6537         
6538         this.response = response;
6539         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6540         this.form.afterAction(this, false);
6541     },
6542
6543     processResponse : function(response){
6544         this.response = response;
6545         if(!response.responseText){
6546             return true;
6547         }
6548         this.result = this.handleResponse(response);
6549         return this.result;
6550     },
6551
6552     // utility functions used internally
6553     getUrl : function(appendParams){
6554         var url = this.options.url || this.form.url || this.form.el.dom.action;
6555         if(appendParams){
6556             var p = this.getParams();
6557             if(p){
6558                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6559             }
6560         }
6561         return url;
6562     },
6563
6564     getMethod : function(){
6565         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6566     },
6567
6568     getParams : function(){
6569         var bp = this.form.baseParams;
6570         var p = this.options.params;
6571         if(p){
6572             if(typeof p == "object"){
6573                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6574             }else if(typeof p == 'string' && bp){
6575                 p += '&' + Roo.urlEncode(bp);
6576             }
6577         }else if(bp){
6578             p = Roo.urlEncode(bp);
6579         }
6580         return p;
6581     },
6582
6583     createCallback : function(){
6584         return {
6585             success: this.success,
6586             failure: this.failure,
6587             scope: this,
6588             timeout: (this.form.timeout*1000),
6589             upload: this.form.fileUpload ? this.success : undefined
6590         };
6591     }
6592 };
6593
6594 Roo.form.Action.Submit = function(form, options){
6595     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6596 };
6597
6598 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6599     type : 'submit',
6600
6601     haveProgress : false,
6602     uploadComplete : false,
6603     
6604     // uploadProgress indicator.
6605     uploadProgress : function()
6606     {
6607         if (!this.form.progressUrl) {
6608             return;
6609         }
6610         
6611         if (!this.haveProgress) {
6612             Roo.MessageBox.progress("Uploading", "Uploading");
6613         }
6614         if (this.uploadComplete) {
6615            Roo.MessageBox.hide();
6616            return;
6617         }
6618         
6619         this.haveProgress = true;
6620    
6621         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6622         
6623         var c = new Roo.data.Connection();
6624         c.request({
6625             url : this.form.progressUrl,
6626             params: {
6627                 id : uid
6628             },
6629             method: 'GET',
6630             success : function(req){
6631                //console.log(data);
6632                 var rdata = false;
6633                 var edata;
6634                 try  {
6635                    rdata = Roo.decode(req.responseText)
6636                 } catch (e) {
6637                     Roo.log("Invalid data from server..");
6638                     Roo.log(edata);
6639                     return;
6640                 }
6641                 if (!rdata || !rdata.success) {
6642                     Roo.log(rdata);
6643                     Roo.MessageBox.alert(Roo.encode(rdata));
6644                     return;
6645                 }
6646                 var data = rdata.data;
6647                 
6648                 if (this.uploadComplete) {
6649                    Roo.MessageBox.hide();
6650                    return;
6651                 }
6652                    
6653                 if (data){
6654                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6655                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6656                     );
6657                 }
6658                 this.uploadProgress.defer(2000,this);
6659             },
6660        
6661             failure: function(data) {
6662                 Roo.log('progress url failed ');
6663                 Roo.log(data);
6664             },
6665             scope : this
6666         });
6667            
6668     },
6669     
6670     
6671     run : function()
6672     {
6673         // run get Values on the form, so it syncs any secondary forms.
6674         this.form.getValues();
6675         
6676         var o = this.options;
6677         var method = this.getMethod();
6678         var isPost = method == 'POST';
6679         if(o.clientValidation === false || this.form.isValid()){
6680             
6681             if (this.form.progressUrl) {
6682                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6683                     (new Date() * 1) + '' + Math.random());
6684                     
6685             } 
6686             
6687             
6688             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6689                 form:this.form.el.dom,
6690                 url:this.getUrl(!isPost),
6691                 method: method,
6692                 params:isPost ? this.getParams() : null,
6693                 isUpload: this.form.fileUpload
6694             }));
6695             
6696             this.uploadProgress();
6697
6698         }else if (o.clientValidation !== false){ // client validation failed
6699             this.failureType = Roo.form.Action.CLIENT_INVALID;
6700             this.form.afterAction(this, false);
6701         }
6702     },
6703
6704     success : function(response)
6705     {
6706         this.uploadComplete= true;
6707         if (this.haveProgress) {
6708             Roo.MessageBox.hide();
6709         }
6710         
6711         
6712         var result = this.processResponse(response);
6713         if(result === true || result.success){
6714             this.form.afterAction(this, true);
6715             return;
6716         }
6717         if(result.errors){
6718             this.form.markInvalid(result.errors);
6719             this.failureType = Roo.form.Action.SERVER_INVALID;
6720         }
6721         this.form.afterAction(this, false);
6722     },
6723     failure : function(response)
6724     {
6725         this.uploadComplete= true;
6726         if (this.haveProgress) {
6727             Roo.MessageBox.hide();
6728         }
6729         
6730         this.response = response;
6731         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6732         this.form.afterAction(this, false);
6733     },
6734     
6735     handleResponse : function(response){
6736         if(this.form.errorReader){
6737             var rs = this.form.errorReader.read(response);
6738             var errors = [];
6739             if(rs.records){
6740                 for(var i = 0, len = rs.records.length; i < len; i++) {
6741                     var r = rs.records[i];
6742                     errors[i] = r.data;
6743                 }
6744             }
6745             if(errors.length < 1){
6746                 errors = null;
6747             }
6748             return {
6749                 success : rs.success,
6750                 errors : errors
6751             };
6752         }
6753         var ret = false;
6754         try {
6755             ret = Roo.decode(response.responseText);
6756         } catch (e) {
6757             ret = {
6758                 success: false,
6759                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6760                 errors : []
6761             };
6762         }
6763         return ret;
6764         
6765     }
6766 });
6767
6768
6769 Roo.form.Action.Load = function(form, options){
6770     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6771     this.reader = this.form.reader;
6772 };
6773
6774 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6775     type : 'load',
6776
6777     run : function(){
6778         
6779         Roo.Ajax.request(Roo.apply(
6780                 this.createCallback(), {
6781                     method:this.getMethod(),
6782                     url:this.getUrl(false),
6783                     params:this.getParams()
6784         }));
6785     },
6786
6787     success : function(response){
6788         
6789         var result = this.processResponse(response);
6790         if(result === true || !result.success || !result.data){
6791             this.failureType = Roo.form.Action.LOAD_FAILURE;
6792             this.form.afterAction(this, false);
6793             return;
6794         }
6795         this.form.clearInvalid();
6796         this.form.setValues(result.data);
6797         this.form.afterAction(this, true);
6798     },
6799
6800     handleResponse : function(response){
6801         if(this.form.reader){
6802             var rs = this.form.reader.read(response);
6803             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6804             return {
6805                 success : rs.success,
6806                 data : data
6807             };
6808         }
6809         return Roo.decode(response.responseText);
6810     }
6811 });
6812
6813 Roo.form.Action.ACTION_TYPES = {
6814     'load' : Roo.form.Action.Load,
6815     'submit' : Roo.form.Action.Submit
6816 };/*
6817  * - LGPL
6818  *
6819  * form
6820  * 
6821  */
6822
6823 /**
6824  * @class Roo.bootstrap.Form
6825  * @extends Roo.bootstrap.Component
6826  * Bootstrap Form class
6827  * @cfg {String} method  GET | POST (default POST)
6828  * @cfg {String} labelAlign top | left (default top)
6829  * @cfg {String} align left  | right - for navbars
6830  * @cfg {Boolean} loadMask load mask when submit (default true)
6831
6832  * 
6833  * @constructor
6834  * Create a new Form
6835  * @param {Object} config The config object
6836  */
6837
6838
6839 Roo.bootstrap.Form = function(config){
6840     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6841     this.addEvents({
6842         /**
6843          * @event clientvalidation
6844          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6845          * @param {Form} this
6846          * @param {Boolean} valid true if the form has passed client-side validation
6847          */
6848         clientvalidation: true,
6849         /**
6850          * @event beforeaction
6851          * Fires before any action is performed. Return false to cancel the action.
6852          * @param {Form} this
6853          * @param {Action} action The action to be performed
6854          */
6855         beforeaction: true,
6856         /**
6857          * @event actionfailed
6858          * Fires when an action fails.
6859          * @param {Form} this
6860          * @param {Action} action The action that failed
6861          */
6862         actionfailed : true,
6863         /**
6864          * @event actioncomplete
6865          * Fires when an action is completed.
6866          * @param {Form} this
6867          * @param {Action} action The action that completed
6868          */
6869         actioncomplete : true
6870     });
6871     
6872 };
6873
6874 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6875       
6876      /**
6877      * @cfg {String} method
6878      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6879      */
6880     method : 'POST',
6881     /**
6882      * @cfg {String} url
6883      * The URL to use for form actions if one isn't supplied in the action options.
6884      */
6885     /**
6886      * @cfg {Boolean} fileUpload
6887      * Set to true if this form is a file upload.
6888      */
6889      
6890     /**
6891      * @cfg {Object} baseParams
6892      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6893      */
6894       
6895     /**
6896      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6897      */
6898     timeout: 30,
6899     /**
6900      * @cfg {Sting} align (left|right) for navbar forms
6901      */
6902     align : 'left',
6903
6904     // private
6905     activeAction : null,
6906  
6907     /**
6908      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6909      * element by passing it or its id or mask the form itself by passing in true.
6910      * @type Mixed
6911      */
6912     waitMsgTarget : false,
6913     
6914     loadMask : true,
6915     
6916     getAutoCreate : function(){
6917         
6918         var cfg = {
6919             tag: 'form',
6920             method : this.method || 'POST',
6921             id : this.id || Roo.id(),
6922             cls : ''
6923         }
6924         if (this.parent().xtype.match(/^Nav/)) {
6925             cfg.cls = 'navbar-form navbar-' + this.align;
6926             
6927         }
6928         
6929         if (this.labelAlign == 'left' ) {
6930             cfg.cls += ' form-horizontal';
6931         }
6932         
6933         
6934         return cfg;
6935     },
6936     initEvents : function()
6937     {
6938         this.el.on('submit', this.onSubmit, this);
6939         // this was added as random key presses on the form where triggering form submit.
6940         this.el.on('keypress', function(e) {
6941             if (e.getCharCode() != 13) {
6942                 return true;
6943             }
6944             // we might need to allow it for textareas.. and some other items.
6945             // check e.getTarget().
6946             
6947             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6948                 return true;
6949             }
6950         
6951             Roo.log("keypress blocked");
6952             
6953             e.preventDefault();
6954             return false;
6955         });
6956         
6957     },
6958     // private
6959     onSubmit : function(e){
6960         e.stopEvent();
6961     },
6962     
6963      /**
6964      * Returns true if client-side validation on the form is successful.
6965      * @return Boolean
6966      */
6967     isValid : function(){
6968         var items = this.getItems();
6969         var valid = true;
6970         items.each(function(f){
6971            if(!f.validate()){
6972                valid = false;
6973                
6974            }
6975         });
6976         return valid;
6977     },
6978     /**
6979      * Returns true if any fields in this form have changed since their original load.
6980      * @return Boolean
6981      */
6982     isDirty : function(){
6983         var dirty = false;
6984         var items = this.getItems();
6985         items.each(function(f){
6986            if(f.isDirty()){
6987                dirty = true;
6988                return false;
6989            }
6990            return true;
6991         });
6992         return dirty;
6993     },
6994      /**
6995      * Performs a predefined action (submit or load) or custom actions you define on this form.
6996      * @param {String} actionName The name of the action type
6997      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6998      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6999      * accept other config options):
7000      * <pre>
7001 Property          Type             Description
7002 ----------------  ---------------  ----------------------------------------------------------------------------------
7003 url               String           The url for the action (defaults to the form's url)
7004 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7005 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7006 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7007                                    validate the form on the client (defaults to false)
7008      * </pre>
7009      * @return {BasicForm} this
7010      */
7011     doAction : function(action, options){
7012         if(typeof action == 'string'){
7013             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7014         }
7015         if(this.fireEvent('beforeaction', this, action) !== false){
7016             this.beforeAction(action);
7017             action.run.defer(100, action);
7018         }
7019         return this;
7020     },
7021     
7022     // private
7023     beforeAction : function(action){
7024         var o = action.options;
7025         
7026         if(this.loadMask){
7027             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7028         }
7029         // not really supported yet.. ??
7030         
7031         //if(this.waitMsgTarget === true){
7032         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7033         //}else if(this.waitMsgTarget){
7034         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7035         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7036         //}else {
7037         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7038        // }
7039          
7040     },
7041
7042     // private
7043     afterAction : function(action, success){
7044         this.activeAction = null;
7045         var o = action.options;
7046         
7047         //if(this.waitMsgTarget === true){
7048             this.el.unmask();
7049         //}else if(this.waitMsgTarget){
7050         //    this.waitMsgTarget.unmask();
7051         //}else{
7052         //    Roo.MessageBox.updateProgress(1);
7053         //    Roo.MessageBox.hide();
7054        // }
7055         // 
7056         if(success){
7057             if(o.reset){
7058                 this.reset();
7059             }
7060             Roo.callback(o.success, o.scope, [this, action]);
7061             this.fireEvent('actioncomplete', this, action);
7062             
7063         }else{
7064             
7065             // failure condition..
7066             // we have a scenario where updates need confirming.
7067             // eg. if a locking scenario exists..
7068             // we look for { errors : { needs_confirm : true }} in the response.
7069             if (
7070                 (typeof(action.result) != 'undefined')  &&
7071                 (typeof(action.result.errors) != 'undefined')  &&
7072                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7073            ){
7074                 var _t = this;
7075                 Roo.log("not supported yet");
7076                  /*
7077                 
7078                 Roo.MessageBox.confirm(
7079                     "Change requires confirmation",
7080                     action.result.errorMsg,
7081                     function(r) {
7082                         if (r != 'yes') {
7083                             return;
7084                         }
7085                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7086                     }
7087                     
7088                 );
7089                 */
7090                 
7091                 
7092                 return;
7093             }
7094             
7095             Roo.callback(o.failure, o.scope, [this, action]);
7096             // show an error message if no failed handler is set..
7097             if (!this.hasListener('actionfailed')) {
7098                 Roo.log("need to add dialog support");
7099                 /*
7100                 Roo.MessageBox.alert("Error",
7101                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7102                         action.result.errorMsg :
7103                         "Saving Failed, please check your entries or try again"
7104                 );
7105                 */
7106             }
7107             
7108             this.fireEvent('actionfailed', this, action);
7109         }
7110         
7111     },
7112     /**
7113      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7114      * @param {String} id The value to search for
7115      * @return Field
7116      */
7117     findField : function(id){
7118         var items = this.getItems();
7119         var field = items.get(id);
7120         if(!field){
7121              items.each(function(f){
7122                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7123                     field = f;
7124                     return false;
7125                 }
7126                 return true;
7127             });
7128         }
7129         return field || null;
7130     },
7131      /**
7132      * Mark fields in this form invalid in bulk.
7133      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7134      * @return {BasicForm} this
7135      */
7136     markInvalid : function(errors){
7137         if(errors instanceof Array){
7138             for(var i = 0, len = errors.length; i < len; i++){
7139                 var fieldError = errors[i];
7140                 var f = this.findField(fieldError.id);
7141                 if(f){
7142                     f.markInvalid(fieldError.msg);
7143                 }
7144             }
7145         }else{
7146             var field, id;
7147             for(id in errors){
7148                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7149                     field.markInvalid(errors[id]);
7150                 }
7151             }
7152         }
7153         //Roo.each(this.childForms || [], function (f) {
7154         //    f.markInvalid(errors);
7155         //});
7156         
7157         return this;
7158     },
7159
7160     /**
7161      * Set values for fields in this form in bulk.
7162      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7163      * @return {BasicForm} this
7164      */
7165     setValues : function(values){
7166         if(values instanceof Array){ // array of objects
7167             for(var i = 0, len = values.length; i < len; i++){
7168                 var v = values[i];
7169                 var f = this.findField(v.id);
7170                 if(f){
7171                     f.setValue(v.value);
7172                     if(this.trackResetOnLoad){
7173                         f.originalValue = f.getValue();
7174                     }
7175                 }
7176             }
7177         }else{ // object hash
7178             var field, id;
7179             for(id in values){
7180                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7181                     
7182                     if (field.setFromData && 
7183                         field.valueField && 
7184                         field.displayField &&
7185                         // combos' with local stores can 
7186                         // be queried via setValue()
7187                         // to set their value..
7188                         (field.store && !field.store.isLocal)
7189                         ) {
7190                         // it's a combo
7191                         var sd = { };
7192                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7193                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7194                         field.setFromData(sd);
7195                         
7196                     } else {
7197                         field.setValue(values[id]);
7198                     }
7199                     
7200                     
7201                     if(this.trackResetOnLoad){
7202                         field.originalValue = field.getValue();
7203                     }
7204                 }
7205             }
7206         }
7207          
7208         //Roo.each(this.childForms || [], function (f) {
7209         //    f.setValues(values);
7210         //});
7211                 
7212         return this;
7213     },
7214
7215     /**
7216      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7217      * they are returned as an array.
7218      * @param {Boolean} asString
7219      * @return {Object}
7220      */
7221     getValues : function(asString){
7222         //if (this.childForms) {
7223             // copy values from the child forms
7224         //    Roo.each(this.childForms, function (f) {
7225         //        this.setValues(f.getValues());
7226         //    }, this);
7227         //}
7228         
7229         
7230         
7231         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7232         if(asString === true){
7233             return fs;
7234         }
7235         return Roo.urlDecode(fs);
7236     },
7237     
7238     /**
7239      * Returns the fields in this form as an object with key/value pairs. 
7240      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7241      * @return {Object}
7242      */
7243     getFieldValues : function(with_hidden)
7244     {
7245         var items = this.getItems();
7246         var ret = {};
7247         items.each(function(f){
7248             if (!f.getName()) {
7249                 return;
7250             }
7251             var v = f.getValue();
7252             if (f.inputType =='radio') {
7253                 if (typeof(ret[f.getName()]) == 'undefined') {
7254                     ret[f.getName()] = ''; // empty..
7255                 }
7256                 
7257                 if (!f.el.dom.checked) {
7258                     return;
7259                     
7260                 }
7261                 v = f.el.dom.value;
7262                 
7263             }
7264             
7265             // not sure if this supported any more..
7266             if ((typeof(v) == 'object') && f.getRawValue) {
7267                 v = f.getRawValue() ; // dates..
7268             }
7269             // combo boxes where name != hiddenName...
7270             if (f.name != f.getName()) {
7271                 ret[f.name] = f.getRawValue();
7272             }
7273             ret[f.getName()] = v;
7274         });
7275         
7276         return ret;
7277     },
7278
7279     /**
7280      * Clears all invalid messages in this form.
7281      * @return {BasicForm} this
7282      */
7283     clearInvalid : function(){
7284         var items = this.getItems();
7285         
7286         items.each(function(f){
7287            f.clearInvalid();
7288         });
7289         
7290         
7291         
7292         return this;
7293     },
7294
7295     /**
7296      * Resets this form.
7297      * @return {BasicForm} this
7298      */
7299     reset : function(){
7300         var items = this.getItems();
7301         items.each(function(f){
7302             f.reset();
7303         });
7304         
7305         Roo.each(this.childForms || [], function (f) {
7306             f.reset();
7307         });
7308        
7309         
7310         return this;
7311     },
7312     getItems : function()
7313     {
7314         var r=new Roo.util.MixedCollection(false, function(o){
7315             return o.id || (o.id = Roo.id());
7316         });
7317         var iter = function(el) {
7318             if (el.inputEl) {
7319                 r.add(el);
7320             }
7321             if (!el.items) {
7322                 return;
7323             }
7324             Roo.each(el.items,function(e) {
7325                 iter(e);
7326             });
7327             
7328             
7329         };
7330         
7331         iter(this);
7332         return r;
7333         
7334         
7335         
7336         
7337     }
7338     
7339 });
7340
7341  
7342 /*
7343  * Based on:
7344  * Ext JS Library 1.1.1
7345  * Copyright(c) 2006-2007, Ext JS, LLC.
7346  *
7347  * Originally Released Under LGPL - original licence link has changed is not relivant.
7348  *
7349  * Fork - LGPL
7350  * <script type="text/javascript">
7351  */
7352 /**
7353  * @class Roo.form.VTypes
7354  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7355  * @singleton
7356  */
7357 Roo.form.VTypes = function(){
7358     // closure these in so they are only created once.
7359     var alpha = /^[a-zA-Z_]+$/;
7360     var alphanum = /^[a-zA-Z0-9_]+$/;
7361     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7362     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7363
7364     // All these messages and functions are configurable
7365     return {
7366         /**
7367          * The function used to validate email addresses
7368          * @param {String} value The email address
7369          */
7370         'email' : function(v){
7371             return email.test(v);
7372         },
7373         /**
7374          * The error text to display when the email validation function returns false
7375          * @type String
7376          */
7377         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7378         /**
7379          * The keystroke filter mask to be applied on email input
7380          * @type RegExp
7381          */
7382         'emailMask' : /[a-z0-9_\.\-@]/i,
7383
7384         /**
7385          * The function used to validate URLs
7386          * @param {String} value The URL
7387          */
7388         'url' : function(v){
7389             return url.test(v);
7390         },
7391         /**
7392          * The error text to display when the url validation function returns false
7393          * @type String
7394          */
7395         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7396         
7397         /**
7398          * The function used to validate alpha values
7399          * @param {String} value The value
7400          */
7401         'alpha' : function(v){
7402             return alpha.test(v);
7403         },
7404         /**
7405          * The error text to display when the alpha validation function returns false
7406          * @type String
7407          */
7408         'alphaText' : 'This field should only contain letters and _',
7409         /**
7410          * The keystroke filter mask to be applied on alpha input
7411          * @type RegExp
7412          */
7413         'alphaMask' : /[a-z_]/i,
7414
7415         /**
7416          * The function used to validate alphanumeric values
7417          * @param {String} value The value
7418          */
7419         'alphanum' : function(v){
7420             return alphanum.test(v);
7421         },
7422         /**
7423          * The error text to display when the alphanumeric validation function returns false
7424          * @type String
7425          */
7426         'alphanumText' : 'This field should only contain letters, numbers and _',
7427         /**
7428          * The keystroke filter mask to be applied on alphanumeric input
7429          * @type RegExp
7430          */
7431         'alphanumMask' : /[a-z0-9_]/i
7432     };
7433 }();/*
7434  * - LGPL
7435  *
7436  * Input
7437  * 
7438  */
7439
7440 /**
7441  * @class Roo.bootstrap.Input
7442  * @extends Roo.bootstrap.Component
7443  * Bootstrap Input class
7444  * @cfg {Boolean} disabled is it disabled
7445  * @cfg {String} fieldLabel - the label associated
7446  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7447  * @cfg {String} name name of the input
7448  * @cfg {string} fieldLabel - the label associated
7449  * @cfg {string}  inputType - input / file submit ...
7450  * @cfg {string} placeholder - placeholder to put in text.
7451  * @cfg {string}  before - input group add on before
7452  * @cfg {string} after - input group add on after
7453  * @cfg {string} size - (lg|sm) or leave empty..
7454  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7455  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7456  * @cfg {Number} md colspan out of 12 for computer-sized screens
7457  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7458  * @cfg {string} value default value of the input
7459  * @cfg {Number} labelWidth set the width of label (0-12)
7460  * @cfg {String} labelAlign (top|left)
7461  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7462  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7463
7464  * @cfg {String} align (left|center|right) Default left
7465  * 
7466  * 
7467  * 
7468  * @constructor
7469  * Create a new Input
7470  * @param {Object} config The config object
7471  */
7472
7473 Roo.bootstrap.Input = function(config){
7474     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7475    
7476         this.addEvents({
7477             /**
7478              * @event focus
7479              * Fires when this field receives input focus.
7480              * @param {Roo.form.Field} this
7481              */
7482             focus : true,
7483             /**
7484              * @event blur
7485              * Fires when this field loses input focus.
7486              * @param {Roo.form.Field} this
7487              */
7488             blur : true,
7489             /**
7490              * @event specialkey
7491              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7492              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7493              * @param {Roo.form.Field} this
7494              * @param {Roo.EventObject} e The event object
7495              */
7496             specialkey : true,
7497             /**
7498              * @event change
7499              * Fires just before the field blurs if the field value has changed.
7500              * @param {Roo.form.Field} this
7501              * @param {Mixed} newValue The new value
7502              * @param {Mixed} oldValue The original value
7503              */
7504             change : true,
7505             /**
7506              * @event invalid
7507              * Fires after the field has been marked as invalid.
7508              * @param {Roo.form.Field} this
7509              * @param {String} msg The validation message
7510              */
7511             invalid : true,
7512             /**
7513              * @event valid
7514              * Fires after the field has been validated with no errors.
7515              * @param {Roo.form.Field} this
7516              */
7517             valid : true,
7518              /**
7519              * @event keyup
7520              * Fires after the key up
7521              * @param {Roo.form.Field} this
7522              * @param {Roo.EventObject}  e The event Object
7523              */
7524             keyup : true
7525         });
7526 };
7527
7528 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7529      /**
7530      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7531       automatic validation (defaults to "keyup").
7532      */
7533     validationEvent : "keyup",
7534      /**
7535      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7536      */
7537     validateOnBlur : true,
7538     /**
7539      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7540      */
7541     validationDelay : 250,
7542      /**
7543      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7544      */
7545     focusClass : "x-form-focus",  // not needed???
7546     
7547        
7548     /**
7549      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7550      */
7551     invalidClass : "has-warning",
7552     
7553     /**
7554      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7555      */
7556     validClass : "has-success",
7557     
7558     /**
7559      * @cfg {Boolean} hasFeedback (true|false) default true
7560      */
7561     hasFeedback : true,
7562     
7563     /**
7564      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7565      */
7566     invalidFeedbackClass : "glyphicon-warning-sign",
7567     
7568     /**
7569      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7570      */
7571     validFeedbackClass : "glyphicon-ok",
7572     
7573     /**
7574      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7575      */
7576     selectOnFocus : false,
7577     
7578      /**
7579      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7580      */
7581     maskRe : null,
7582        /**
7583      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7584      */
7585     vtype : null,
7586     
7587       /**
7588      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7589      */
7590     disableKeyFilter : false,
7591     
7592        /**
7593      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7594      */
7595     disabled : false,
7596      /**
7597      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7598      */
7599     allowBlank : true,
7600     /**
7601      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7602      */
7603     blankText : "This field is required",
7604     
7605      /**
7606      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7607      */
7608     minLength : 0,
7609     /**
7610      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7611      */
7612     maxLength : Number.MAX_VALUE,
7613     /**
7614      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7615      */
7616     minLengthText : "The minimum length for this field is {0}",
7617     /**
7618      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7619      */
7620     maxLengthText : "The maximum length for this field is {0}",
7621   
7622     
7623     /**
7624      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7625      * If available, this function will be called only after the basic validators all return true, and will be passed the
7626      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7627      */
7628     validator : null,
7629     /**
7630      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7631      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7632      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7633      */
7634     regex : null,
7635     /**
7636      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7637      */
7638     regexText : "",
7639     
7640     autocomplete: false,
7641     
7642     
7643     fieldLabel : '',
7644     inputType : 'text',
7645     
7646     name : false,
7647     placeholder: false,
7648     before : false,
7649     after : false,
7650     size : false,
7651     hasFocus : false,
7652     preventMark: false,
7653     isFormField : true,
7654     value : '',
7655     labelWidth : 2,
7656     labelAlign : false,
7657     readOnly : false,
7658     align : false,
7659     formatedValue : false,
7660     
7661     parentLabelAlign : function()
7662     {
7663         var parent = this;
7664         while (parent.parent()) {
7665             parent = parent.parent();
7666             if (typeof(parent.labelAlign) !='undefined') {
7667                 return parent.labelAlign;
7668             }
7669         }
7670         return 'left';
7671         
7672     },
7673     
7674     getAutoCreate : function(){
7675         
7676         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7677         
7678         var id = Roo.id();
7679         
7680         var cfg = {};
7681         
7682         if(this.inputType != 'hidden'){
7683             cfg.cls = 'form-group' //input-group
7684         }
7685         
7686         var input =  {
7687             tag: 'input',
7688             id : id,
7689             type : this.inputType,
7690             value : this.value,
7691             cls : 'form-control',
7692             placeholder : this.placeholder || '',
7693             autocomplete : this.autocomplete || 'new-password'
7694         };
7695         
7696         
7697         if(this.align){
7698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7699         }
7700         
7701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7702             input.maxLength = this.maxLength;
7703         }
7704         
7705         if (this.disabled) {
7706             input.disabled=true;
7707         }
7708         
7709         if (this.readOnly) {
7710             input.readonly=true;
7711         }
7712         
7713         if (this.name) {
7714             input.name = this.name;
7715         }
7716         if (this.size) {
7717             input.cls += ' input-' + this.size;
7718         }
7719         var settings=this;
7720         ['xs','sm','md','lg'].map(function(size){
7721             if (settings[size]) {
7722                 cfg.cls += ' col-' + size + '-' + settings[size];
7723             }
7724         });
7725         
7726         var inputblock = input;
7727         
7728         var feedback = {
7729             tag: 'span',
7730             cls: 'glyphicon form-control-feedback'
7731         };
7732             
7733         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7734             
7735             inputblock = {
7736                 cls : 'has-feedback',
7737                 cn :  [
7738                     input,
7739                     feedback
7740                 ] 
7741             };  
7742         }
7743         
7744         if (this.before || this.after) {
7745             
7746             inputblock = {
7747                 cls : 'input-group',
7748                 cn :  [] 
7749             };
7750             
7751             if (this.before && typeof(this.before) == 'string') {
7752                 
7753                 inputblock.cn.push({
7754                     tag :'span',
7755                     cls : 'roo-input-before input-group-addon',
7756                     html : this.before
7757                 });
7758             }
7759             if (this.before && typeof(this.before) == 'object') {
7760                 this.before = Roo.factory(this.before);
7761                 Roo.log(this.before);
7762                 inputblock.cn.push({
7763                     tag :'span',
7764                     cls : 'roo-input-before input-group-' +
7765                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7766                 });
7767             }
7768             
7769             inputblock.cn.push(input);
7770             
7771             if (this.after && typeof(this.after) == 'string') {
7772                 inputblock.cn.push({
7773                     tag :'span',
7774                     cls : 'roo-input-after input-group-addon',
7775                     html : this.after
7776                 });
7777             }
7778             if (this.after && typeof(this.after) == 'object') {
7779                 this.after = Roo.factory(this.after);
7780                 Roo.log(this.after);
7781                 inputblock.cn.push({
7782                     tag :'span',
7783                     cls : 'roo-input-after input-group-' +
7784                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7785                 });
7786             }
7787             
7788             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7789                 inputblock.cls += ' has-feedback';
7790                 inputblock.cn.push(feedback);
7791             }
7792         };
7793         
7794         if (align ==='left' && this.fieldLabel.length) {
7795                 Roo.log("left and has label");
7796                 cfg.cn = [
7797                     
7798                     {
7799                         tag: 'label',
7800                         'for' :  id,
7801                         cls : 'control-label col-sm-' + this.labelWidth,
7802                         html : this.fieldLabel
7803                         
7804                     },
7805                     {
7806                         cls : "col-sm-" + (12 - this.labelWidth), 
7807                         cn: [
7808                             inputblock
7809                         ]
7810                     }
7811                     
7812                 ];
7813         } else if ( this.fieldLabel.length) {
7814                 Roo.log(" label");
7815                  cfg.cn = [
7816                    
7817                     {
7818                         tag: 'label',
7819                         //cls : 'input-group-addon',
7820                         html : this.fieldLabel
7821                         
7822                     },
7823                     
7824                     inputblock
7825                     
7826                 ];
7827
7828         } else {
7829             
7830                 Roo.log(" no label && no align");
7831                 cfg.cn = [
7832                     
7833                         inputblock
7834                     
7835                 ];
7836                 
7837                 
7838         };
7839         Roo.log('input-parentType: ' + this.parentType);
7840         
7841         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7842            cfg.cls += ' navbar-form';
7843            Roo.log(cfg);
7844         }
7845         
7846         return cfg;
7847         
7848     },
7849     /**
7850      * return the real input element.
7851      */
7852     inputEl: function ()
7853     {
7854         return this.el.select('input.form-control',true).first();
7855     },
7856     
7857     tooltipEl : function()
7858     {
7859         return this.inputEl();
7860     },
7861     
7862     setDisabled : function(v)
7863     {
7864         var i  = this.inputEl().dom;
7865         if (!v) {
7866             i.removeAttribute('disabled');
7867             return;
7868             
7869         }
7870         i.setAttribute('disabled','true');
7871     },
7872     initEvents : function()
7873     {
7874           
7875         this.inputEl().on("keydown" , this.fireKey,  this);
7876         this.inputEl().on("focus", this.onFocus,  this);
7877         this.inputEl().on("blur", this.onBlur,  this);
7878         
7879         this.inputEl().relayEvent('keyup', this);
7880
7881         // reference to original value for reset
7882         this.originalValue = this.getValue();
7883         //Roo.form.TextField.superclass.initEvents.call(this);
7884         if(this.validationEvent == 'keyup'){
7885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7886             this.inputEl().on('keyup', this.filterValidation, this);
7887         }
7888         else if(this.validationEvent !== false){
7889             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7890         }
7891         
7892         if(this.selectOnFocus){
7893             this.on("focus", this.preFocus, this);
7894             
7895         }
7896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7897             this.inputEl().on("keypress", this.filterKeys, this);
7898         }
7899        /* if(this.grow){
7900             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7901             this.el.on("click", this.autoSize,  this);
7902         }
7903         */
7904         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7905             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7906         }
7907         
7908         if (typeof(this.before) == 'object') {
7909             this.before.render(this.el.select('.roo-input-before',true).first());
7910         }
7911         if (typeof(this.after) == 'object') {
7912             this.after.render(this.el.select('.roo-input-after',true).first());
7913         }
7914         
7915         
7916     },
7917     filterValidation : function(e){
7918         if(!e.isNavKeyPress()){
7919             this.validationTask.delay(this.validationDelay);
7920         }
7921     },
7922      /**
7923      * Validates the field value
7924      * @return {Boolean} True if the value is valid, else false
7925      */
7926     validate : function(){
7927         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7928         if(this.disabled || this.validateValue(this.getRawValue())){
7929             this.markValid();
7930             return true;
7931         }
7932         
7933         this.markInvalid();
7934         return false;
7935     },
7936     
7937     
7938     /**
7939      * Validates a value according to the field's validation rules and marks the field as invalid
7940      * if the validation fails
7941      * @param {Mixed} value The value to validate
7942      * @return {Boolean} True if the value is valid, else false
7943      */
7944     validateValue : function(value){
7945         if(value.length < 1)  { // if it's blank
7946             if(this.allowBlank){
7947                 return true;
7948             }
7949             return false;
7950         }
7951         
7952         if(value.length < this.minLength){
7953             return false;
7954         }
7955         if(value.length > this.maxLength){
7956             return false;
7957         }
7958         if(this.vtype){
7959             var vt = Roo.form.VTypes;
7960             if(!vt[this.vtype](value, this)){
7961                 return false;
7962             }
7963         }
7964         if(typeof this.validator == "function"){
7965             var msg = this.validator(value);
7966             if(msg !== true){
7967                 return false;
7968             }
7969         }
7970         
7971         if(this.regex && !this.regex.test(value)){
7972             return false;
7973         }
7974         
7975         return true;
7976     },
7977
7978     
7979     
7980      // private
7981     fireKey : function(e){
7982         //Roo.log('field ' + e.getKey());
7983         if(e.isNavKeyPress()){
7984             this.fireEvent("specialkey", this, e);
7985         }
7986     },
7987     focus : function (selectText){
7988         if(this.rendered){
7989             this.inputEl().focus();
7990             if(selectText === true){
7991                 this.inputEl().dom.select();
7992             }
7993         }
7994         return this;
7995     } ,
7996     
7997     onFocus : function(){
7998         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7999            // this.el.addClass(this.focusClass);
8000         }
8001         if(!this.hasFocus){
8002             this.hasFocus = true;
8003             this.startValue = this.getValue();
8004             this.fireEvent("focus", this);
8005         }
8006     },
8007     
8008     beforeBlur : Roo.emptyFn,
8009
8010     
8011     // private
8012     onBlur : function(){
8013         this.beforeBlur();
8014         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8015             //this.el.removeClass(this.focusClass);
8016         }
8017         this.hasFocus = false;
8018         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8019             this.validate();
8020         }
8021         var v = this.getValue();
8022         if(String(v) !== String(this.startValue)){
8023             this.fireEvent('change', this, v, this.startValue);
8024         }
8025         this.fireEvent("blur", this);
8026     },
8027     
8028     /**
8029      * Resets the current field value to the originally loaded value and clears any validation messages
8030      */
8031     reset : function(){
8032         this.setValue(this.originalValue);
8033         this.validate();
8034     },
8035      /**
8036      * Returns the name of the field
8037      * @return {Mixed} name The name field
8038      */
8039     getName: function(){
8040         return this.name;
8041     },
8042      /**
8043      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8044      * @return {Mixed} value The field value
8045      */
8046     getValue : function(){
8047         
8048         var v = this.inputEl().getValue();
8049         
8050         return v;
8051     },
8052     /**
8053      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8054      * @return {Mixed} value The field value
8055      */
8056     getRawValue : function(){
8057         var v = this.inputEl().getValue();
8058         
8059         return v;
8060     },
8061     
8062     /**
8063      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8064      * @param {Mixed} value The value to set
8065      */
8066     setRawValue : function(v){
8067         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8068     },
8069     
8070     selectText : function(start, end){
8071         var v = this.getRawValue();
8072         if(v.length > 0){
8073             start = start === undefined ? 0 : start;
8074             end = end === undefined ? v.length : end;
8075             var d = this.inputEl().dom;
8076             if(d.setSelectionRange){
8077                 d.setSelectionRange(start, end);
8078             }else if(d.createTextRange){
8079                 var range = d.createTextRange();
8080                 range.moveStart("character", start);
8081                 range.moveEnd("character", v.length-end);
8082                 range.select();
8083             }
8084         }
8085     },
8086     
8087     /**
8088      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8089      * @param {Mixed} value The value to set
8090      */
8091     setValue : function(v){
8092         this.value = v;
8093         if(this.rendered){
8094             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8095             this.validate();
8096         }
8097     },
8098     
8099     /*
8100     processValue : function(value){
8101         if(this.stripCharsRe){
8102             var newValue = value.replace(this.stripCharsRe, '');
8103             if(newValue !== value){
8104                 this.setRawValue(newValue);
8105                 return newValue;
8106             }
8107         }
8108         return value;
8109     },
8110   */
8111     preFocus : function(){
8112         
8113         if(this.selectOnFocus){
8114             this.inputEl().dom.select();
8115         }
8116     },
8117     filterKeys : function(e){
8118         var k = e.getKey();
8119         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8120             return;
8121         }
8122         var c = e.getCharCode(), cc = String.fromCharCode(c);
8123         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8124             return;
8125         }
8126         if(!this.maskRe.test(cc)){
8127             e.stopEvent();
8128         }
8129     },
8130      /**
8131      * Clear any invalid styles/messages for this field
8132      */
8133     clearInvalid : function(){
8134         
8135         if(!this.el || this.preventMark){ // not rendered
8136             return;
8137         }
8138         this.el.removeClass(this.invalidClass);
8139         
8140         this.fireEvent('valid', this);
8141     },
8142     
8143      /**
8144      * Mark this field as valid
8145      */
8146     markValid : function(){
8147         if(!this.el  || this.preventMark){ // not rendered
8148             return;
8149         }
8150         
8151         this.el.removeClass([this.invalidClass, this.validClass]);
8152         
8153         if(this.disabled || this.allowBlank){
8154             return;
8155         }
8156         
8157         this.el.addClass(this.validClass);
8158         
8159         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8160             
8161             var feedback = this.el.select('.form-control-feedback', true).first();
8162             
8163             if(feedback){
8164                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8165                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8166             }
8167             
8168         }
8169         
8170         this.fireEvent('valid', this);
8171     },
8172     
8173      /**
8174      * Mark this field as invalid
8175      * @param {String} msg The validation message
8176      */
8177     markInvalid : function(msg){
8178         if(!this.el  || this.preventMark){ // not rendered
8179             return;
8180         }
8181         
8182         this.el.removeClass([this.invalidClass, this.validClass]);
8183         
8184         if(this.disabled || this.allowBlank){
8185             return;
8186         }
8187         
8188         this.el.addClass(this.invalidClass);
8189         
8190         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8191             
8192             var feedback = this.el.select('.form-control-feedback', true).first();
8193             
8194             if(feedback){
8195                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8196                 
8197                 if(this.getValue().length){
8198                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8199                 }
8200                 
8201             }
8202             
8203         }
8204         
8205         this.fireEvent('invalid', this, msg);
8206     },
8207     // private
8208     SafariOnKeyDown : function(event)
8209     {
8210         // this is a workaround for a password hang bug on chrome/ webkit.
8211         
8212         var isSelectAll = false;
8213         
8214         if(this.inputEl().dom.selectionEnd > 0){
8215             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8216         }
8217         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8218             event.preventDefault();
8219             this.setValue('');
8220             return;
8221         }
8222         
8223         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8224             
8225             event.preventDefault();
8226             // this is very hacky as keydown always get's upper case.
8227             //
8228             var cc = String.fromCharCode(event.getCharCode());
8229             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8230             
8231         }
8232     },
8233     adjustWidth : function(tag, w){
8234         tag = tag.toLowerCase();
8235         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8236             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8237                 if(tag == 'input'){
8238                     return w + 2;
8239                 }
8240                 if(tag == 'textarea'){
8241                     return w-2;
8242                 }
8243             }else if(Roo.isOpera){
8244                 if(tag == 'input'){
8245                     return w + 2;
8246                 }
8247                 if(tag == 'textarea'){
8248                     return w-2;
8249                 }
8250             }
8251         }
8252         return w;
8253     }
8254     
8255 });
8256
8257  
8258 /*
8259  * - LGPL
8260  *
8261  * Input
8262  * 
8263  */
8264
8265 /**
8266  * @class Roo.bootstrap.TextArea
8267  * @extends Roo.bootstrap.Input
8268  * Bootstrap TextArea class
8269  * @cfg {Number} cols Specifies the visible width of a text area
8270  * @cfg {Number} rows Specifies the visible number of lines in a text area
8271  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8272  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8273  * @cfg {string} html text
8274  * 
8275  * @constructor
8276  * Create a new TextArea
8277  * @param {Object} config The config object
8278  */
8279
8280 Roo.bootstrap.TextArea = function(config){
8281     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8282    
8283 };
8284
8285 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8286      
8287     cols : false,
8288     rows : 5,
8289     readOnly : false,
8290     warp : 'soft',
8291     resize : false,
8292     value: false,
8293     html: false,
8294     
8295     getAutoCreate : function(){
8296         
8297         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8298         
8299         var id = Roo.id();
8300         
8301         var cfg = {};
8302         
8303         var input =  {
8304             tag: 'textarea',
8305             id : id,
8306             warp : this.warp,
8307             rows : this.rows,
8308             value : this.value || '',
8309             html: this.html || '',
8310             cls : 'form-control',
8311             placeholder : this.placeholder || '' 
8312             
8313         };
8314         
8315         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8316             input.maxLength = this.maxLength;
8317         }
8318         
8319         if(this.resize){
8320             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8321         }
8322         
8323         if(this.cols){
8324             input.cols = this.cols;
8325         }
8326         
8327         if (this.readOnly) {
8328             input.readonly = true;
8329         }
8330         
8331         if (this.name) {
8332             input.name = this.name;
8333         }
8334         
8335         if (this.size) {
8336             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8337         }
8338         
8339         var settings=this;
8340         ['xs','sm','md','lg'].map(function(size){
8341             if (settings[size]) {
8342                 cfg.cls += ' col-' + size + '-' + settings[size];
8343             }
8344         });
8345         
8346         var inputblock = input;
8347         
8348         if(this.hasFeedback && !this.allowBlank){
8349             
8350             var feedback = {
8351                 tag: 'span',
8352                 cls: 'glyphicon form-control-feedback'
8353             };
8354
8355             inputblock = {
8356                 cls : 'has-feedback',
8357                 cn :  [
8358                     input,
8359                     feedback
8360                 ] 
8361             };  
8362         }
8363         
8364         
8365         if (this.before || this.after) {
8366             
8367             inputblock = {
8368                 cls : 'input-group',
8369                 cn :  [] 
8370             };
8371             if (this.before) {
8372                 inputblock.cn.push({
8373                     tag :'span',
8374                     cls : 'input-group-addon',
8375                     html : this.before
8376                 });
8377             }
8378             
8379             inputblock.cn.push(input);
8380             
8381             if(this.hasFeedback && !this.allowBlank){
8382                 inputblock.cls += ' has-feedback';
8383                 inputblock.cn.push(feedback);
8384             }
8385             
8386             if (this.after) {
8387                 inputblock.cn.push({
8388                     tag :'span',
8389                     cls : 'input-group-addon',
8390                     html : this.after
8391                 });
8392             }
8393             
8394         }
8395         
8396         if (align ==='left' && this.fieldLabel.length) {
8397                 Roo.log("left and has label");
8398                 cfg.cn = [
8399                     
8400                     {
8401                         tag: 'label',
8402                         'for' :  id,
8403                         cls : 'control-label col-sm-' + this.labelWidth,
8404                         html : this.fieldLabel
8405                         
8406                     },
8407                     {
8408                         cls : "col-sm-" + (12 - this.labelWidth), 
8409                         cn: [
8410                             inputblock
8411                         ]
8412                     }
8413                     
8414                 ];
8415         } else if ( this.fieldLabel.length) {
8416                 Roo.log(" label");
8417                  cfg.cn = [
8418                    
8419                     {
8420                         tag: 'label',
8421                         //cls : 'input-group-addon',
8422                         html : this.fieldLabel
8423                         
8424                     },
8425                     
8426                     inputblock
8427                     
8428                 ];
8429
8430         } else {
8431             
8432                    Roo.log(" no label && no align");
8433                 cfg.cn = [
8434                     
8435                         inputblock
8436                     
8437                 ];
8438                 
8439                 
8440         }
8441         
8442         if (this.disabled) {
8443             input.disabled=true;
8444         }
8445         
8446         return cfg;
8447         
8448     },
8449     /**
8450      * return the real textarea element.
8451      */
8452     inputEl: function ()
8453     {
8454         return this.el.select('textarea.form-control',true).first();
8455     }
8456 });
8457
8458  
8459 /*
8460  * - LGPL
8461  *
8462  * trigger field - base class for combo..
8463  * 
8464  */
8465  
8466 /**
8467  * @class Roo.bootstrap.TriggerField
8468  * @extends Roo.bootstrap.Input
8469  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8470  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8471  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8472  * for which you can provide a custom implementation.  For example:
8473  * <pre><code>
8474 var trigger = new Roo.bootstrap.TriggerField();
8475 trigger.onTriggerClick = myTriggerFn;
8476 trigger.applyTo('my-field');
8477 </code></pre>
8478  *
8479  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8480  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8481  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8482  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8483  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8484
8485  * @constructor
8486  * Create a new TriggerField.
8487  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8488  * to the base TextField)
8489  */
8490 Roo.bootstrap.TriggerField = function(config){
8491     this.mimicing = false;
8492     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8493 };
8494
8495 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8496     /**
8497      * @cfg {String} triggerClass A CSS class to apply to the trigger
8498      */
8499      /**
8500      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8501      */
8502     hideTrigger:false,
8503
8504     /**
8505      * @cfg {Boolean} removable (true|false) special filter default false
8506      */
8507     removable : false,
8508     
8509     /** @cfg {Boolean} grow @hide */
8510     /** @cfg {Number} growMin @hide */
8511     /** @cfg {Number} growMax @hide */
8512
8513     /**
8514      * @hide 
8515      * @method
8516      */
8517     autoSize: Roo.emptyFn,
8518     // private
8519     monitorTab : true,
8520     // private
8521     deferHeight : true,
8522
8523     
8524     actionMode : 'wrap',
8525     
8526     caret : false,
8527     
8528     
8529     getAutoCreate : function(){
8530        
8531         var align = this.labelAlign || this.parentLabelAlign();
8532         
8533         var id = Roo.id();
8534         
8535         var cfg = {
8536             cls: 'form-group' //input-group
8537         };
8538         
8539         
8540         var input =  {
8541             tag: 'input',
8542             id : id,
8543             type : this.inputType,
8544             cls : 'form-control',
8545             autocomplete: 'new-password',
8546             placeholder : this.placeholder || '' 
8547             
8548         };
8549         if (this.name) {
8550             input.name = this.name;
8551         }
8552         if (this.size) {
8553             input.cls += ' input-' + this.size;
8554         }
8555         
8556         if (this.disabled) {
8557             input.disabled=true;
8558         }
8559         
8560         var inputblock = input;
8561         
8562         if(this.hasFeedback && !this.allowBlank){
8563             
8564             var feedback = {
8565                 tag: 'span',
8566                 cls: 'glyphicon form-control-feedback'
8567             };
8568             
8569             if(this.removable && !this.editable && !this.tickable){
8570                 inputblock = {
8571                     cls : 'has-feedback',
8572                     cn :  [
8573                         inputblock,
8574                         {
8575                             tag: 'button',
8576                             html : 'x',
8577                             cls : 'roo-combo-removable-btn close'
8578                         },
8579                         feedback
8580                     ] 
8581                 };
8582             } else {
8583                 inputblock = {
8584                     cls : 'has-feedback',
8585                     cn :  [
8586                         inputblock,
8587                         feedback
8588                     ] 
8589                 };
8590             }
8591
8592         } else {
8593             if(this.removable && !this.editable && !this.tickable){
8594                 inputblock = {
8595                     cls : 'roo-removable',
8596                     cn :  [
8597                         inputblock,
8598                         {
8599                             tag: 'button',
8600                             html : 'x',
8601                             cls : 'roo-combo-removable-btn close'
8602                         }
8603                     ] 
8604                 };
8605             }
8606         }
8607         
8608         if (this.before || this.after) {
8609             
8610             inputblock = {
8611                 cls : 'input-group',
8612                 cn :  [] 
8613             };
8614             if (this.before) {
8615                 inputblock.cn.push({
8616                     tag :'span',
8617                     cls : 'input-group-addon',
8618                     html : this.before
8619                 });
8620             }
8621             
8622             inputblock.cn.push(input);
8623             
8624             if(this.hasFeedback && !this.allowBlank){
8625                 inputblock.cls += ' has-feedback';
8626                 inputblock.cn.push(feedback);
8627             }
8628             
8629             if (this.after) {
8630                 inputblock.cn.push({
8631                     tag :'span',
8632                     cls : 'input-group-addon',
8633                     html : this.after
8634                 });
8635             }
8636             
8637         };
8638         
8639         var box = {
8640             tag: 'div',
8641             cn: [
8642                 {
8643                     tag: 'input',
8644                     type : 'hidden',
8645                     cls: 'form-hidden-field'
8646                 },
8647                 inputblock
8648             ]
8649             
8650         };
8651         
8652         if(this.multiple){
8653             Roo.log('multiple');
8654             
8655             box = {
8656                 tag: 'div',
8657                 cn: [
8658                     {
8659                         tag: 'input',
8660                         type : 'hidden',
8661                         cls: 'form-hidden-field'
8662                     },
8663                     {
8664                         tag: 'ul',
8665                         cls: 'select2-choices',
8666                         cn:[
8667                             {
8668                                 tag: 'li',
8669                                 cls: 'select2-search-field',
8670                                 cn: [
8671
8672                                     inputblock
8673                                 ]
8674                             }
8675                         ]
8676                     }
8677                 ]
8678             }
8679         };
8680         
8681         var combobox = {
8682             cls: 'select2-container input-group',
8683             cn: [
8684                 box
8685 //                {
8686 //                    tag: 'ul',
8687 //                    cls: 'typeahead typeahead-long dropdown-menu',
8688 //                    style: 'display:none'
8689 //                }
8690             ]
8691         };
8692         
8693         if(!this.multiple && this.showToggleBtn){
8694             
8695             var caret = {
8696                         tag: 'span',
8697                         cls: 'caret'
8698              };
8699             if (this.caret != false) {
8700                 caret = {
8701                      tag: 'i',
8702                      cls: 'fa fa-' + this.caret
8703                 };
8704                 
8705             }
8706             
8707             combobox.cn.push({
8708                 tag :'span',
8709                 cls : 'input-group-addon btn dropdown-toggle',
8710                 cn : [
8711                     caret,
8712                     {
8713                         tag: 'span',
8714                         cls: 'combobox-clear',
8715                         cn  : [
8716                             {
8717                                 tag : 'i',
8718                                 cls: 'icon-remove'
8719                             }
8720                         ]
8721                     }
8722                 ]
8723
8724             })
8725         }
8726         
8727         if(this.multiple){
8728             combobox.cls += ' select2-container-multi';
8729         }
8730         
8731         if (align ==='left' && this.fieldLabel.length) {
8732             
8733                 Roo.log("left and has label");
8734                 cfg.cn = [
8735                     
8736                     {
8737                         tag: 'label',
8738                         'for' :  id,
8739                         cls : 'control-label col-sm-' + this.labelWidth,
8740                         html : this.fieldLabel
8741                         
8742                     },
8743                     {
8744                         cls : "col-sm-" + (12 - this.labelWidth), 
8745                         cn: [
8746                             combobox
8747                         ]
8748                     }
8749                     
8750                 ];
8751         } else if ( this.fieldLabel.length) {
8752                 Roo.log(" label");
8753                  cfg.cn = [
8754                    
8755                     {
8756                         tag: 'label',
8757                         //cls : 'input-group-addon',
8758                         html : this.fieldLabel
8759                         
8760                     },
8761                     
8762                     combobox
8763                     
8764                 ];
8765
8766         } else {
8767             
8768                 Roo.log(" no label && no align");
8769                 cfg = combobox
8770                      
8771                 
8772         }
8773          
8774         var settings=this;
8775         ['xs','sm','md','lg'].map(function(size){
8776             if (settings[size]) {
8777                 cfg.cls += ' col-' + size + '-' + settings[size];
8778             }
8779         });
8780         Roo.log(cfg);
8781         return cfg;
8782         
8783     },
8784     
8785     
8786     
8787     // private
8788     onResize : function(w, h){
8789 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8790 //        if(typeof w == 'number'){
8791 //            var x = w - this.trigger.getWidth();
8792 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8793 //            this.trigger.setStyle('left', x+'px');
8794 //        }
8795     },
8796
8797     // private
8798     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8799
8800     // private
8801     getResizeEl : function(){
8802         return this.inputEl();
8803     },
8804
8805     // private
8806     getPositionEl : function(){
8807         return this.inputEl();
8808     },
8809
8810     // private
8811     alignErrorIcon : function(){
8812         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8813     },
8814
8815     // private
8816     initEvents : function(){
8817         
8818         this.createList();
8819         
8820         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8821         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8822         if(!this.multiple && this.showToggleBtn){
8823             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8824             if(this.hideTrigger){
8825                 this.trigger.setDisplayed(false);
8826             }
8827             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8828         }
8829         
8830         if(this.multiple){
8831             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8832         }
8833         
8834         if(this.removable && !this.editable && !this.tickable){
8835             var close = this.closeTriggerEl();
8836             
8837             if(close){
8838                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8839                 close.on('click', this.removeBtnClick, this, close);
8840             }
8841         }
8842         
8843         //this.trigger.addClassOnOver('x-form-trigger-over');
8844         //this.trigger.addClassOnClick('x-form-trigger-click');
8845         
8846         //if(!this.width){
8847         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8848         //}
8849     },
8850     
8851     closeTriggerEl : function()
8852     {
8853         var close = this.el.select('.roo-combo-removable-btn', true).first();
8854         return close ? close : false;
8855     },
8856     
8857     removeBtnClick : function(e, h, el)
8858     {
8859         e.preventDefault();
8860         
8861         if(this.fireEvent("remove", this) !== false){
8862             this.reset();
8863         }
8864     },
8865     
8866     createList : function()
8867     {
8868         this.list = Roo.get(document.body).createChild({
8869             tag: 'ul',
8870             cls: 'typeahead typeahead-long dropdown-menu',
8871             style: 'display:none'
8872         });
8873         
8874         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8875         
8876     },
8877
8878     // private
8879     initTrigger : function(){
8880        
8881     },
8882
8883     // private
8884     onDestroy : function(){
8885         if(this.trigger){
8886             this.trigger.removeAllListeners();
8887           //  this.trigger.remove();
8888         }
8889         //if(this.wrap){
8890         //    this.wrap.remove();
8891         //}
8892         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8893     },
8894
8895     // private
8896     onFocus : function(){
8897         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8898         /*
8899         if(!this.mimicing){
8900             this.wrap.addClass('x-trigger-wrap-focus');
8901             this.mimicing = true;
8902             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8903             if(this.monitorTab){
8904                 this.el.on("keydown", this.checkTab, this);
8905             }
8906         }
8907         */
8908     },
8909
8910     // private
8911     checkTab : function(e){
8912         if(e.getKey() == e.TAB){
8913             this.triggerBlur();
8914         }
8915     },
8916
8917     // private
8918     onBlur : function(){
8919         // do nothing
8920     },
8921
8922     // private
8923     mimicBlur : function(e, t){
8924         /*
8925         if(!this.wrap.contains(t) && this.validateBlur()){
8926             this.triggerBlur();
8927         }
8928         */
8929     },
8930
8931     // private
8932     triggerBlur : function(){
8933         this.mimicing = false;
8934         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8935         if(this.monitorTab){
8936             this.el.un("keydown", this.checkTab, this);
8937         }
8938         //this.wrap.removeClass('x-trigger-wrap-focus');
8939         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8940     },
8941
8942     // private
8943     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8944     validateBlur : function(e, t){
8945         return true;
8946     },
8947
8948     // private
8949     onDisable : function(){
8950         this.inputEl().dom.disabled = true;
8951         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8952         //if(this.wrap){
8953         //    this.wrap.addClass('x-item-disabled');
8954         //}
8955     },
8956
8957     // private
8958     onEnable : function(){
8959         this.inputEl().dom.disabled = false;
8960         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8961         //if(this.wrap){
8962         //    this.el.removeClass('x-item-disabled');
8963         //}
8964     },
8965
8966     // private
8967     onShow : function(){
8968         var ae = this.getActionEl();
8969         
8970         if(ae){
8971             ae.dom.style.display = '';
8972             ae.dom.style.visibility = 'visible';
8973         }
8974     },
8975
8976     // private
8977     
8978     onHide : function(){
8979         var ae = this.getActionEl();
8980         ae.dom.style.display = 'none';
8981     },
8982
8983     /**
8984      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8985      * by an implementing function.
8986      * @method
8987      * @param {EventObject} e
8988      */
8989     onTriggerClick : Roo.emptyFn
8990 });
8991  /*
8992  * Based on:
8993  * Ext JS Library 1.1.1
8994  * Copyright(c) 2006-2007, Ext JS, LLC.
8995  *
8996  * Originally Released Under LGPL - original licence link has changed is not relivant.
8997  *
8998  * Fork - LGPL
8999  * <script type="text/javascript">
9000  */
9001
9002
9003 /**
9004  * @class Roo.data.SortTypes
9005  * @singleton
9006  * Defines the default sorting (casting?) comparison functions used when sorting data.
9007  */
9008 Roo.data.SortTypes = {
9009     /**
9010      * Default sort that does nothing
9011      * @param {Mixed} s The value being converted
9012      * @return {Mixed} The comparison value
9013      */
9014     none : function(s){
9015         return s;
9016     },
9017     
9018     /**
9019      * The regular expression used to strip tags
9020      * @type {RegExp}
9021      * @property
9022      */
9023     stripTagsRE : /<\/?[^>]+>/gi,
9024     
9025     /**
9026      * Strips all HTML tags to sort on text only
9027      * @param {Mixed} s The value being converted
9028      * @return {String} The comparison value
9029      */
9030     asText : function(s){
9031         return String(s).replace(this.stripTagsRE, "");
9032     },
9033     
9034     /**
9035      * Strips all HTML tags to sort on text only - Case insensitive
9036      * @param {Mixed} s The value being converted
9037      * @return {String} The comparison value
9038      */
9039     asUCText : function(s){
9040         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9041     },
9042     
9043     /**
9044      * Case insensitive string
9045      * @param {Mixed} s The value being converted
9046      * @return {String} The comparison value
9047      */
9048     asUCString : function(s) {
9049         return String(s).toUpperCase();
9050     },
9051     
9052     /**
9053      * Date sorting
9054      * @param {Mixed} s The value being converted
9055      * @return {Number} The comparison value
9056      */
9057     asDate : function(s) {
9058         if(!s){
9059             return 0;
9060         }
9061         if(s instanceof Date){
9062             return s.getTime();
9063         }
9064         return Date.parse(String(s));
9065     },
9066     
9067     /**
9068      * Float sorting
9069      * @param {Mixed} s The value being converted
9070      * @return {Float} The comparison value
9071      */
9072     asFloat : function(s) {
9073         var val = parseFloat(String(s).replace(/,/g, ""));
9074         if(isNaN(val)) val = 0;
9075         return val;
9076     },
9077     
9078     /**
9079      * Integer sorting
9080      * @param {Mixed} s The value being converted
9081      * @return {Number} The comparison value
9082      */
9083     asInt : function(s) {
9084         var val = parseInt(String(s).replace(/,/g, ""));
9085         if(isNaN(val)) val = 0;
9086         return val;
9087     }
9088 };/*
9089  * Based on:
9090  * Ext JS Library 1.1.1
9091  * Copyright(c) 2006-2007, Ext JS, LLC.
9092  *
9093  * Originally Released Under LGPL - original licence link has changed is not relivant.
9094  *
9095  * Fork - LGPL
9096  * <script type="text/javascript">
9097  */
9098
9099 /**
9100 * @class Roo.data.Record
9101  * Instances of this class encapsulate both record <em>definition</em> information, and record
9102  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9103  * to access Records cached in an {@link Roo.data.Store} object.<br>
9104  * <p>
9105  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9106  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9107  * objects.<br>
9108  * <p>
9109  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9110  * @constructor
9111  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9112  * {@link #create}. The parameters are the same.
9113  * @param {Array} data An associative Array of data values keyed by the field name.
9114  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9115  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9116  * not specified an integer id is generated.
9117  */
9118 Roo.data.Record = function(data, id){
9119     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9120     this.data = data;
9121 };
9122
9123 /**
9124  * Generate a constructor for a specific record layout.
9125  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9126  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9127  * Each field definition object may contain the following properties: <ul>
9128  * <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,
9129  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9130  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9131  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9132  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9133  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9134  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9135  * this may be omitted.</p></li>
9136  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9137  * <ul><li>auto (Default, implies no conversion)</li>
9138  * <li>string</li>
9139  * <li>int</li>
9140  * <li>float</li>
9141  * <li>boolean</li>
9142  * <li>date</li></ul></p></li>
9143  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9144  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9145  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9146  * by the Reader into an object that will be stored in the Record. It is passed the
9147  * following parameters:<ul>
9148  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9149  * </ul></p></li>
9150  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9151  * </ul>
9152  * <br>usage:<br><pre><code>
9153 var TopicRecord = Roo.data.Record.create(
9154     {name: 'title', mapping: 'topic_title'},
9155     {name: 'author', mapping: 'username'},
9156     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9157     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9158     {name: 'lastPoster', mapping: 'user2'},
9159     {name: 'excerpt', mapping: 'post_text'}
9160 );
9161
9162 var myNewRecord = new TopicRecord({
9163     title: 'Do my job please',
9164     author: 'noobie',
9165     totalPosts: 1,
9166     lastPost: new Date(),
9167     lastPoster: 'Animal',
9168     excerpt: 'No way dude!'
9169 });
9170 myStore.add(myNewRecord);
9171 </code></pre>
9172  * @method create
9173  * @static
9174  */
9175 Roo.data.Record.create = function(o){
9176     var f = function(){
9177         f.superclass.constructor.apply(this, arguments);
9178     };
9179     Roo.extend(f, Roo.data.Record);
9180     var p = f.prototype;
9181     p.fields = new Roo.util.MixedCollection(false, function(field){
9182         return field.name;
9183     });
9184     for(var i = 0, len = o.length; i < len; i++){
9185         p.fields.add(new Roo.data.Field(o[i]));
9186     }
9187     f.getField = function(name){
9188         return p.fields.get(name);  
9189     };
9190     return f;
9191 };
9192
9193 Roo.data.Record.AUTO_ID = 1000;
9194 Roo.data.Record.EDIT = 'edit';
9195 Roo.data.Record.REJECT = 'reject';
9196 Roo.data.Record.COMMIT = 'commit';
9197
9198 Roo.data.Record.prototype = {
9199     /**
9200      * Readonly flag - true if this record has been modified.
9201      * @type Boolean
9202      */
9203     dirty : false,
9204     editing : false,
9205     error: null,
9206     modified: null,
9207
9208     // private
9209     join : function(store){
9210         this.store = store;
9211     },
9212
9213     /**
9214      * Set the named field to the specified value.
9215      * @param {String} name The name of the field to set.
9216      * @param {Object} value The value to set the field to.
9217      */
9218     set : function(name, value){
9219         if(this.data[name] == value){
9220             return;
9221         }
9222         this.dirty = true;
9223         if(!this.modified){
9224             this.modified = {};
9225         }
9226         if(typeof this.modified[name] == 'undefined'){
9227             this.modified[name] = this.data[name];
9228         }
9229         this.data[name] = value;
9230         if(!this.editing && this.store){
9231             this.store.afterEdit(this);
9232         }       
9233     },
9234
9235     /**
9236      * Get the value of the named field.
9237      * @param {String} name The name of the field to get the value of.
9238      * @return {Object} The value of the field.
9239      */
9240     get : function(name){
9241         return this.data[name]; 
9242     },
9243
9244     // private
9245     beginEdit : function(){
9246         this.editing = true;
9247         this.modified = {}; 
9248     },
9249
9250     // private
9251     cancelEdit : function(){
9252         this.editing = false;
9253         delete this.modified;
9254     },
9255
9256     // private
9257     endEdit : function(){
9258         this.editing = false;
9259         if(this.dirty && this.store){
9260             this.store.afterEdit(this);
9261         }
9262     },
9263
9264     /**
9265      * Usually called by the {@link Roo.data.Store} which owns the Record.
9266      * Rejects all changes made to the Record since either creation, or the last commit operation.
9267      * Modified fields are reverted to their original values.
9268      * <p>
9269      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9270      * of reject operations.
9271      */
9272     reject : function(){
9273         var m = this.modified;
9274         for(var n in m){
9275             if(typeof m[n] != "function"){
9276                 this.data[n] = m[n];
9277             }
9278         }
9279         this.dirty = false;
9280         delete this.modified;
9281         this.editing = false;
9282         if(this.store){
9283             this.store.afterReject(this);
9284         }
9285     },
9286
9287     /**
9288      * Usually called by the {@link Roo.data.Store} which owns the Record.
9289      * Commits all changes made to the Record since either creation, or the last commit operation.
9290      * <p>
9291      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9292      * of commit operations.
9293      */
9294     commit : function(){
9295         this.dirty = false;
9296         delete this.modified;
9297         this.editing = false;
9298         if(this.store){
9299             this.store.afterCommit(this);
9300         }
9301     },
9302
9303     // private
9304     hasError : function(){
9305         return this.error != null;
9306     },
9307
9308     // private
9309     clearError : function(){
9310         this.error = null;
9311     },
9312
9313     /**
9314      * Creates a copy of this record.
9315      * @param {String} id (optional) A new record id if you don't want to use this record's id
9316      * @return {Record}
9317      */
9318     copy : function(newId) {
9319         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9320     }
9321 };/*
9322  * Based on:
9323  * Ext JS Library 1.1.1
9324  * Copyright(c) 2006-2007, Ext JS, LLC.
9325  *
9326  * Originally Released Under LGPL - original licence link has changed is not relivant.
9327  *
9328  * Fork - LGPL
9329  * <script type="text/javascript">
9330  */
9331
9332
9333
9334 /**
9335  * @class Roo.data.Store
9336  * @extends Roo.util.Observable
9337  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9338  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9339  * <p>
9340  * 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
9341  * has no knowledge of the format of the data returned by the Proxy.<br>
9342  * <p>
9343  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9344  * instances from the data object. These records are cached and made available through accessor functions.
9345  * @constructor
9346  * Creates a new Store.
9347  * @param {Object} config A config object containing the objects needed for the Store to access data,
9348  * and read the data into Records.
9349  */
9350 Roo.data.Store = function(config){
9351     this.data = new Roo.util.MixedCollection(false);
9352     this.data.getKey = function(o){
9353         return o.id;
9354     };
9355     this.baseParams = {};
9356     // private
9357     this.paramNames = {
9358         "start" : "start",
9359         "limit" : "limit",
9360         "sort" : "sort",
9361         "dir" : "dir",
9362         "multisort" : "_multisort"
9363     };
9364
9365     if(config && config.data){
9366         this.inlineData = config.data;
9367         delete config.data;
9368     }
9369
9370     Roo.apply(this, config);
9371     
9372     if(this.reader){ // reader passed
9373         this.reader = Roo.factory(this.reader, Roo.data);
9374         this.reader.xmodule = this.xmodule || false;
9375         if(!this.recordType){
9376             this.recordType = this.reader.recordType;
9377         }
9378         if(this.reader.onMetaChange){
9379             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9380         }
9381     }
9382
9383     if(this.recordType){
9384         this.fields = this.recordType.prototype.fields;
9385     }
9386     this.modified = [];
9387
9388     this.addEvents({
9389         /**
9390          * @event datachanged
9391          * Fires when the data cache has changed, and a widget which is using this Store
9392          * as a Record cache should refresh its view.
9393          * @param {Store} this
9394          */
9395         datachanged : true,
9396         /**
9397          * @event metachange
9398          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9399          * @param {Store} this
9400          * @param {Object} meta The JSON metadata
9401          */
9402         metachange : true,
9403         /**
9404          * @event add
9405          * Fires when Records have been added to the Store
9406          * @param {Store} this
9407          * @param {Roo.data.Record[]} records The array of Records added
9408          * @param {Number} index The index at which the record(s) were added
9409          */
9410         add : true,
9411         /**
9412          * @event remove
9413          * Fires when a Record has been removed from the Store
9414          * @param {Store} this
9415          * @param {Roo.data.Record} record The Record that was removed
9416          * @param {Number} index The index at which the record was removed
9417          */
9418         remove : true,
9419         /**
9420          * @event update
9421          * Fires when a Record has been updated
9422          * @param {Store} this
9423          * @param {Roo.data.Record} record The Record that was updated
9424          * @param {String} operation The update operation being performed.  Value may be one of:
9425          * <pre><code>
9426  Roo.data.Record.EDIT
9427  Roo.data.Record.REJECT
9428  Roo.data.Record.COMMIT
9429          * </code></pre>
9430          */
9431         update : true,
9432         /**
9433          * @event clear
9434          * Fires when the data cache has been cleared.
9435          * @param {Store} this
9436          */
9437         clear : true,
9438         /**
9439          * @event beforeload
9440          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9441          * the load action will be canceled.
9442          * @param {Store} this
9443          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9444          */
9445         beforeload : true,
9446         /**
9447          * @event beforeloadadd
9448          * Fires after a new set of Records has been loaded.
9449          * @param {Store} this
9450          * @param {Roo.data.Record[]} records The Records that were loaded
9451          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9452          */
9453         beforeloadadd : true,
9454         /**
9455          * @event load
9456          * Fires after a new set of Records has been loaded, before they are added to the store.
9457          * @param {Store} this
9458          * @param {Roo.data.Record[]} records The Records that were loaded
9459          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9460          * @params {Object} return from reader
9461          */
9462         load : true,
9463         /**
9464          * @event loadexception
9465          * Fires if an exception occurs in the Proxy during loading.
9466          * Called with the signature of the Proxy's "loadexception" event.
9467          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9468          * 
9469          * @param {Proxy} 
9470          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9471          * @param {Object} load options 
9472          * @param {Object} jsonData from your request (normally this contains the Exception)
9473          */
9474         loadexception : true
9475     });
9476     
9477     if(this.proxy){
9478         this.proxy = Roo.factory(this.proxy, Roo.data);
9479         this.proxy.xmodule = this.xmodule || false;
9480         this.relayEvents(this.proxy,  ["loadexception"]);
9481     }
9482     this.sortToggle = {};
9483     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9484
9485     Roo.data.Store.superclass.constructor.call(this);
9486
9487     if(this.inlineData){
9488         this.loadData(this.inlineData);
9489         delete this.inlineData;
9490     }
9491 };
9492
9493 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9494      /**
9495     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9496     * without a remote query - used by combo/forms at present.
9497     */
9498     
9499     /**
9500     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9501     */
9502     /**
9503     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9504     */
9505     /**
9506     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9507     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9508     */
9509     /**
9510     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9511     * on any HTTP request
9512     */
9513     /**
9514     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9515     */
9516     /**
9517     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9518     */
9519     multiSort: false,
9520     /**
9521     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9522     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9523     */
9524     remoteSort : false,
9525
9526     /**
9527     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9528      * loaded or when a record is removed. (defaults to false).
9529     */
9530     pruneModifiedRecords : false,
9531
9532     // private
9533     lastOptions : null,
9534
9535     /**
9536      * Add Records to the Store and fires the add event.
9537      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9538      */
9539     add : function(records){
9540         records = [].concat(records);
9541         for(var i = 0, len = records.length; i < len; i++){
9542             records[i].join(this);
9543         }
9544         var index = this.data.length;
9545         this.data.addAll(records);
9546         this.fireEvent("add", this, records, index);
9547     },
9548
9549     /**
9550      * Remove a Record from the Store and fires the remove event.
9551      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9552      */
9553     remove : function(record){
9554         var index = this.data.indexOf(record);
9555         this.data.removeAt(index);
9556         if(this.pruneModifiedRecords){
9557             this.modified.remove(record);
9558         }
9559         this.fireEvent("remove", this, record, index);
9560     },
9561
9562     /**
9563      * Remove all Records from the Store and fires the clear event.
9564      */
9565     removeAll : function(){
9566         this.data.clear();
9567         if(this.pruneModifiedRecords){
9568             this.modified = [];
9569         }
9570         this.fireEvent("clear", this);
9571     },
9572
9573     /**
9574      * Inserts Records to the Store at the given index and fires the add event.
9575      * @param {Number} index The start index at which to insert the passed Records.
9576      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9577      */
9578     insert : function(index, records){
9579         records = [].concat(records);
9580         for(var i = 0, len = records.length; i < len; i++){
9581             this.data.insert(index, records[i]);
9582             records[i].join(this);
9583         }
9584         this.fireEvent("add", this, records, index);
9585     },
9586
9587     /**
9588      * Get the index within the cache of the passed Record.
9589      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9590      * @return {Number} The index of the passed Record. Returns -1 if not found.
9591      */
9592     indexOf : function(record){
9593         return this.data.indexOf(record);
9594     },
9595
9596     /**
9597      * Get the index within the cache of the Record with the passed id.
9598      * @param {String} id The id of the Record to find.
9599      * @return {Number} The index of the Record. Returns -1 if not found.
9600      */
9601     indexOfId : function(id){
9602         return this.data.indexOfKey(id);
9603     },
9604
9605     /**
9606      * Get the Record with the specified id.
9607      * @param {String} id The id of the Record to find.
9608      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9609      */
9610     getById : function(id){
9611         return this.data.key(id);
9612     },
9613
9614     /**
9615      * Get the Record at the specified index.
9616      * @param {Number} index The index of the Record to find.
9617      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9618      */
9619     getAt : function(index){
9620         return this.data.itemAt(index);
9621     },
9622
9623     /**
9624      * Returns a range of Records between specified indices.
9625      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9626      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9627      * @return {Roo.data.Record[]} An array of Records
9628      */
9629     getRange : function(start, end){
9630         return this.data.getRange(start, end);
9631     },
9632
9633     // private
9634     storeOptions : function(o){
9635         o = Roo.apply({}, o);
9636         delete o.callback;
9637         delete o.scope;
9638         this.lastOptions = o;
9639     },
9640
9641     /**
9642      * Loads the Record cache from the configured Proxy using the configured Reader.
9643      * <p>
9644      * If using remote paging, then the first load call must specify the <em>start</em>
9645      * and <em>limit</em> properties in the options.params property to establish the initial
9646      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9647      * <p>
9648      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9649      * and this call will return before the new data has been loaded. Perform any post-processing
9650      * in a callback function, or in a "load" event handler.</strong>
9651      * <p>
9652      * @param {Object} options An object containing properties which control loading options:<ul>
9653      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9654      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9655      * passed the following arguments:<ul>
9656      * <li>r : Roo.data.Record[]</li>
9657      * <li>options: Options object from the load call</li>
9658      * <li>success: Boolean success indicator</li></ul></li>
9659      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9660      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9661      * </ul>
9662      */
9663     load : function(options){
9664         options = options || {};
9665         if(this.fireEvent("beforeload", this, options) !== false){
9666             this.storeOptions(options);
9667             var p = Roo.apply(options.params || {}, this.baseParams);
9668             // if meta was not loaded from remote source.. try requesting it.
9669             if (!this.reader.metaFromRemote) {
9670                 p._requestMeta = 1;
9671             }
9672             if(this.sortInfo && this.remoteSort){
9673                 var pn = this.paramNames;
9674                 p[pn["sort"]] = this.sortInfo.field;
9675                 p[pn["dir"]] = this.sortInfo.direction;
9676             }
9677             if (this.multiSort) {
9678                 var pn = this.paramNames;
9679                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9680             }
9681             
9682             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9683         }
9684     },
9685
9686     /**
9687      * Reloads the Record cache from the configured Proxy using the configured Reader and
9688      * the options from the last load operation performed.
9689      * @param {Object} options (optional) An object containing properties which may override the options
9690      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9691      * the most recently used options are reused).
9692      */
9693     reload : function(options){
9694         this.load(Roo.applyIf(options||{}, this.lastOptions));
9695     },
9696
9697     // private
9698     // Called as a callback by the Reader during a load operation.
9699     loadRecords : function(o, options, success){
9700         if(!o || success === false){
9701             if(success !== false){
9702                 this.fireEvent("load", this, [], options, o);
9703             }
9704             if(options.callback){
9705                 options.callback.call(options.scope || this, [], options, false);
9706             }
9707             return;
9708         }
9709         // if data returned failure - throw an exception.
9710         if (o.success === false) {
9711             // show a message if no listener is registered.
9712             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9713                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9714             }
9715             // loadmask wil be hooked into this..
9716             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9717             return;
9718         }
9719         var r = o.records, t = o.totalRecords || r.length;
9720         
9721         this.fireEvent("beforeloadadd", this, r, options, o);
9722         
9723         if(!options || options.add !== true){
9724             if(this.pruneModifiedRecords){
9725                 this.modified = [];
9726             }
9727             for(var i = 0, len = r.length; i < len; i++){
9728                 r[i].join(this);
9729             }
9730             if(this.snapshot){
9731                 this.data = this.snapshot;
9732                 delete this.snapshot;
9733             }
9734             this.data.clear();
9735             this.data.addAll(r);
9736             this.totalLength = t;
9737             this.applySort();
9738             this.fireEvent("datachanged", this);
9739         }else{
9740             this.totalLength = Math.max(t, this.data.length+r.length);
9741             this.add(r);
9742         }
9743         this.fireEvent("load", this, r, options, o);
9744         if(options.callback){
9745             options.callback.call(options.scope || this, r, options, true);
9746         }
9747     },
9748
9749
9750     /**
9751      * Loads data from a passed data block. A Reader which understands the format of the data
9752      * must have been configured in the constructor.
9753      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9754      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9755      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9756      */
9757     loadData : function(o, append){
9758         var r = this.reader.readRecords(o);
9759         this.loadRecords(r, {add: append}, true);
9760     },
9761
9762     /**
9763      * Gets the number of cached records.
9764      * <p>
9765      * <em>If using paging, this may not be the total size of the dataset. If the data object
9766      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9767      * the data set size</em>
9768      */
9769     getCount : function(){
9770         return this.data.length || 0;
9771     },
9772
9773     /**
9774      * Gets the total number of records in the dataset as returned by the server.
9775      * <p>
9776      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9777      * the dataset size</em>
9778      */
9779     getTotalCount : function(){
9780         return this.totalLength || 0;
9781     },
9782
9783     /**
9784      * Returns the sort state of the Store as an object with two properties:
9785      * <pre><code>
9786  field {String} The name of the field by which the Records are sorted
9787  direction {String} The sort order, "ASC" or "DESC"
9788      * </code></pre>
9789      */
9790     getSortState : function(){
9791         return this.sortInfo;
9792     },
9793
9794     // private
9795     applySort : function(){
9796         if(this.sortInfo && !this.remoteSort){
9797             var s = this.sortInfo, f = s.field;
9798             var st = this.fields.get(f).sortType;
9799             var fn = function(r1, r2){
9800                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9801                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9802             };
9803             this.data.sort(s.direction, fn);
9804             if(this.snapshot && this.snapshot != this.data){
9805                 this.snapshot.sort(s.direction, fn);
9806             }
9807         }
9808     },
9809
9810     /**
9811      * Sets the default sort column and order to be used by the next load operation.
9812      * @param {String} fieldName The name of the field to sort by.
9813      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9814      */
9815     setDefaultSort : function(field, dir){
9816         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9817     },
9818
9819     /**
9820      * Sort the Records.
9821      * If remote sorting is used, the sort is performed on the server, and the cache is
9822      * reloaded. If local sorting is used, the cache is sorted internally.
9823      * @param {String} fieldName The name of the field to sort by.
9824      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9825      */
9826     sort : function(fieldName, dir){
9827         var f = this.fields.get(fieldName);
9828         if(!dir){
9829             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9830             
9831             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9832                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9833             }else{
9834                 dir = f.sortDir;
9835             }
9836         }
9837         this.sortToggle[f.name] = dir;
9838         this.sortInfo = {field: f.name, direction: dir};
9839         if(!this.remoteSort){
9840             this.applySort();
9841             this.fireEvent("datachanged", this);
9842         }else{
9843             this.load(this.lastOptions);
9844         }
9845     },
9846
9847     /**
9848      * Calls the specified function for each of the Records in the cache.
9849      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9850      * Returning <em>false</em> aborts and exits the iteration.
9851      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9852      */
9853     each : function(fn, scope){
9854         this.data.each(fn, scope);
9855     },
9856
9857     /**
9858      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9859      * (e.g., during paging).
9860      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9861      */
9862     getModifiedRecords : function(){
9863         return this.modified;
9864     },
9865
9866     // private
9867     createFilterFn : function(property, value, anyMatch){
9868         if(!value.exec){ // not a regex
9869             value = String(value);
9870             if(value.length == 0){
9871                 return false;
9872             }
9873             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9874         }
9875         return function(r){
9876             return value.test(r.data[property]);
9877         };
9878     },
9879
9880     /**
9881      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9882      * @param {String} property A field on your records
9883      * @param {Number} start The record index to start at (defaults to 0)
9884      * @param {Number} end The last record index to include (defaults to length - 1)
9885      * @return {Number} The sum
9886      */
9887     sum : function(property, start, end){
9888         var rs = this.data.items, v = 0;
9889         start = start || 0;
9890         end = (end || end === 0) ? end : rs.length-1;
9891
9892         for(var i = start; i <= end; i++){
9893             v += (rs[i].data[property] || 0);
9894         }
9895         return v;
9896     },
9897
9898     /**
9899      * Filter the records by a specified property.
9900      * @param {String} field A field on your records
9901      * @param {String/RegExp} value Either a string that the field
9902      * should start with or a RegExp to test against the field
9903      * @param {Boolean} anyMatch True to match any part not just the beginning
9904      */
9905     filter : function(property, value, anyMatch){
9906         var fn = this.createFilterFn(property, value, anyMatch);
9907         return fn ? this.filterBy(fn) : this.clearFilter();
9908     },
9909
9910     /**
9911      * Filter by a function. The specified function will be called with each
9912      * record in this data source. If the function returns true the record is included,
9913      * otherwise it is filtered.
9914      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9915      * @param {Object} scope (optional) The scope of the function (defaults to this)
9916      */
9917     filterBy : function(fn, scope){
9918         this.snapshot = this.snapshot || this.data;
9919         this.data = this.queryBy(fn, scope||this);
9920         this.fireEvent("datachanged", this);
9921     },
9922
9923     /**
9924      * Query the records by a specified property.
9925      * @param {String} field A field on your records
9926      * @param {String/RegExp} value Either a string that the field
9927      * should start with or a RegExp to test against the field
9928      * @param {Boolean} anyMatch True to match any part not just the beginning
9929      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9930      */
9931     query : function(property, value, anyMatch){
9932         var fn = this.createFilterFn(property, value, anyMatch);
9933         return fn ? this.queryBy(fn) : this.data.clone();
9934     },
9935
9936     /**
9937      * Query by a function. The specified function will be called with each
9938      * record in this data source. If the function returns true the record is included
9939      * in the results.
9940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9941      * @param {Object} scope (optional) The scope of the function (defaults to this)
9942       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9943      **/
9944     queryBy : function(fn, scope){
9945         var data = this.snapshot || this.data;
9946         return data.filterBy(fn, scope||this);
9947     },
9948
9949     /**
9950      * Collects unique values for a particular dataIndex from this store.
9951      * @param {String} dataIndex The property to collect
9952      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9953      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9954      * @return {Array} An array of the unique values
9955      **/
9956     collect : function(dataIndex, allowNull, bypassFilter){
9957         var d = (bypassFilter === true && this.snapshot) ?
9958                 this.snapshot.items : this.data.items;
9959         var v, sv, r = [], l = {};
9960         for(var i = 0, len = d.length; i < len; i++){
9961             v = d[i].data[dataIndex];
9962             sv = String(v);
9963             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9964                 l[sv] = true;
9965                 r[r.length] = v;
9966             }
9967         }
9968         return r;
9969     },
9970
9971     /**
9972      * Revert to a view of the Record cache with no filtering applied.
9973      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9974      */
9975     clearFilter : function(suppressEvent){
9976         if(this.snapshot && this.snapshot != this.data){
9977             this.data = this.snapshot;
9978             delete this.snapshot;
9979             if(suppressEvent !== true){
9980                 this.fireEvent("datachanged", this);
9981             }
9982         }
9983     },
9984
9985     // private
9986     afterEdit : function(record){
9987         if(this.modified.indexOf(record) == -1){
9988             this.modified.push(record);
9989         }
9990         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9991     },
9992     
9993     // private
9994     afterReject : function(record){
9995         this.modified.remove(record);
9996         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9997     },
9998
9999     // private
10000     afterCommit : function(record){
10001         this.modified.remove(record);
10002         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10003     },
10004
10005     /**
10006      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10007      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10008      */
10009     commitChanges : function(){
10010         var m = this.modified.slice(0);
10011         this.modified = [];
10012         for(var i = 0, len = m.length; i < len; i++){
10013             m[i].commit();
10014         }
10015     },
10016
10017     /**
10018      * Cancel outstanding changes on all changed records.
10019      */
10020     rejectChanges : function(){
10021         var m = this.modified.slice(0);
10022         this.modified = [];
10023         for(var i = 0, len = m.length; i < len; i++){
10024             m[i].reject();
10025         }
10026     },
10027
10028     onMetaChange : function(meta, rtype, o){
10029         this.recordType = rtype;
10030         this.fields = rtype.prototype.fields;
10031         delete this.snapshot;
10032         this.sortInfo = meta.sortInfo || this.sortInfo;
10033         this.modified = [];
10034         this.fireEvent('metachange', this, this.reader.meta);
10035     },
10036     
10037     moveIndex : function(data, type)
10038     {
10039         var index = this.indexOf(data);
10040         
10041         var newIndex = index + type;
10042         
10043         this.remove(data);
10044         
10045         this.insert(newIndex, data);
10046         
10047     }
10048 });/*
10049  * Based on:
10050  * Ext JS Library 1.1.1
10051  * Copyright(c) 2006-2007, Ext JS, LLC.
10052  *
10053  * Originally Released Under LGPL - original licence link has changed is not relivant.
10054  *
10055  * Fork - LGPL
10056  * <script type="text/javascript">
10057  */
10058
10059 /**
10060  * @class Roo.data.SimpleStore
10061  * @extends Roo.data.Store
10062  * Small helper class to make creating Stores from Array data easier.
10063  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10064  * @cfg {Array} fields An array of field definition objects, or field name strings.
10065  * @cfg {Array} data The multi-dimensional array of data
10066  * @constructor
10067  * @param {Object} config
10068  */
10069 Roo.data.SimpleStore = function(config){
10070     Roo.data.SimpleStore.superclass.constructor.call(this, {
10071         isLocal : true,
10072         reader: new Roo.data.ArrayReader({
10073                 id: config.id
10074             },
10075             Roo.data.Record.create(config.fields)
10076         ),
10077         proxy : new Roo.data.MemoryProxy(config.data)
10078     });
10079     this.load();
10080 };
10081 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10082  * Based on:
10083  * Ext JS Library 1.1.1
10084  * Copyright(c) 2006-2007, Ext JS, LLC.
10085  *
10086  * Originally Released Under LGPL - original licence link has changed is not relivant.
10087  *
10088  * Fork - LGPL
10089  * <script type="text/javascript">
10090  */
10091
10092 /**
10093 /**
10094  * @extends Roo.data.Store
10095  * @class Roo.data.JsonStore
10096  * Small helper class to make creating Stores for JSON data easier. <br/>
10097 <pre><code>
10098 var store = new Roo.data.JsonStore({
10099     url: 'get-images.php',
10100     root: 'images',
10101     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10102 });
10103 </code></pre>
10104  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10105  * JsonReader and HttpProxy (unless inline data is provided).</b>
10106  * @cfg {Array} fields An array of field definition objects, or field name strings.
10107  * @constructor
10108  * @param {Object} config
10109  */
10110 Roo.data.JsonStore = function(c){
10111     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10112         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10113         reader: new Roo.data.JsonReader(c, c.fields)
10114     }));
10115 };
10116 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10117  * Based on:
10118  * Ext JS Library 1.1.1
10119  * Copyright(c) 2006-2007, Ext JS, LLC.
10120  *
10121  * Originally Released Under LGPL - original licence link has changed is not relivant.
10122  *
10123  * Fork - LGPL
10124  * <script type="text/javascript">
10125  */
10126
10127  
10128 Roo.data.Field = function(config){
10129     if(typeof config == "string"){
10130         config = {name: config};
10131     }
10132     Roo.apply(this, config);
10133     
10134     if(!this.type){
10135         this.type = "auto";
10136     }
10137     
10138     var st = Roo.data.SortTypes;
10139     // named sortTypes are supported, here we look them up
10140     if(typeof this.sortType == "string"){
10141         this.sortType = st[this.sortType];
10142     }
10143     
10144     // set default sortType for strings and dates
10145     if(!this.sortType){
10146         switch(this.type){
10147             case "string":
10148                 this.sortType = st.asUCString;
10149                 break;
10150             case "date":
10151                 this.sortType = st.asDate;
10152                 break;
10153             default:
10154                 this.sortType = st.none;
10155         }
10156     }
10157
10158     // define once
10159     var stripRe = /[\$,%]/g;
10160
10161     // prebuilt conversion function for this field, instead of
10162     // switching every time we're reading a value
10163     if(!this.convert){
10164         var cv, dateFormat = this.dateFormat;
10165         switch(this.type){
10166             case "":
10167             case "auto":
10168             case undefined:
10169                 cv = function(v){ return v; };
10170                 break;
10171             case "string":
10172                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10173                 break;
10174             case "int":
10175                 cv = function(v){
10176                     return v !== undefined && v !== null && v !== '' ?
10177                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10178                     };
10179                 break;
10180             case "float":
10181                 cv = function(v){
10182                     return v !== undefined && v !== null && v !== '' ?
10183                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10184                     };
10185                 break;
10186             case "bool":
10187             case "boolean":
10188                 cv = function(v){ return v === true || v === "true" || v == 1; };
10189                 break;
10190             case "date":
10191                 cv = function(v){
10192                     if(!v){
10193                         return '';
10194                     }
10195                     if(v instanceof Date){
10196                         return v;
10197                     }
10198                     if(dateFormat){
10199                         if(dateFormat == "timestamp"){
10200                             return new Date(v*1000);
10201                         }
10202                         return Date.parseDate(v, dateFormat);
10203                     }
10204                     var parsed = Date.parse(v);
10205                     return parsed ? new Date(parsed) : null;
10206                 };
10207              break;
10208             
10209         }
10210         this.convert = cv;
10211     }
10212 };
10213
10214 Roo.data.Field.prototype = {
10215     dateFormat: null,
10216     defaultValue: "",
10217     mapping: null,
10218     sortType : null,
10219     sortDir : "ASC"
10220 };/*
10221  * Based on:
10222  * Ext JS Library 1.1.1
10223  * Copyright(c) 2006-2007, Ext JS, LLC.
10224  *
10225  * Originally Released Under LGPL - original licence link has changed is not relivant.
10226  *
10227  * Fork - LGPL
10228  * <script type="text/javascript">
10229  */
10230  
10231 // Base class for reading structured data from a data source.  This class is intended to be
10232 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10233
10234 /**
10235  * @class Roo.data.DataReader
10236  * Base class for reading structured data from a data source.  This class is intended to be
10237  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10238  */
10239
10240 Roo.data.DataReader = function(meta, recordType){
10241     
10242     this.meta = meta;
10243     
10244     this.recordType = recordType instanceof Array ? 
10245         Roo.data.Record.create(recordType) : recordType;
10246 };
10247
10248 Roo.data.DataReader.prototype = {
10249      /**
10250      * Create an empty record
10251      * @param {Object} data (optional) - overlay some values
10252      * @return {Roo.data.Record} record created.
10253      */
10254     newRow :  function(d) {
10255         var da =  {};
10256         this.recordType.prototype.fields.each(function(c) {
10257             switch( c.type) {
10258                 case 'int' : da[c.name] = 0; break;
10259                 case 'date' : da[c.name] = new Date(); break;
10260                 case 'float' : da[c.name] = 0.0; break;
10261                 case 'boolean' : da[c.name] = false; break;
10262                 default : da[c.name] = ""; break;
10263             }
10264             
10265         });
10266         return new this.recordType(Roo.apply(da, d));
10267     }
10268     
10269 };/*
10270  * Based on:
10271  * Ext JS Library 1.1.1
10272  * Copyright(c) 2006-2007, Ext JS, LLC.
10273  *
10274  * Originally Released Under LGPL - original licence link has changed is not relivant.
10275  *
10276  * Fork - LGPL
10277  * <script type="text/javascript">
10278  */
10279
10280 /**
10281  * @class Roo.data.DataProxy
10282  * @extends Roo.data.Observable
10283  * This class is an abstract base class for implementations which provide retrieval of
10284  * unformatted data objects.<br>
10285  * <p>
10286  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10287  * (of the appropriate type which knows how to parse the data object) to provide a block of
10288  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10289  * <p>
10290  * Custom implementations must implement the load method as described in
10291  * {@link Roo.data.HttpProxy#load}.
10292  */
10293 Roo.data.DataProxy = function(){
10294     this.addEvents({
10295         /**
10296          * @event beforeload
10297          * Fires before a network request is made to retrieve a data object.
10298          * @param {Object} This DataProxy object.
10299          * @param {Object} params The params parameter to the load function.
10300          */
10301         beforeload : true,
10302         /**
10303          * @event load
10304          * Fires before the load method's callback is called.
10305          * @param {Object} This DataProxy object.
10306          * @param {Object} o The data object.
10307          * @param {Object} arg The callback argument object passed to the load function.
10308          */
10309         load : true,
10310         /**
10311          * @event loadexception
10312          * Fires if an Exception occurs during data retrieval.
10313          * @param {Object} This DataProxy object.
10314          * @param {Object} o The data object.
10315          * @param {Object} arg The callback argument object passed to the load function.
10316          * @param {Object} e The Exception.
10317          */
10318         loadexception : true
10319     });
10320     Roo.data.DataProxy.superclass.constructor.call(this);
10321 };
10322
10323 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10324
10325     /**
10326      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10327      */
10328 /*
10329  * Based on:
10330  * Ext JS Library 1.1.1
10331  * Copyright(c) 2006-2007, Ext JS, LLC.
10332  *
10333  * Originally Released Under LGPL - original licence link has changed is not relivant.
10334  *
10335  * Fork - LGPL
10336  * <script type="text/javascript">
10337  */
10338 /**
10339  * @class Roo.data.MemoryProxy
10340  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10341  * to the Reader when its load method is called.
10342  * @constructor
10343  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10344  */
10345 Roo.data.MemoryProxy = function(data){
10346     if (data.data) {
10347         data = data.data;
10348     }
10349     Roo.data.MemoryProxy.superclass.constructor.call(this);
10350     this.data = data;
10351 };
10352
10353 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10354     /**
10355      * Load data from the requested source (in this case an in-memory
10356      * data object passed to the constructor), read the data object into
10357      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10358      * process that block using the passed callback.
10359      * @param {Object} params This parameter is not used by the MemoryProxy class.
10360      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10361      * object into a block of Roo.data.Records.
10362      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10363      * The function must be passed <ul>
10364      * <li>The Record block object</li>
10365      * <li>The "arg" argument from the load function</li>
10366      * <li>A boolean success indicator</li>
10367      * </ul>
10368      * @param {Object} scope The scope in which to call the callback
10369      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10370      */
10371     load : function(params, reader, callback, scope, arg){
10372         params = params || {};
10373         var result;
10374         try {
10375             result = reader.readRecords(this.data);
10376         }catch(e){
10377             this.fireEvent("loadexception", this, arg, null, e);
10378             callback.call(scope, null, arg, false);
10379             return;
10380         }
10381         callback.call(scope, result, arg, true);
10382     },
10383     
10384     // private
10385     update : function(params, records){
10386         
10387     }
10388 });/*
10389  * Based on:
10390  * Ext JS Library 1.1.1
10391  * Copyright(c) 2006-2007, Ext JS, LLC.
10392  *
10393  * Originally Released Under LGPL - original licence link has changed is not relivant.
10394  *
10395  * Fork - LGPL
10396  * <script type="text/javascript">
10397  */
10398 /**
10399  * @class Roo.data.HttpProxy
10400  * @extends Roo.data.DataProxy
10401  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10402  * configured to reference a certain URL.<br><br>
10403  * <p>
10404  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10405  * from which the running page was served.<br><br>
10406  * <p>
10407  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10408  * <p>
10409  * Be aware that to enable the browser to parse an XML document, the server must set
10410  * the Content-Type header in the HTTP response to "text/xml".
10411  * @constructor
10412  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10413  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10414  * will be used to make the request.
10415  */
10416 Roo.data.HttpProxy = function(conn){
10417     Roo.data.HttpProxy.superclass.constructor.call(this);
10418     // is conn a conn config or a real conn?
10419     this.conn = conn;
10420     this.useAjax = !conn || !conn.events;
10421   
10422 };
10423
10424 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10425     // thse are take from connection...
10426     
10427     /**
10428      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10429      */
10430     /**
10431      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10432      * extra parameters to each request made by this object. (defaults to undefined)
10433      */
10434     /**
10435      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10436      *  to each request made by this object. (defaults to undefined)
10437      */
10438     /**
10439      * @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)
10440      */
10441     /**
10442      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10443      */
10444      /**
10445      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10446      * @type Boolean
10447      */
10448   
10449
10450     /**
10451      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10452      * @type Boolean
10453      */
10454     /**
10455      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10456      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10457      * a finer-grained basis than the DataProxy events.
10458      */
10459     getConnection : function(){
10460         return this.useAjax ? Roo.Ajax : this.conn;
10461     },
10462
10463     /**
10464      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10465      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10466      * process that block using the passed callback.
10467      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10468      * for the request to the remote server.
10469      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10470      * object into a block of Roo.data.Records.
10471      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10472      * The function must be passed <ul>
10473      * <li>The Record block object</li>
10474      * <li>The "arg" argument from the load function</li>
10475      * <li>A boolean success indicator</li>
10476      * </ul>
10477      * @param {Object} scope The scope in which to call the callback
10478      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10479      */
10480     load : function(params, reader, callback, scope, arg){
10481         if(this.fireEvent("beforeload", this, params) !== false){
10482             var  o = {
10483                 params : params || {},
10484                 request: {
10485                     callback : callback,
10486                     scope : scope,
10487                     arg : arg
10488                 },
10489                 reader: reader,
10490                 callback : this.loadResponse,
10491                 scope: this
10492             };
10493             if(this.useAjax){
10494                 Roo.applyIf(o, this.conn);
10495                 if(this.activeRequest){
10496                     Roo.Ajax.abort(this.activeRequest);
10497                 }
10498                 this.activeRequest = Roo.Ajax.request(o);
10499             }else{
10500                 this.conn.request(o);
10501             }
10502         }else{
10503             callback.call(scope||this, null, arg, false);
10504         }
10505     },
10506
10507     // private
10508     loadResponse : function(o, success, response){
10509         delete this.activeRequest;
10510         if(!success){
10511             this.fireEvent("loadexception", this, o, response);
10512             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10513             return;
10514         }
10515         var result;
10516         try {
10517             result = o.reader.read(response);
10518         }catch(e){
10519             this.fireEvent("loadexception", this, o, response, e);
10520             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10521             return;
10522         }
10523         
10524         this.fireEvent("load", this, o, o.request.arg);
10525         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10526     },
10527
10528     // private
10529     update : function(dataSet){
10530
10531     },
10532
10533     // private
10534     updateResponse : function(dataSet){
10535
10536     }
10537 });/*
10538  * Based on:
10539  * Ext JS Library 1.1.1
10540  * Copyright(c) 2006-2007, Ext JS, LLC.
10541  *
10542  * Originally Released Under LGPL - original licence link has changed is not relivant.
10543  *
10544  * Fork - LGPL
10545  * <script type="text/javascript">
10546  */
10547
10548 /**
10549  * @class Roo.data.ScriptTagProxy
10550  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10551  * other than the originating domain of the running page.<br><br>
10552  * <p>
10553  * <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
10554  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10555  * <p>
10556  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10557  * source code that is used as the source inside a &lt;script> tag.<br><br>
10558  * <p>
10559  * In order for the browser to process the returned data, the server must wrap the data object
10560  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10561  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10562  * depending on whether the callback name was passed:
10563  * <p>
10564  * <pre><code>
10565 boolean scriptTag = false;
10566 String cb = request.getParameter("callback");
10567 if (cb != null) {
10568     scriptTag = true;
10569     response.setContentType("text/javascript");
10570 } else {
10571     response.setContentType("application/x-json");
10572 }
10573 Writer out = response.getWriter();
10574 if (scriptTag) {
10575     out.write(cb + "(");
10576 }
10577 out.print(dataBlock.toJsonString());
10578 if (scriptTag) {
10579     out.write(");");
10580 }
10581 </pre></code>
10582  *
10583  * @constructor
10584  * @param {Object} config A configuration object.
10585  */
10586 Roo.data.ScriptTagProxy = function(config){
10587     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10588     Roo.apply(this, config);
10589     this.head = document.getElementsByTagName("head")[0];
10590 };
10591
10592 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10593
10594 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10595     /**
10596      * @cfg {String} url The URL from which to request the data object.
10597      */
10598     /**
10599      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10600      */
10601     timeout : 30000,
10602     /**
10603      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10604      * the server the name of the callback function set up by the load call to process the returned data object.
10605      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10606      * javascript output which calls this named function passing the data object as its only parameter.
10607      */
10608     callbackParam : "callback",
10609     /**
10610      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10611      * name to the request.
10612      */
10613     nocache : true,
10614
10615     /**
10616      * Load data from the configured URL, read the data object into
10617      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10618      * process that block using the passed callback.
10619      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10620      * for the request to the remote server.
10621      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10622      * object into a block of Roo.data.Records.
10623      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10624      * The function must be passed <ul>
10625      * <li>The Record block object</li>
10626      * <li>The "arg" argument from the load function</li>
10627      * <li>A boolean success indicator</li>
10628      * </ul>
10629      * @param {Object} scope The scope in which to call the callback
10630      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10631      */
10632     load : function(params, reader, callback, scope, arg){
10633         if(this.fireEvent("beforeload", this, params) !== false){
10634
10635             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10636
10637             var url = this.url;
10638             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10639             if(this.nocache){
10640                 url += "&_dc=" + (new Date().getTime());
10641             }
10642             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10643             var trans = {
10644                 id : transId,
10645                 cb : "stcCallback"+transId,
10646                 scriptId : "stcScript"+transId,
10647                 params : params,
10648                 arg : arg,
10649                 url : url,
10650                 callback : callback,
10651                 scope : scope,
10652                 reader : reader
10653             };
10654             var conn = this;
10655
10656             window[trans.cb] = function(o){
10657                 conn.handleResponse(o, trans);
10658             };
10659
10660             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10661
10662             if(this.autoAbort !== false){
10663                 this.abort();
10664             }
10665
10666             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10667
10668             var script = document.createElement("script");
10669             script.setAttribute("src", url);
10670             script.setAttribute("type", "text/javascript");
10671             script.setAttribute("id", trans.scriptId);
10672             this.head.appendChild(script);
10673
10674             this.trans = trans;
10675         }else{
10676             callback.call(scope||this, null, arg, false);
10677         }
10678     },
10679
10680     // private
10681     isLoading : function(){
10682         return this.trans ? true : false;
10683     },
10684
10685     /**
10686      * Abort the current server request.
10687      */
10688     abort : function(){
10689         if(this.isLoading()){
10690             this.destroyTrans(this.trans);
10691         }
10692     },
10693
10694     // private
10695     destroyTrans : function(trans, isLoaded){
10696         this.head.removeChild(document.getElementById(trans.scriptId));
10697         clearTimeout(trans.timeoutId);
10698         if(isLoaded){
10699             window[trans.cb] = undefined;
10700             try{
10701                 delete window[trans.cb];
10702             }catch(e){}
10703         }else{
10704             // if hasn't been loaded, wait for load to remove it to prevent script error
10705             window[trans.cb] = function(){
10706                 window[trans.cb] = undefined;
10707                 try{
10708                     delete window[trans.cb];
10709                 }catch(e){}
10710             };
10711         }
10712     },
10713
10714     // private
10715     handleResponse : function(o, trans){
10716         this.trans = false;
10717         this.destroyTrans(trans, true);
10718         var result;
10719         try {
10720             result = trans.reader.readRecords(o);
10721         }catch(e){
10722             this.fireEvent("loadexception", this, o, trans.arg, e);
10723             trans.callback.call(trans.scope||window, null, trans.arg, false);
10724             return;
10725         }
10726         this.fireEvent("load", this, o, trans.arg);
10727         trans.callback.call(trans.scope||window, result, trans.arg, true);
10728     },
10729
10730     // private
10731     handleFailure : function(trans){
10732         this.trans = false;
10733         this.destroyTrans(trans, false);
10734         this.fireEvent("loadexception", this, null, trans.arg);
10735         trans.callback.call(trans.scope||window, null, trans.arg, false);
10736     }
10737 });/*
10738  * Based on:
10739  * Ext JS Library 1.1.1
10740  * Copyright(c) 2006-2007, Ext JS, LLC.
10741  *
10742  * Originally Released Under LGPL - original licence link has changed is not relivant.
10743  *
10744  * Fork - LGPL
10745  * <script type="text/javascript">
10746  */
10747
10748 /**
10749  * @class Roo.data.JsonReader
10750  * @extends Roo.data.DataReader
10751  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10752  * based on mappings in a provided Roo.data.Record constructor.
10753  * 
10754  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10755  * in the reply previously. 
10756  * 
10757  * <p>
10758  * Example code:
10759  * <pre><code>
10760 var RecordDef = Roo.data.Record.create([
10761     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10762     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10763 ]);
10764 var myReader = new Roo.data.JsonReader({
10765     totalProperty: "results",    // The property which contains the total dataset size (optional)
10766     root: "rows",                // The property which contains an Array of row objects
10767     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10768 }, RecordDef);
10769 </code></pre>
10770  * <p>
10771  * This would consume a JSON file like this:
10772  * <pre><code>
10773 { 'results': 2, 'rows': [
10774     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10775     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10776 }
10777 </code></pre>
10778  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10779  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10780  * paged from the remote server.
10781  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10782  * @cfg {String} root name of the property which contains the Array of row objects.
10783  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10784  * @cfg {Array} fields Array of field definition objects
10785  * @constructor
10786  * Create a new JsonReader
10787  * @param {Object} meta Metadata configuration options
10788  * @param {Object} recordType Either an Array of field definition objects,
10789  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10790  */
10791 Roo.data.JsonReader = function(meta, recordType){
10792     
10793     meta = meta || {};
10794     // set some defaults:
10795     Roo.applyIf(meta, {
10796         totalProperty: 'total',
10797         successProperty : 'success',
10798         root : 'data',
10799         id : 'id'
10800     });
10801     
10802     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10803 };
10804 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10805     
10806     /**
10807      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10808      * Used by Store query builder to append _requestMeta to params.
10809      * 
10810      */
10811     metaFromRemote : false,
10812     /**
10813      * This method is only used by a DataProxy which has retrieved data from a remote server.
10814      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10815      * @return {Object} data A data block which is used by an Roo.data.Store object as
10816      * a cache of Roo.data.Records.
10817      */
10818     read : function(response){
10819         var json = response.responseText;
10820        
10821         var o = /* eval:var:o */ eval("("+json+")");
10822         if(!o) {
10823             throw {message: "JsonReader.read: Json object not found"};
10824         }
10825         
10826         if(o.metaData){
10827             
10828             delete this.ef;
10829             this.metaFromRemote = true;
10830             this.meta = o.metaData;
10831             this.recordType = Roo.data.Record.create(o.metaData.fields);
10832             this.onMetaChange(this.meta, this.recordType, o);
10833         }
10834         return this.readRecords(o);
10835     },
10836
10837     // private function a store will implement
10838     onMetaChange : function(meta, recordType, o){
10839
10840     },
10841
10842     /**
10843          * @ignore
10844          */
10845     simpleAccess: function(obj, subsc) {
10846         return obj[subsc];
10847     },
10848
10849         /**
10850          * @ignore
10851          */
10852     getJsonAccessor: function(){
10853         var re = /[\[\.]/;
10854         return function(expr) {
10855             try {
10856                 return(re.test(expr))
10857                     ? new Function("obj", "return obj." + expr)
10858                     : function(obj){
10859                         return obj[expr];
10860                     };
10861             } catch(e){}
10862             return Roo.emptyFn;
10863         };
10864     }(),
10865
10866     /**
10867      * Create a data block containing Roo.data.Records from an XML document.
10868      * @param {Object} o An object which contains an Array of row objects in the property specified
10869      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10870      * which contains the total size of the dataset.
10871      * @return {Object} data A data block which is used by an Roo.data.Store object as
10872      * a cache of Roo.data.Records.
10873      */
10874     readRecords : function(o){
10875         /**
10876          * After any data loads, the raw JSON data is available for further custom processing.
10877          * @type Object
10878          */
10879         this.o = o;
10880         var s = this.meta, Record = this.recordType,
10881             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10882
10883 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10884         if (!this.ef) {
10885             if(s.totalProperty) {
10886                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10887                 }
10888                 if(s.successProperty) {
10889                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10890                 }
10891                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10892                 if (s.id) {
10893                         var g = this.getJsonAccessor(s.id);
10894                         this.getId = function(rec) {
10895                                 var r = g(rec);  
10896                                 return (r === undefined || r === "") ? null : r;
10897                         };
10898                 } else {
10899                         this.getId = function(){return null;};
10900                 }
10901             this.ef = [];
10902             for(var jj = 0; jj < fl; jj++){
10903                 f = fi[jj];
10904                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10905                 this.ef[jj] = this.getJsonAccessor(map);
10906             }
10907         }
10908
10909         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10910         if(s.totalProperty){
10911             var vt = parseInt(this.getTotal(o), 10);
10912             if(!isNaN(vt)){
10913                 totalRecords = vt;
10914             }
10915         }
10916         if(s.successProperty){
10917             var vs = this.getSuccess(o);
10918             if(vs === false || vs === 'false'){
10919                 success = false;
10920             }
10921         }
10922         var records = [];
10923         for(var i = 0; i < c; i++){
10924                 var n = root[i];
10925             var values = {};
10926             var id = this.getId(n);
10927             for(var j = 0; j < fl; j++){
10928                 f = fi[j];
10929             var v = this.ef[j](n);
10930             if (!f.convert) {
10931                 Roo.log('missing convert for ' + f.name);
10932                 Roo.log(f);
10933                 continue;
10934             }
10935             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10936             }
10937             var record = new Record(values, id);
10938             record.json = n;
10939             records[i] = record;
10940         }
10941         return {
10942             raw : o,
10943             success : success,
10944             records : records,
10945             totalRecords : totalRecords
10946         };
10947     }
10948 });/*
10949  * Based on:
10950  * Ext JS Library 1.1.1
10951  * Copyright(c) 2006-2007, Ext JS, LLC.
10952  *
10953  * Originally Released Under LGPL - original licence link has changed is not relivant.
10954  *
10955  * Fork - LGPL
10956  * <script type="text/javascript">
10957  */
10958
10959 /**
10960  * @class Roo.data.ArrayReader
10961  * @extends Roo.data.DataReader
10962  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10963  * Each element of that Array represents a row of data fields. The
10964  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10965  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10966  * <p>
10967  * Example code:.
10968  * <pre><code>
10969 var RecordDef = Roo.data.Record.create([
10970     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10971     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10972 ]);
10973 var myReader = new Roo.data.ArrayReader({
10974     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10975 }, RecordDef);
10976 </code></pre>
10977  * <p>
10978  * This would consume an Array like this:
10979  * <pre><code>
10980 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10981   </code></pre>
10982  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10983  * @constructor
10984  * Create a new JsonReader
10985  * @param {Object} meta Metadata configuration options.
10986  * @param {Object} recordType Either an Array of field definition objects
10987  * as specified to {@link Roo.data.Record#create},
10988  * or an {@link Roo.data.Record} object
10989  * created using {@link Roo.data.Record#create}.
10990  */
10991 Roo.data.ArrayReader = function(meta, recordType){
10992     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10993 };
10994
10995 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10996     /**
10997      * Create a data block containing Roo.data.Records from an XML document.
10998      * @param {Object} o An Array of row objects which represents the dataset.
10999      * @return {Object} data A data block which is used by an Roo.data.Store object as
11000      * a cache of Roo.data.Records.
11001      */
11002     readRecords : function(o){
11003         var sid = this.meta ? this.meta.id : null;
11004         var recordType = this.recordType, fields = recordType.prototype.fields;
11005         var records = [];
11006         var root = o;
11007             for(var i = 0; i < root.length; i++){
11008                     var n = root[i];
11009                 var values = {};
11010                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11011                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11012                 var f = fields.items[j];
11013                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11014                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11015                 v = f.convert(v);
11016                 values[f.name] = v;
11017             }
11018                 var record = new recordType(values, id);
11019                 record.json = n;
11020                 records[records.length] = record;
11021             }
11022             return {
11023                 records : records,
11024                 totalRecords : records.length
11025             };
11026     }
11027 });/*
11028  * - LGPL
11029  * * 
11030  */
11031
11032 /**
11033  * @class Roo.bootstrap.ComboBox
11034  * @extends Roo.bootstrap.TriggerField
11035  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11036  * @cfg {Boolean} append (true|false) default false
11037  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11038  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11039  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11040  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11041  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11042  * @cfg {Boolean} animate default true
11043  * @cfg {Boolean} emptyResultText only for touch device
11044  * @constructor
11045  * Create a new ComboBox.
11046  * @param {Object} config Configuration options
11047  */
11048 Roo.bootstrap.ComboBox = function(config){
11049     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11050     this.addEvents({
11051         /**
11052          * @event expand
11053          * Fires when the dropdown list is expanded
11054              * @param {Roo.bootstrap.ComboBox} combo This combo box
11055              */
11056         'expand' : true,
11057         /**
11058          * @event collapse
11059          * Fires when the dropdown list is collapsed
11060              * @param {Roo.bootstrap.ComboBox} combo This combo box
11061              */
11062         'collapse' : true,
11063         /**
11064          * @event beforeselect
11065          * Fires before a list item is selected. Return false to cancel the selection.
11066              * @param {Roo.bootstrap.ComboBox} combo This combo box
11067              * @param {Roo.data.Record} record The data record returned from the underlying store
11068              * @param {Number} index The index of the selected item in the dropdown list
11069              */
11070         'beforeselect' : true,
11071         /**
11072          * @event select
11073          * Fires when a list item is selected
11074              * @param {Roo.bootstrap.ComboBox} combo This combo box
11075              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11076              * @param {Number} index The index of the selected item in the dropdown list
11077              */
11078         'select' : true,
11079         /**
11080          * @event beforequery
11081          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11082          * The event object passed has these properties:
11083              * @param {Roo.bootstrap.ComboBox} combo This combo box
11084              * @param {String} query The query
11085              * @param {Boolean} forceAll true to force "all" query
11086              * @param {Boolean} cancel true to cancel the query
11087              * @param {Object} e The query event object
11088              */
11089         'beforequery': true,
11090          /**
11091          * @event add
11092          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11093              * @param {Roo.bootstrap.ComboBox} combo This combo box
11094              */
11095         'add' : true,
11096         /**
11097          * @event edit
11098          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11099              * @param {Roo.bootstrap.ComboBox} combo This combo box
11100              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11101              */
11102         'edit' : true,
11103         /**
11104          * @event remove
11105          * Fires when the remove value from the combobox array
11106              * @param {Roo.bootstrap.ComboBox} combo This combo box
11107              */
11108         'remove' : true,
11109         /**
11110          * @event specialfilter
11111          * Fires when specialfilter
11112             * @param {Roo.bootstrap.ComboBox} combo This combo box
11113             */
11114         'specialfilter' : true
11115         
11116     });
11117     
11118     this.item = [];
11119     this.tickItems = [];
11120     
11121     this.selectedIndex = -1;
11122     if(this.mode == 'local'){
11123         if(config.queryDelay === undefined){
11124             this.queryDelay = 10;
11125         }
11126         if(config.minChars === undefined){
11127             this.minChars = 0;
11128         }
11129     }
11130 };
11131
11132 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11133      
11134     /**
11135      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11136      * rendering into an Roo.Editor, defaults to false)
11137      */
11138     /**
11139      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11140      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11141      */
11142     /**
11143      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11144      */
11145     /**
11146      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11147      * the dropdown list (defaults to undefined, with no header element)
11148      */
11149
11150      /**
11151      * @cfg {String/Roo.Template} tpl The template to use to render the output
11152      */
11153      
11154      /**
11155      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11156      */
11157     listWidth: undefined,
11158     /**
11159      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11160      * mode = 'remote' or 'text' if mode = 'local')
11161      */
11162     displayField: undefined,
11163     
11164     /**
11165      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11166      * mode = 'remote' or 'value' if mode = 'local'). 
11167      * Note: use of a valueField requires the user make a selection
11168      * in order for a value to be mapped.
11169      */
11170     valueField: undefined,
11171     
11172     
11173     /**
11174      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11175      * field's data value (defaults to the underlying DOM element's name)
11176      */
11177     hiddenName: undefined,
11178     /**
11179      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11180      */
11181     listClass: '',
11182     /**
11183      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11184      */
11185     selectedClass: 'active',
11186     
11187     /**
11188      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11189      */
11190     shadow:'sides',
11191     /**
11192      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11193      * anchor positions (defaults to 'tl-bl')
11194      */
11195     listAlign: 'tl-bl?',
11196     /**
11197      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11198      */
11199     maxHeight: 300,
11200     /**
11201      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11202      * query specified by the allQuery config option (defaults to 'query')
11203      */
11204     triggerAction: 'query',
11205     /**
11206      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11207      * (defaults to 4, does not apply if editable = false)
11208      */
11209     minChars : 4,
11210     /**
11211      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11212      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11213      */
11214     typeAhead: false,
11215     /**
11216      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11217      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11218      */
11219     queryDelay: 500,
11220     /**
11221      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11222      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11223      */
11224     pageSize: 0,
11225     /**
11226      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11227      * when editable = true (defaults to false)
11228      */
11229     selectOnFocus:false,
11230     /**
11231      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11232      */
11233     queryParam: 'query',
11234     /**
11235      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11236      * when mode = 'remote' (defaults to 'Loading...')
11237      */
11238     loadingText: 'Loading...',
11239     /**
11240      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11241      */
11242     resizable: false,
11243     /**
11244      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11245      */
11246     handleHeight : 8,
11247     /**
11248      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11249      * traditional select (defaults to true)
11250      */
11251     editable: true,
11252     /**
11253      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11254      */
11255     allQuery: '',
11256     /**
11257      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11258      */
11259     mode: 'remote',
11260     /**
11261      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11262      * listWidth has a higher value)
11263      */
11264     minListWidth : 70,
11265     /**
11266      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11267      * allow the user to set arbitrary text into the field (defaults to false)
11268      */
11269     forceSelection:false,
11270     /**
11271      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11272      * if typeAhead = true (defaults to 250)
11273      */
11274     typeAheadDelay : 250,
11275     /**
11276      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11277      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11278      */
11279     valueNotFoundText : undefined,
11280     /**
11281      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11282      */
11283     blockFocus : false,
11284     
11285     /**
11286      * @cfg {Boolean} disableClear Disable showing of clear button.
11287      */
11288     disableClear : false,
11289     /**
11290      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11291      */
11292     alwaysQuery : false,
11293     
11294     /**
11295      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11296      */
11297     multiple : false,
11298     
11299     /**
11300      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11301      */
11302     invalidClass : "has-warning",
11303     
11304     /**
11305      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11306      */
11307     validClass : "has-success",
11308     
11309     /**
11310      * @cfg {Boolean} specialFilter (true|false) special filter default false
11311      */
11312     specialFilter : false,
11313     
11314     /**
11315      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11316      */
11317     mobileTouchView : true,
11318     
11319     //private
11320     addicon : false,
11321     editicon: false,
11322     
11323     page: 0,
11324     hasQuery: false,
11325     append: false,
11326     loadNext: false,
11327     autoFocus : true,
11328     tickable : false,
11329     btnPosition : 'right',
11330     triggerList : true,
11331     showToggleBtn : true,
11332     animate : true,
11333     emptyResultText: 'Empty',
11334     // element that contains real text value.. (when hidden is used..)
11335     
11336     getAutoCreate : function()
11337     {
11338         var cfg = false;
11339         
11340         /*
11341          * Touch Devices
11342          */
11343         
11344         if(Roo.isTouch && this.mobileTouchView){
11345             cfg = this.getAutoCreateTouchView();
11346             return cfg;;
11347         }
11348         
11349         /*
11350          *  Normal ComboBox
11351          */
11352         if(!this.tickable){
11353             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11354             return cfg;
11355         }
11356         
11357         /*
11358          *  ComboBox with tickable selections
11359          */
11360              
11361         var align = this.labelAlign || this.parentLabelAlign();
11362         
11363         cfg = {
11364             cls : 'form-group roo-combobox-tickable' //input-group
11365         };
11366         
11367         var buttons = {
11368             tag : 'div',
11369             cls : 'tickable-buttons',
11370             cn : [
11371                 {
11372                     tag : 'button',
11373                     type : 'button',
11374                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11375                     html : 'Edit'
11376                 },
11377                 {
11378                     tag : 'button',
11379                     type : 'button',
11380                     name : 'ok',
11381                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11382                     html : 'Done'
11383                 },
11384                 {
11385                     tag : 'button',
11386                     type : 'button',
11387                     name : 'cancel',
11388                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11389                     html : 'Cancel'
11390                 }
11391             ]
11392         };
11393         
11394         if(this.editable){
11395             buttons.cn.unshift({
11396                 tag: 'input',
11397                 cls: 'select2-search-field-input'
11398             });
11399         }
11400         
11401         var _this = this;
11402         
11403         Roo.each(buttons.cn, function(c){
11404             if (_this.size) {
11405                 c.cls += ' btn-' + _this.size;
11406             }
11407
11408             if (_this.disabled) {
11409                 c.disabled = true;
11410             }
11411         });
11412         
11413         var box = {
11414             tag: 'div',
11415             cn: [
11416                 {
11417                     tag: 'input',
11418                     type : 'hidden',
11419                     cls: 'form-hidden-field'
11420                 },
11421                 {
11422                     tag: 'ul',
11423                     cls: 'select2-choices',
11424                     cn:[
11425                         {
11426                             tag: 'li',
11427                             cls: 'select2-search-field',
11428                             cn: [
11429
11430                                 buttons
11431                             ]
11432                         }
11433                     ]
11434                 }
11435             ]
11436         }
11437         
11438         var combobox = {
11439             cls: 'select2-container input-group select2-container-multi',
11440             cn: [
11441                 box
11442 //                {
11443 //                    tag: 'ul',
11444 //                    cls: 'typeahead typeahead-long dropdown-menu',
11445 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11446 //                }
11447             ]
11448         };
11449         
11450         if(this.hasFeedback && !this.allowBlank){
11451             
11452             var feedback = {
11453                 tag: 'span',
11454                 cls: 'glyphicon form-control-feedback'
11455             };
11456
11457             combobox.cn.push(feedback);
11458         }
11459         
11460         if (align ==='left' && this.fieldLabel.length) {
11461             
11462                 Roo.log("left and has label");
11463                 cfg.cn = [
11464                     
11465                     {
11466                         tag: 'label',
11467                         'for' :  id,
11468                         cls : 'control-label col-sm-' + this.labelWidth,
11469                         html : this.fieldLabel
11470                         
11471                     },
11472                     {
11473                         cls : "col-sm-" + (12 - this.labelWidth), 
11474                         cn: [
11475                             combobox
11476                         ]
11477                     }
11478                     
11479                 ];
11480         } else if ( this.fieldLabel.length) {
11481                 Roo.log(" label");
11482                  cfg.cn = [
11483                    
11484                     {
11485                         tag: 'label',
11486                         //cls : 'input-group-addon',
11487                         html : this.fieldLabel
11488                         
11489                     },
11490                     
11491                     combobox
11492                     
11493                 ];
11494
11495         } else {
11496             
11497                 Roo.log(" no label && no align");
11498                 cfg = combobox
11499                      
11500                 
11501         }
11502          
11503         var settings=this;
11504         ['xs','sm','md','lg'].map(function(size){
11505             if (settings[size]) {
11506                 cfg.cls += ' col-' + size + '-' + settings[size];
11507             }
11508         });
11509         
11510         return cfg;
11511         
11512     },
11513     
11514     // private
11515     initEvents: function()
11516     {
11517         
11518         if (!this.store) {
11519             throw "can not find store for combo";
11520         }
11521         
11522         this.store = Roo.factory(this.store, Roo.data);
11523         
11524         /*
11525          * Touch Devices
11526          */
11527         
11528         if(Roo.isTouch && this.mobileTouchView){
11529             this.initTouchView();
11530             return;
11531         }
11532         
11533         if(this.tickable){
11534             this.initTickableEvents();
11535             return;
11536         }
11537         
11538         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11539         
11540         if(this.hiddenName){
11541             
11542             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11543             
11544             this.hiddenField.dom.value =
11545                 this.hiddenValue !== undefined ? this.hiddenValue :
11546                 this.value !== undefined ? this.value : '';
11547
11548             // prevent input submission
11549             this.el.dom.removeAttribute('name');
11550             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11551              
11552              
11553         }
11554         //if(Roo.isGecko){
11555         //    this.el.dom.setAttribute('autocomplete', 'off');
11556         //}
11557         
11558         var cls = 'x-combo-list';
11559         
11560         //this.list = new Roo.Layer({
11561         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11562         //});
11563         
11564         var _this = this;
11565         
11566         (function(){
11567             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11568             _this.list.setWidth(lw);
11569         }).defer(100);
11570         
11571         this.list.on('mouseover', this.onViewOver, this);
11572         this.list.on('mousemove', this.onViewMove, this);
11573         
11574         this.list.on('scroll', this.onViewScroll, this);
11575         
11576         /*
11577         this.list.swallowEvent('mousewheel');
11578         this.assetHeight = 0;
11579
11580         if(this.title){
11581             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11582             this.assetHeight += this.header.getHeight();
11583         }
11584
11585         this.innerList = this.list.createChild({cls:cls+'-inner'});
11586         this.innerList.on('mouseover', this.onViewOver, this);
11587         this.innerList.on('mousemove', this.onViewMove, this);
11588         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11589         
11590         if(this.allowBlank && !this.pageSize && !this.disableClear){
11591             this.footer = this.list.createChild({cls:cls+'-ft'});
11592             this.pageTb = new Roo.Toolbar(this.footer);
11593            
11594         }
11595         if(this.pageSize){
11596             this.footer = this.list.createChild({cls:cls+'-ft'});
11597             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11598                     {pageSize: this.pageSize});
11599             
11600         }
11601         
11602         if (this.pageTb && this.allowBlank && !this.disableClear) {
11603             var _this = this;
11604             this.pageTb.add(new Roo.Toolbar.Fill(), {
11605                 cls: 'x-btn-icon x-btn-clear',
11606                 text: '&#160;',
11607                 handler: function()
11608                 {
11609                     _this.collapse();
11610                     _this.clearValue();
11611                     _this.onSelect(false, -1);
11612                 }
11613             });
11614         }
11615         if (this.footer) {
11616             this.assetHeight += this.footer.getHeight();
11617         }
11618         */
11619             
11620         if(!this.tpl){
11621             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11622         }
11623
11624         this.view = new Roo.View(this.list, this.tpl, {
11625             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11626         });
11627         //this.view.wrapEl.setDisplayed(false);
11628         this.view.on('click', this.onViewClick, this);
11629         
11630         
11631         
11632         this.store.on('beforeload', this.onBeforeLoad, this);
11633         this.store.on('load', this.onLoad, this);
11634         this.store.on('loadexception', this.onLoadException, this);
11635         /*
11636         if(this.resizable){
11637             this.resizer = new Roo.Resizable(this.list,  {
11638                pinned:true, handles:'se'
11639             });
11640             this.resizer.on('resize', function(r, w, h){
11641                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11642                 this.listWidth = w;
11643                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11644                 this.restrictHeight();
11645             }, this);
11646             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11647         }
11648         */
11649         if(!this.editable){
11650             this.editable = true;
11651             this.setEditable(false);
11652         }
11653         
11654         /*
11655         
11656         if (typeof(this.events.add.listeners) != 'undefined') {
11657             
11658             this.addicon = this.wrap.createChild(
11659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11660        
11661             this.addicon.on('click', function(e) {
11662                 this.fireEvent('add', this);
11663             }, this);
11664         }
11665         if (typeof(this.events.edit.listeners) != 'undefined') {
11666             
11667             this.editicon = this.wrap.createChild(
11668                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11669             if (this.addicon) {
11670                 this.editicon.setStyle('margin-left', '40px');
11671             }
11672             this.editicon.on('click', function(e) {
11673                 
11674                 // we fire even  if inothing is selected..
11675                 this.fireEvent('edit', this, this.lastData );
11676                 
11677             }, this);
11678         }
11679         */
11680         
11681         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11682             "up" : function(e){
11683                 this.inKeyMode = true;
11684                 this.selectPrev();
11685             },
11686
11687             "down" : function(e){
11688                 if(!this.isExpanded()){
11689                     this.onTriggerClick();
11690                 }else{
11691                     this.inKeyMode = true;
11692                     this.selectNext();
11693                 }
11694             },
11695
11696             "enter" : function(e){
11697 //                this.onViewClick();
11698                 //return true;
11699                 this.collapse();
11700                 
11701                 if(this.fireEvent("specialkey", this, e)){
11702                     this.onViewClick(false);
11703                 }
11704                 
11705                 return true;
11706             },
11707
11708             "esc" : function(e){
11709                 this.collapse();
11710             },
11711
11712             "tab" : function(e){
11713                 this.collapse();
11714                 
11715                 if(this.fireEvent("specialkey", this, e)){
11716                     this.onViewClick(false);
11717                 }
11718                 
11719                 return true;
11720             },
11721
11722             scope : this,
11723
11724             doRelay : function(foo, bar, hname){
11725                 if(hname == 'down' || this.scope.isExpanded()){
11726                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11727                 }
11728                 return true;
11729             },
11730
11731             forceKeyDown: true
11732         });
11733         
11734         
11735         this.queryDelay = Math.max(this.queryDelay || 10,
11736                 this.mode == 'local' ? 10 : 250);
11737         
11738         
11739         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11740         
11741         if(this.typeAhead){
11742             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11743         }
11744         if(this.editable !== false){
11745             this.inputEl().on("keyup", this.onKeyUp, this);
11746         }
11747         if(this.forceSelection){
11748             this.inputEl().on('blur', this.doForce, this);
11749         }
11750         
11751         if(this.multiple){
11752             this.choices = this.el.select('ul.select2-choices', true).first();
11753             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11754         }
11755     },
11756     
11757     initTickableEvents: function()
11758     {   
11759         this.createList();
11760         
11761         if(this.hiddenName){
11762             
11763             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11764             
11765             this.hiddenField.dom.value =
11766                 this.hiddenValue !== undefined ? this.hiddenValue :
11767                 this.value !== undefined ? this.value : '';
11768
11769             // prevent input submission
11770             this.el.dom.removeAttribute('name');
11771             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11772              
11773              
11774         }
11775         
11776 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11777         
11778         this.choices = this.el.select('ul.select2-choices', true).first();
11779         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11780         if(this.triggerList){
11781             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11782         }
11783          
11784         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11785         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11786         
11787         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11788         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11789         
11790         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11791         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11792         
11793         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11794         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11795         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11796         
11797         this.okBtn.hide();
11798         this.cancelBtn.hide();
11799         
11800         var _this = this;
11801         
11802         (function(){
11803             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11804             _this.list.setWidth(lw);
11805         }).defer(100);
11806         
11807         this.list.on('mouseover', this.onViewOver, this);
11808         this.list.on('mousemove', this.onViewMove, this);
11809         
11810         this.list.on('scroll', this.onViewScroll, this);
11811         
11812         if(!this.tpl){
11813             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>';
11814         }
11815
11816         this.view = new Roo.View(this.list, this.tpl, {
11817             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11818         });
11819         
11820         //this.view.wrapEl.setDisplayed(false);
11821         this.view.on('click', this.onViewClick, this);
11822         
11823         
11824         
11825         this.store.on('beforeload', this.onBeforeLoad, this);
11826         this.store.on('load', this.onLoad, this);
11827         this.store.on('loadexception', this.onLoadException, this);
11828         
11829         if(this.editable){
11830             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11831                 "up" : function(e){
11832                     this.inKeyMode = true;
11833                     this.selectPrev();
11834                 },
11835
11836                 "down" : function(e){
11837                     this.inKeyMode = true;
11838                     this.selectNext();
11839                 },
11840
11841                 "enter" : function(e){
11842                     if(this.fireEvent("specialkey", this, e)){
11843                         this.onViewClick(false);
11844                     }
11845                     
11846                     return true;
11847                 },
11848
11849                 "esc" : function(e){
11850                     this.onTickableFooterButtonClick(e, false, false);
11851                 },
11852
11853                 "tab" : function(e){
11854                     this.fireEvent("specialkey", this, e);
11855                     
11856                     this.onTickableFooterButtonClick(e, false, false);
11857                     
11858                     return true;
11859                 },
11860
11861                 scope : this,
11862
11863                 doRelay : function(e, fn, key){
11864                     if(this.scope.isExpanded()){
11865                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11866                     }
11867                     return true;
11868                 },
11869
11870                 forceKeyDown: true
11871             });
11872         }
11873         
11874         this.queryDelay = Math.max(this.queryDelay || 10,
11875                 this.mode == 'local' ? 10 : 250);
11876         
11877         
11878         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11879         
11880         if(this.typeAhead){
11881             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11882         }
11883         
11884         if(this.editable !== false){
11885             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11886         }
11887         
11888     },
11889
11890     onDestroy : function(){
11891         if(this.view){
11892             this.view.setStore(null);
11893             this.view.el.removeAllListeners();
11894             this.view.el.remove();
11895             this.view.purgeListeners();
11896         }
11897         if(this.list){
11898             this.list.dom.innerHTML  = '';
11899         }
11900         
11901         if(this.store){
11902             this.store.un('beforeload', this.onBeforeLoad, this);
11903             this.store.un('load', this.onLoad, this);
11904             this.store.un('loadexception', this.onLoadException, this);
11905         }
11906         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11907     },
11908
11909     // private
11910     fireKey : function(e){
11911         if(e.isNavKeyPress() && !this.list.isVisible()){
11912             this.fireEvent("specialkey", this, e);
11913         }
11914     },
11915
11916     // private
11917     onResize: function(w, h){
11918 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11919 //        
11920 //        if(typeof w != 'number'){
11921 //            // we do not handle it!?!?
11922 //            return;
11923 //        }
11924 //        var tw = this.trigger.getWidth();
11925 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11926 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11927 //        var x = w - tw;
11928 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11929 //            
11930 //        //this.trigger.setStyle('left', x+'px');
11931 //        
11932 //        if(this.list && this.listWidth === undefined){
11933 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11934 //            this.list.setWidth(lw);
11935 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11936 //        }
11937         
11938     
11939         
11940     },
11941
11942     /**
11943      * Allow or prevent the user from directly editing the field text.  If false is passed,
11944      * the user will only be able to select from the items defined in the dropdown list.  This method
11945      * is the runtime equivalent of setting the 'editable' config option at config time.
11946      * @param {Boolean} value True to allow the user to directly edit the field text
11947      */
11948     setEditable : function(value){
11949         if(value == this.editable){
11950             return;
11951         }
11952         this.editable = value;
11953         if(!value){
11954             this.inputEl().dom.setAttribute('readOnly', true);
11955             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11956             this.inputEl().addClass('x-combo-noedit');
11957         }else{
11958             this.inputEl().dom.setAttribute('readOnly', false);
11959             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11960             this.inputEl().removeClass('x-combo-noedit');
11961         }
11962     },
11963
11964     // private
11965     
11966     onBeforeLoad : function(combo,opts){
11967         if(!this.hasFocus){
11968             return;
11969         }
11970          if (!opts.add) {
11971             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11972          }
11973         this.restrictHeight();
11974         this.selectedIndex = -1;
11975     },
11976
11977     // private
11978     onLoad : function(){
11979         
11980         this.hasQuery = false;
11981         
11982         if(!this.hasFocus){
11983             return;
11984         }
11985         
11986         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11987             this.loading.hide();
11988         }
11989              
11990         if(this.store.getCount() > 0){
11991             this.expand();
11992             this.restrictHeight();
11993             if(this.lastQuery == this.allQuery){
11994                 if(this.editable && !this.tickable){
11995                     this.inputEl().dom.select();
11996                 }
11997                 
11998                 if(
11999                     !this.selectByValue(this.value, true) &&
12000                     this.autoFocus && 
12001                     (
12002                         !this.store.lastOptions ||
12003                         typeof(this.store.lastOptions.add) == 'undefined' || 
12004                         this.store.lastOptions.add != true
12005                     )
12006                 ){
12007                     this.select(0, true);
12008                 }
12009             }else{
12010                 if(this.autoFocus){
12011                     this.selectNext();
12012                 }
12013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12014                     this.taTask.delay(this.typeAheadDelay);
12015                 }
12016             }
12017         }else{
12018             this.onEmptyResults();
12019         }
12020         
12021         //this.el.focus();
12022     },
12023     // private
12024     onLoadException : function()
12025     {
12026         this.hasQuery = false;
12027         
12028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12029             this.loading.hide();
12030         }
12031         
12032         if(this.tickable && this.editable){
12033             return;
12034         }
12035         
12036         this.collapse();
12037         
12038         Roo.log(this.store.reader.jsonData);
12039         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12040             // fixme
12041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12042         }
12043         
12044         
12045     },
12046     // private
12047     onTypeAhead : function(){
12048         if(this.store.getCount() > 0){
12049             var r = this.store.getAt(0);
12050             var newValue = r.data[this.displayField];
12051             var len = newValue.length;
12052             var selStart = this.getRawValue().length;
12053             
12054             if(selStart != len){
12055                 this.setRawValue(newValue);
12056                 this.selectText(selStart, newValue.length);
12057             }
12058         }
12059     },
12060
12061     // private
12062     onSelect : function(record, index){
12063         
12064         if(this.fireEvent('beforeselect', this, record, index) !== false){
12065         
12066             this.setFromData(index > -1 ? record.data : false);
12067             
12068             this.collapse();
12069             this.fireEvent('select', this, record, index);
12070         }
12071     },
12072
12073     /**
12074      * Returns the currently selected field value or empty string if no value is set.
12075      * @return {String} value The selected value
12076      */
12077     getValue : function(){
12078         
12079         if(this.multiple){
12080             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12081         }
12082         
12083         if(this.valueField){
12084             return typeof this.value != 'undefined' ? this.value : '';
12085         }else{
12086             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12087         }
12088     },
12089
12090     /**
12091      * Clears any text/value currently set in the field
12092      */
12093     clearValue : function(){
12094         if(this.hiddenField){
12095             this.hiddenField.dom.value = '';
12096         }
12097         this.value = '';
12098         this.setRawValue('');
12099         this.lastSelectionText = '';
12100         this.lastData = false;
12101         
12102         var close = this.closeTriggerEl();
12103         
12104         if(close){
12105             close.hide();
12106         }
12107         
12108     },
12109
12110     /**
12111      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12112      * will be displayed in the field.  If the value does not match the data value of an existing item,
12113      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12114      * Otherwise the field will be blank (although the value will still be set).
12115      * @param {String} value The value to match
12116      */
12117     setValue : function(v){
12118         if(this.multiple){
12119             this.syncValue();
12120             return;
12121         }
12122         
12123         var text = v;
12124         if(this.valueField){
12125             var r = this.findRecord(this.valueField, v);
12126             if(r){
12127                 text = r.data[this.displayField];
12128             }else if(this.valueNotFoundText !== undefined){
12129                 text = this.valueNotFoundText;
12130             }
12131         }
12132         this.lastSelectionText = text;
12133         if(this.hiddenField){
12134             this.hiddenField.dom.value = v;
12135         }
12136         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12137         this.value = v;
12138         
12139         var close = this.closeTriggerEl();
12140         
12141         if(close){
12142             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12143         }
12144     },
12145     /**
12146      * @property {Object} the last set data for the element
12147      */
12148     
12149     lastData : false,
12150     /**
12151      * Sets the value of the field based on a object which is related to the record format for the store.
12152      * @param {Object} value the value to set as. or false on reset?
12153      */
12154     setFromData : function(o){
12155         
12156         if(this.multiple){
12157             this.addItem(o);
12158             return;
12159         }
12160             
12161         var dv = ''; // display value
12162         var vv = ''; // value value..
12163         this.lastData = o;
12164         if (this.displayField) {
12165             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12166         } else {
12167             // this is an error condition!!!
12168             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12169         }
12170         
12171         if(this.valueField){
12172             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12173         }
12174         
12175         var close = this.closeTriggerEl();
12176         
12177         if(close){
12178             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12179         }
12180         
12181         if(this.hiddenField){
12182             this.hiddenField.dom.value = vv;
12183             
12184             this.lastSelectionText = dv;
12185             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12186             this.value = vv;
12187             return;
12188         }
12189         // no hidden field.. - we store the value in 'value', but still display
12190         // display field!!!!
12191         this.lastSelectionText = dv;
12192         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12193         this.value = vv;
12194         
12195         
12196         
12197     },
12198     // private
12199     reset : function(){
12200         // overridden so that last data is reset..
12201         
12202         if(this.multiple){
12203             this.clearItem();
12204             return;
12205         }
12206         
12207         this.setValue(this.originalValue);
12208         this.clearInvalid();
12209         this.lastData = false;
12210         if (this.view) {
12211             this.view.clearSelections();
12212         }
12213     },
12214     // private
12215     findRecord : function(prop, value){
12216         var record;
12217         if(this.store.getCount() > 0){
12218             this.store.each(function(r){
12219                 if(r.data[prop] == value){
12220                     record = r;
12221                     return false;
12222                 }
12223                 return true;
12224             });
12225         }
12226         return record;
12227     },
12228     
12229     getName: function()
12230     {
12231         // returns hidden if it's set..
12232         if (!this.rendered) {return ''};
12233         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12234         
12235     },
12236     // private
12237     onViewMove : function(e, t){
12238         this.inKeyMode = false;
12239     },
12240
12241     // private
12242     onViewOver : function(e, t){
12243         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12244             return;
12245         }
12246         var item = this.view.findItemFromChild(t);
12247         
12248         if(item){
12249             var index = this.view.indexOf(item);
12250             this.select(index, false);
12251         }
12252     },
12253
12254     // private
12255     onViewClick : function(view, doFocus, el, e)
12256     {
12257         var index = this.view.getSelectedIndexes()[0];
12258         
12259         var r = this.store.getAt(index);
12260         
12261         if(this.tickable){
12262             
12263             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12264                 return;
12265             }
12266             
12267             var rm = false;
12268             var _this = this;
12269             
12270             Roo.each(this.tickItems, function(v,k){
12271                 
12272                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12273                     _this.tickItems.splice(k, 1);
12274                     
12275                     if(typeof(e) == 'undefined' && view == false){
12276                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12277                     }
12278                     
12279                     rm = true;
12280                     return;
12281                 }
12282             });
12283             
12284             if(rm){
12285                 return;
12286             }
12287             
12288             this.tickItems.push(r.data);
12289             
12290             if(typeof(e) == 'undefined' && view == false){
12291                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12292             }
12293                     
12294             return;
12295         }
12296         
12297         if(r){
12298             this.onSelect(r, index);
12299         }
12300         if(doFocus !== false && !this.blockFocus){
12301             this.inputEl().focus();
12302         }
12303     },
12304
12305     // private
12306     restrictHeight : function(){
12307         //this.innerList.dom.style.height = '';
12308         //var inner = this.innerList.dom;
12309         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12310         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12311         //this.list.beginUpdate();
12312         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12313         this.list.alignTo(this.inputEl(), this.listAlign);
12314         this.list.alignTo(this.inputEl(), this.listAlign);
12315         //this.list.endUpdate();
12316     },
12317
12318     // private
12319     onEmptyResults : function(){
12320         
12321         if(this.tickable && this.editable){
12322             this.restrictHeight();
12323             return;
12324         }
12325         
12326         this.collapse();
12327     },
12328
12329     /**
12330      * Returns true if the dropdown list is expanded, else false.
12331      */
12332     isExpanded : function(){
12333         return this.list.isVisible();
12334     },
12335
12336     /**
12337      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12338      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12339      * @param {String} value The data value of the item to select
12340      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12341      * selected item if it is not currently in view (defaults to true)
12342      * @return {Boolean} True if the value matched an item in the list, else false
12343      */
12344     selectByValue : function(v, scrollIntoView){
12345         if(v !== undefined && v !== null){
12346             var r = this.findRecord(this.valueField || this.displayField, v);
12347             if(r){
12348                 this.select(this.store.indexOf(r), scrollIntoView);
12349                 return true;
12350             }
12351         }
12352         return false;
12353     },
12354
12355     /**
12356      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12358      * @param {Number} index The zero-based index of the list item to select
12359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12360      * selected item if it is not currently in view (defaults to true)
12361      */
12362     select : function(index, scrollIntoView){
12363         this.selectedIndex = index;
12364         this.view.select(index);
12365         if(scrollIntoView !== false){
12366             var el = this.view.getNode(index);
12367             /*
12368              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12369              */
12370             if(el){
12371                 this.list.scrollChildIntoView(el, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     selectNext : function(){
12378         var ct = this.store.getCount();
12379         if(ct > 0){
12380             if(this.selectedIndex == -1){
12381                 this.select(0);
12382             }else if(this.selectedIndex < ct-1){
12383                 this.select(this.selectedIndex+1);
12384             }
12385         }
12386     },
12387
12388     // private
12389     selectPrev : function(){
12390         var ct = this.store.getCount();
12391         if(ct > 0){
12392             if(this.selectedIndex == -1){
12393                 this.select(0);
12394             }else if(this.selectedIndex != 0){
12395                 this.select(this.selectedIndex-1);
12396             }
12397         }
12398     },
12399
12400     // private
12401     onKeyUp : function(e){
12402         if(this.editable !== false && !e.isSpecialKey()){
12403             this.lastKey = e.getKey();
12404             this.dqTask.delay(this.queryDelay);
12405         }
12406     },
12407
12408     // private
12409     validateBlur : function(){
12410         return !this.list || !this.list.isVisible();   
12411     },
12412
12413     // private
12414     initQuery : function(){
12415         
12416         var v = this.getRawValue();
12417         
12418         if(this.tickable && this.editable){
12419             v = this.tickableInputEl().getValue();
12420         }
12421         
12422         this.doQuery(v);
12423     },
12424
12425     // private
12426     doForce : function(){
12427         if(this.inputEl().dom.value.length > 0){
12428             this.inputEl().dom.value =
12429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12430              
12431         }
12432     },
12433
12434     /**
12435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12436      * query allowing the query action to be canceled if needed.
12437      * @param {String} query The SQL query to execute
12438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12440      * saved in the current store (defaults to false)
12441      */
12442     doQuery : function(q, forceAll){
12443         
12444         if(q === undefined || q === null){
12445             q = '';
12446         }
12447         var qe = {
12448             query: q,
12449             forceAll: forceAll,
12450             combo: this,
12451             cancel:false
12452         };
12453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12454             return false;
12455         }
12456         q = qe.query;
12457         
12458         forceAll = qe.forceAll;
12459         if(forceAll === true || (q.length >= this.minChars)){
12460             
12461             this.hasQuery = true;
12462             
12463             if(this.lastQuery != q || this.alwaysQuery){
12464                 this.lastQuery = q;
12465                 if(this.mode == 'local'){
12466                     this.selectedIndex = -1;
12467                     if(forceAll){
12468                         this.store.clearFilter();
12469                     }else{
12470                         
12471                         if(this.specialFilter){
12472                             this.fireEvent('specialfilter', this);
12473                             this.onLoad();
12474                             return;
12475                         }
12476                         
12477                         this.store.filter(this.displayField, q);
12478                     }
12479                     
12480                     this.store.fireEvent("datachanged", this.store);
12481                     
12482                     this.onLoad();
12483                     
12484                     
12485                 }else{
12486                     
12487                     this.store.baseParams[this.queryParam] = q;
12488                     
12489                     var options = {params : this.getParams(q)};
12490                     
12491                     if(this.loadNext){
12492                         options.add = true;
12493                         options.params.start = this.page * this.pageSize;
12494                     }
12495                     
12496                     this.store.load(options);
12497                     
12498                     /*
12499                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12500                      *  we should expand the list on onLoad
12501                      *  so command out it
12502                      */
12503 //                    this.expand();
12504                 }
12505             }else{
12506                 this.selectedIndex = -1;
12507                 this.onLoad();   
12508             }
12509         }
12510         
12511         this.loadNext = false;
12512     },
12513     
12514     // private
12515     getParams : function(q){
12516         var p = {};
12517         //p[this.queryParam] = q;
12518         
12519         if(this.pageSize){
12520             p.start = 0;
12521             p.limit = this.pageSize;
12522         }
12523         return p;
12524     },
12525
12526     /**
12527      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12528      */
12529     collapse : function(){
12530         if(!this.isExpanded()){
12531             return;
12532         }
12533         
12534         this.list.hide();
12535         
12536         if(this.tickable){
12537             this.hasFocus = false;
12538             this.okBtn.hide();
12539             this.cancelBtn.hide();
12540             this.trigger.show();
12541             
12542             if(this.editable){
12543                 this.tickableInputEl().dom.value = '';
12544                 this.tickableInputEl().blur();
12545             }
12546             
12547         }
12548         
12549         Roo.get(document).un('mousedown', this.collapseIf, this);
12550         Roo.get(document).un('mousewheel', this.collapseIf, this);
12551         if (!this.editable) {
12552             Roo.get(document).un('keydown', this.listKeyPress, this);
12553         }
12554         this.fireEvent('collapse', this);
12555     },
12556
12557     // private
12558     collapseIf : function(e){
12559         var in_combo  = e.within(this.el);
12560         var in_list =  e.within(this.list);
12561         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12562         
12563         if (in_combo || in_list || is_list) {
12564             //e.stopPropagation();
12565             return;
12566         }
12567         
12568         if(this.tickable){
12569             this.onTickableFooterButtonClick(e, false, false);
12570         }
12571
12572         this.collapse();
12573         
12574     },
12575
12576     /**
12577      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12578      */
12579     expand : function(){
12580        
12581         if(this.isExpanded() || !this.hasFocus){
12582             return;
12583         }
12584         
12585         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12586         this.list.setWidth(lw);
12587         
12588         
12589          Roo.log('expand');
12590         
12591         this.list.show();
12592         
12593         this.restrictHeight();
12594         
12595         if(this.tickable){
12596             
12597             this.tickItems = Roo.apply([], this.item);
12598             
12599             this.okBtn.show();
12600             this.cancelBtn.show();
12601             this.trigger.hide();
12602             
12603             if(this.editable){
12604                 this.tickableInputEl().focus();
12605             }
12606             
12607         }
12608         
12609         Roo.get(document).on('mousedown', this.collapseIf, this);
12610         Roo.get(document).on('mousewheel', this.collapseIf, this);
12611         if (!this.editable) {
12612             Roo.get(document).on('keydown', this.listKeyPress, this);
12613         }
12614         
12615         this.fireEvent('expand', this);
12616     },
12617
12618     // private
12619     // Implements the default empty TriggerField.onTriggerClick function
12620     onTriggerClick : function(e)
12621     {
12622         Roo.log('trigger click');
12623         
12624         if(this.disabled || !this.triggerList){
12625             return;
12626         }
12627         
12628         this.page = 0;
12629         this.loadNext = false;
12630         
12631         if(this.isExpanded()){
12632             this.collapse();
12633             if (!this.blockFocus) {
12634                 this.inputEl().focus();
12635             }
12636             
12637         }else {
12638             this.hasFocus = true;
12639             if(this.triggerAction == 'all') {
12640                 this.doQuery(this.allQuery, true);
12641             } else {
12642                 this.doQuery(this.getRawValue());
12643             }
12644             if (!this.blockFocus) {
12645                 this.inputEl().focus();
12646             }
12647         }
12648     },
12649     
12650     onTickableTriggerClick : function(e)
12651     {
12652         if(this.disabled){
12653             return;
12654         }
12655         
12656         this.page = 0;
12657         this.loadNext = false;
12658         this.hasFocus = true;
12659         
12660         if(this.triggerAction == 'all') {
12661             this.doQuery(this.allQuery, true);
12662         } else {
12663             this.doQuery(this.getRawValue());
12664         }
12665     },
12666     
12667     onSearchFieldClick : function(e)
12668     {
12669         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12670             this.onTickableFooterButtonClick(e, false, false);
12671             return;
12672         }
12673         
12674         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12675             return;
12676         }
12677         
12678         this.page = 0;
12679         this.loadNext = false;
12680         this.hasFocus = true;
12681         
12682         if(this.triggerAction == 'all') {
12683             this.doQuery(this.allQuery, true);
12684         } else {
12685             this.doQuery(this.getRawValue());
12686         }
12687     },
12688     
12689     listKeyPress : function(e)
12690     {
12691         //Roo.log('listkeypress');
12692         // scroll to first matching element based on key pres..
12693         if (e.isSpecialKey()) {
12694             return false;
12695         }
12696         var k = String.fromCharCode(e.getKey()).toUpperCase();
12697         //Roo.log(k);
12698         var match  = false;
12699         var csel = this.view.getSelectedNodes();
12700         var cselitem = false;
12701         if (csel.length) {
12702             var ix = this.view.indexOf(csel[0]);
12703             cselitem  = this.store.getAt(ix);
12704             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12705                 cselitem = false;
12706             }
12707             
12708         }
12709         
12710         this.store.each(function(v) { 
12711             if (cselitem) {
12712                 // start at existing selection.
12713                 if (cselitem.id == v.id) {
12714                     cselitem = false;
12715                 }
12716                 return true;
12717             }
12718                 
12719             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12720                 match = this.store.indexOf(v);
12721                 return false;
12722             }
12723             return true;
12724         }, this);
12725         
12726         if (match === false) {
12727             return true; // no more action?
12728         }
12729         // scroll to?
12730         this.view.select(match);
12731         var sn = Roo.get(this.view.getSelectedNodes()[0])
12732         sn.scrollIntoView(sn.dom.parentNode, false);
12733     },
12734     
12735     onViewScroll : function(e, t){
12736         
12737         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){
12738             return;
12739         }
12740         
12741         this.hasQuery = true;
12742         
12743         this.loading = this.list.select('.loading', true).first();
12744         
12745         if(this.loading === null){
12746             this.list.createChild({
12747                 tag: 'div',
12748                 cls: 'loading select2-more-results select2-active',
12749                 html: 'Loading more results...'
12750             })
12751             
12752             this.loading = this.list.select('.loading', true).first();
12753             
12754             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12755             
12756             this.loading.hide();
12757         }
12758         
12759         this.loading.show();
12760         
12761         var _combo = this;
12762         
12763         this.page++;
12764         this.loadNext = true;
12765         
12766         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12767         
12768         return;
12769     },
12770     
12771     addItem : function(o)
12772     {   
12773         var dv = ''; // display value
12774         
12775         if (this.displayField) {
12776             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12777         } else {
12778             // this is an error condition!!!
12779             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12780         }
12781         
12782         if(!dv.length){
12783             return;
12784         }
12785         
12786         var choice = this.choices.createChild({
12787             tag: 'li',
12788             cls: 'select2-search-choice',
12789             cn: [
12790                 {
12791                     tag: 'div',
12792                     html: dv
12793                 },
12794                 {
12795                     tag: 'a',
12796                     href: '#',
12797                     cls: 'select2-search-choice-close',
12798                     tabindex: '-1'
12799                 }
12800             ]
12801             
12802         }, this.searchField);
12803         
12804         var close = choice.select('a.select2-search-choice-close', true).first()
12805         
12806         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12807         
12808         this.item.push(o);
12809         
12810         this.lastData = o;
12811         
12812         this.syncValue();
12813         
12814         this.inputEl().dom.value = '';
12815         
12816         this.validate();
12817     },
12818     
12819     onRemoveItem : function(e, _self, o)
12820     {
12821         e.preventDefault();
12822         
12823         this.lastItem = Roo.apply([], this.item);
12824         
12825         var index = this.item.indexOf(o.data) * 1;
12826         
12827         if( index < 0){
12828             Roo.log('not this item?!');
12829             return;
12830         }
12831         
12832         this.item.splice(index, 1);
12833         o.item.remove();
12834         
12835         this.syncValue();
12836         
12837         this.fireEvent('remove', this, e);
12838         
12839         this.validate();
12840         
12841     },
12842     
12843     syncValue : function()
12844     {
12845         if(!this.item.length){
12846             this.clearValue();
12847             return;
12848         }
12849             
12850         var value = [];
12851         var _this = this;
12852         Roo.each(this.item, function(i){
12853             if(_this.valueField){
12854                 value.push(i[_this.valueField]);
12855                 return;
12856             }
12857
12858             value.push(i);
12859         });
12860
12861         this.value = value.join(',');
12862
12863         if(this.hiddenField){
12864             this.hiddenField.dom.value = this.value;
12865         }
12866         
12867         this.store.fireEvent("datachanged", this.store);
12868     },
12869     
12870     clearItem : function()
12871     {
12872         if(!this.multiple){
12873             return;
12874         }
12875         
12876         this.item = [];
12877         
12878         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12879            c.remove();
12880         });
12881         
12882         this.syncValue();
12883         
12884         this.validate();
12885     },
12886     
12887     inputEl: function ()
12888     {
12889         if(Roo.isTouch && this.mobileTouchView){
12890             return this.el.select('input.form-control',true).first();
12891         }
12892         
12893         if(this.tickable){
12894             return this.searchField;
12895         }
12896         
12897         return this.el.select('input.form-control',true).first();
12898     },
12899     
12900     
12901     onTickableFooterButtonClick : function(e, btn, el)
12902     {
12903         e.preventDefault();
12904         
12905         this.lastItem = Roo.apply([], this.item);
12906         
12907         if(btn && btn.name == 'cancel'){
12908             this.tickItems = Roo.apply([], this.item);
12909             this.collapse();
12910             return;
12911         }
12912         
12913         this.clearItem();
12914         
12915         var _this = this;
12916         
12917         Roo.each(this.tickItems, function(o){
12918             _this.addItem(o);
12919         });
12920         
12921         this.collapse();
12922         
12923     },
12924     
12925     validate : function()
12926     {
12927         var v = this.getRawValue();
12928         
12929         if(this.multiple){
12930             v = this.getValue();
12931         }
12932         
12933         if(this.disabled || this.allowBlank || v.length){
12934             this.markValid();
12935             return true;
12936         }
12937         
12938         this.markInvalid();
12939         return false;
12940     },
12941     
12942     tickableInputEl : function()
12943     {
12944         if(!this.tickable || !this.editable){
12945             return this.inputEl();
12946         }
12947         
12948         return this.inputEl().select('.select2-search-field-input', true).first();
12949     },
12950     
12951     
12952     getAutoCreateTouchView : function()
12953     {
12954         var id = Roo.id();
12955         
12956         var cfg = {
12957             cls: 'form-group' //input-group
12958         };
12959         
12960         var input =  {
12961             tag: 'input',
12962             id : id,
12963             type : this.inputType,
12964             cls : 'form-control x-combo-noedit',
12965             autocomplete: 'new-password',
12966             placeholder : this.placeholder || '',
12967             readonly : true
12968         };
12969         
12970         if (this.name) {
12971             input.name = this.name;
12972         }
12973         
12974         if (this.size) {
12975             input.cls += ' input-' + this.size;
12976         }
12977         
12978         if (this.disabled) {
12979             input.disabled = true;
12980         }
12981         
12982         var inputblock = {
12983             cls : '',
12984             cn : [
12985                 input
12986             ]
12987         };
12988         
12989         if(this.before){
12990             inputblock.cls += ' input-group';
12991             
12992             inputblock.cn.unshift({
12993                 tag :'span',
12994                 cls : 'input-group-addon',
12995                 html : this.before
12996             });
12997         }
12998         
12999         if(this.removable && !this.multiple){
13000             inputblock.cls += ' roo-removable';
13001             
13002             inputblock.cn.push({
13003                 tag: 'button',
13004                 html : 'x',
13005                 cls : 'roo-combo-removable-btn close'
13006             });
13007         }
13008
13009         if(this.hasFeedback && !this.allowBlank){
13010             
13011             inputblock.cls += ' has-feedback';
13012             
13013             inputblock.cn.push({
13014                 tag: 'span',
13015                 cls: 'glyphicon form-control-feedback'
13016             });
13017             
13018         }
13019         
13020         if (this.after) {
13021             
13022             inputblock.cls += (this.before) ? '' : ' input-group';
13023             
13024             inputblock.cn.push({
13025                 tag :'span',
13026                 cls : 'input-group-addon',
13027                 html : this.after
13028             });
13029         }
13030
13031         var box = {
13032             tag: 'div',
13033             cn: [
13034                 {
13035                     tag: 'input',
13036                     type : 'hidden',
13037                     cls: 'form-hidden-field'
13038                 },
13039                 inputblock
13040             ]
13041             
13042         };
13043         
13044         if(this.multiple){
13045             box = {
13046                 tag: 'div',
13047                 cn: [
13048                     {
13049                         tag: 'input',
13050                         type : 'hidden',
13051                         cls: 'form-hidden-field'
13052                     },
13053                     {
13054                         tag: 'ul',
13055                         cls: 'select2-choices',
13056                         cn:[
13057                             {
13058                                 tag: 'li',
13059                                 cls: 'select2-search-field',
13060                                 cn: [
13061
13062                                     inputblock
13063                                 ]
13064                             }
13065                         ]
13066                     }
13067                 ]
13068             }
13069         };
13070         
13071         var combobox = {
13072             cls: 'select2-container input-group',
13073             cn: [
13074                 box
13075             ]
13076         };
13077         
13078         if(this.multiple){
13079             combobox.cls += ' select2-container-multi';
13080         }
13081         
13082         var align = this.labelAlign || this.parentLabelAlign();
13083         
13084         cfg.cn = combobox;
13085         
13086         if(this.fieldLabel.length){
13087             
13088             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13089             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13090             
13091             cfg.cn = [
13092                 {
13093                     tag: 'label',
13094                     cls : 'control-label ' + lw,
13095                     html : this.fieldLabel
13096
13097                 },
13098                 {
13099                     cls : cw, 
13100                     cn: [
13101                         combobox
13102                     ]
13103                 }
13104             ];
13105         }
13106         
13107         var settings = this;
13108         
13109         ['xs','sm','md','lg'].map(function(size){
13110             if (settings[size]) {
13111                 cfg.cls += ' col-' + size + '-' + settings[size];
13112             }
13113         });
13114         
13115         return cfg;
13116     },
13117     
13118     initTouchView : function()
13119     {
13120         this.renderTouchView();
13121         
13122         this.touchViewEl.on('scroll', function(){
13123             this.el.dom.scrollTop = 0;
13124         }, this);
13125         
13126         this.inputEl().on("click", this.showTouchView, this);
13127         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13128         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13129         
13130         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13131         
13132         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13133         this.store.on('load', this.onTouchViewLoad, this);
13134         this.store.on('loadexception', this.onTouchViewLoadException, this);
13135         
13136         if(this.hiddenName){
13137             
13138             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13139             
13140             this.hiddenField.dom.value =
13141                 this.hiddenValue !== undefined ? this.hiddenValue :
13142                 this.value !== undefined ? this.value : '';
13143         
13144             this.el.dom.removeAttribute('name');
13145             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13146         }
13147         
13148         if(this.multiple){
13149             this.choices = this.el.select('ul.select2-choices', true).first();
13150             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13151         }
13152         
13153         if(this.removable && !this.multiple){
13154             var close = this.closeTriggerEl();
13155             if(close){
13156                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13157                 close.on('click', this.removeBtnClick, this, close);
13158             }
13159         }
13160         
13161         return;
13162         
13163         
13164     },
13165     
13166     renderTouchView : function()
13167     {
13168         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13169         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13170         
13171         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13172         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13173         
13174         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13175         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13176         this.touchViewBodyEl.setStyle('overflow', 'auto');
13177         
13178         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13179         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13180         
13181         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13182         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13183         
13184     },
13185     
13186     showTouchView : function()
13187     {
13188         this.touchViewHeaderEl.hide();
13189
13190         if(this.fieldLabel.length){
13191             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13192             this.touchViewHeaderEl.show();
13193         }
13194
13195         this.touchViewEl.show();
13196
13197         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13198         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13199
13200         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13201
13202         if(this.fieldLabel.length){
13203             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13204         }
13205         
13206         this.touchViewBodyEl.setHeight(bodyHeight);
13207
13208         if(this.animate){
13209             var _this = this;
13210             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13211         }else{
13212             this.touchViewEl.addClass('in');
13213         }
13214
13215         this.doTouchViewQuery();
13216         
13217     },
13218     
13219     hideTouchView : function()
13220     {
13221         this.touchViewEl.removeClass('in');
13222
13223         if(this.animate){
13224             var _this = this;
13225             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13226         }else{
13227             this.touchViewEl.setStyle('display', 'none');
13228         }
13229         
13230     },
13231     
13232     setTouchViewValue : function()
13233     {
13234         if(this.multiple){
13235             this.clearItem();
13236         
13237             var _this = this;
13238
13239             Roo.each(this.tickItems, function(o){
13240                 this.addItem(o);
13241             }, this);
13242         }
13243         
13244         this.hideTouchView();
13245     },
13246     
13247     doTouchViewQuery : function()
13248     {
13249         var qe = {
13250             query: '',
13251             forceAll: true,
13252             combo: this,
13253             cancel:false
13254         };
13255         
13256         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13257             return false;
13258         }
13259         
13260         if(!this.alwaysQuery || this.mode == 'local'){
13261             this.onTouchViewLoad();
13262             return;
13263         }
13264         
13265         this.store.load();
13266     },
13267     
13268     onTouchViewBeforeLoad : function(combo,opts)
13269     {
13270         return;
13271     },
13272
13273     // private
13274     onTouchViewLoad : function()
13275     {
13276         if(this.store.getCount() < 1){
13277             this.onTouchViewEmptyResults();
13278             return;
13279         }
13280         
13281         this.clearTouchView();
13282         
13283         var rawValue = this.getRawValue();
13284         
13285         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13286         
13287         this.tickItems = [];
13288         
13289         this.store.data.each(function(d, rowIndex){
13290             var row = this.touchViewListGroup.createChild(template);
13291             
13292             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13293                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13294             }
13295             
13296             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13297                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13298             }
13299             
13300             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13301                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13302                 this.tickItems.push(d.data);
13303             }
13304             
13305             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13306             
13307         }, this);
13308         
13309         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13310         
13311         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13312
13313         if(this.fieldLabel.length){
13314             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13315         }
13316
13317         var listHeight = this.touchViewListGroup.getHeight();
13318         
13319         if(firstChecked && listHeight > bodyHeight){
13320             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13321         }
13322         
13323     },
13324     
13325     onTouchViewLoadException : function()
13326     {
13327         this.hideTouchView();
13328     },
13329     
13330     onTouchViewEmptyResults : function()
13331     {
13332         this.clearTouchView();
13333         
13334         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13335         
13336         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13337         
13338     },
13339     
13340     clearTouchView : function()
13341     {
13342         this.touchViewListGroup.dom.innerHTML = '';
13343     },
13344     
13345     onTouchViewClick : function(e, el, o)
13346     {
13347         e.preventDefault();
13348         
13349         var row = o.row;
13350         var rowIndex = o.rowIndex;
13351         
13352         var r = this.store.getAt(rowIndex);
13353         
13354         if(!this.multiple){
13355             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13356                 c.dom.removeAttribute('checked');
13357             }, this);
13358             
13359             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13360         
13361             this.setFromData(r.data);
13362             
13363             var close = this.closeTriggerEl();
13364         
13365             if(close){
13366                 close.show();
13367             }
13368
13369             this.hideTouchView();
13370             
13371             return;
13372         }
13373         
13374         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13375             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13376             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13377             return;
13378         }
13379         
13380         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13381         this.addItem(r.data);
13382         this.tickItems.push(r.data);
13383         
13384         
13385         
13386     }
13387     
13388
13389     /** 
13390     * @cfg {Boolean} grow 
13391     * @hide 
13392     */
13393     /** 
13394     * @cfg {Number} growMin 
13395     * @hide 
13396     */
13397     /** 
13398     * @cfg {Number} growMax 
13399     * @hide 
13400     */
13401     /**
13402      * @hide
13403      * @method autoSize
13404      */
13405 });
13406
13407 Roo.apply(Roo.bootstrap.ComboBox,  {
13408     
13409     header : {
13410         tag: 'div',
13411         cls: 'modal-header',
13412         cn: [
13413             {
13414                 tag: 'h4',
13415                 cls: 'modal-title'
13416             }
13417         ]
13418     },
13419     
13420     body : {
13421         tag: 'div',
13422         cls: 'modal-body',
13423         cn: [
13424             {
13425                 tag: 'ul',
13426                 cls: 'list-group'
13427             }
13428         ]
13429     },
13430     
13431     listItemRadio : {
13432         tag: 'li',
13433         cls: 'list-group-item',
13434         cn: [
13435             {
13436                 tag: 'span',
13437                 cls: 'roo-combobox-list-group-item-value'
13438             },
13439             {
13440                 tag: 'div',
13441                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13442                 cn: [
13443                     {
13444                         tag: 'input',
13445                         type: 'radio'
13446                     },
13447                     {
13448                         tag: 'label'
13449                     }
13450                 ]
13451             }
13452         ]
13453     },
13454     
13455     listItemCheckbox : {
13456         tag: 'li',
13457         cls: 'list-group-item',
13458         cn: [
13459             {
13460                 tag: 'span',
13461                 cls: 'roo-combobox-list-group-item-value'
13462             },
13463             {
13464                 tag: 'div',
13465                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13466                 cn: [
13467                     {
13468                         tag: 'input',
13469                         type: 'checkbox'
13470                     },
13471                     {
13472                         tag: 'label'
13473                     }
13474                 ]
13475             }
13476         ]
13477     },
13478     
13479     emptyResult : {
13480         tag: 'div',
13481         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13482     },
13483     
13484     footer : {
13485         tag: 'div',
13486         cls: 'modal-footer',
13487         cn: [
13488             {
13489                 tag: 'div',
13490                 cls: 'row',
13491                 cn: [
13492                     {
13493                         tag: 'div',
13494                         cls: 'col-xs-6 text-left',
13495                         cn: {
13496                             tag: 'button',
13497                             cls: 'btn btn-danger roo-touch-view-cancel',
13498                             html: 'Cancel'
13499                         }
13500                     },
13501                     {
13502                         tag: 'div',
13503                         cls: 'col-xs-6 text-right',
13504                         cn: {
13505                             tag: 'button',
13506                             cls: 'btn btn-success roo-touch-view-ok',
13507                             html: 'OK'
13508                         }
13509                     }
13510                 ]
13511             }
13512         ]
13513         
13514     }
13515 });
13516
13517 Roo.apply(Roo.bootstrap.ComboBox,  {
13518     
13519     touchViewTemplate : {
13520         tag: 'div',
13521         cls: 'modal fade roo-combobox-touch-view',
13522         cn: [
13523             {
13524                 tag: 'div',
13525                 cls: 'modal-dialog',
13526                 cn: [
13527                     {
13528                         tag: 'div',
13529                         cls: 'modal-content',
13530                         cn: [
13531                             Roo.bootstrap.ComboBox.header,
13532                             Roo.bootstrap.ComboBox.body,
13533                             Roo.bootstrap.ComboBox.footer
13534                         ]
13535                     }
13536                 ]
13537             }
13538         ]
13539     }
13540 });/*
13541  * Based on:
13542  * Ext JS Library 1.1.1
13543  * Copyright(c) 2006-2007, Ext JS, LLC.
13544  *
13545  * Originally Released Under LGPL - original licence link has changed is not relivant.
13546  *
13547  * Fork - LGPL
13548  * <script type="text/javascript">
13549  */
13550
13551 /**
13552  * @class Roo.View
13553  * @extends Roo.util.Observable
13554  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13555  * This class also supports single and multi selection modes. <br>
13556  * Create a data model bound view:
13557  <pre><code>
13558  var store = new Roo.data.Store(...);
13559
13560  var view = new Roo.View({
13561     el : "my-element",
13562     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13563  
13564     singleSelect: true,
13565     selectedClass: "ydataview-selected",
13566     store: store
13567  });
13568
13569  // listen for node click?
13570  view.on("click", function(vw, index, node, e){
13571  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13572  });
13573
13574  // load XML data
13575  dataModel.load("foobar.xml");
13576  </code></pre>
13577  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13578  * <br><br>
13579  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13580  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13581  * 
13582  * Note: old style constructor is still suported (container, template, config)
13583  * 
13584  * @constructor
13585  * Create a new View
13586  * @param {Object} config The config object
13587  * 
13588  */
13589 Roo.View = function(config, depreciated_tpl, depreciated_config){
13590     
13591     this.parent = false;
13592     
13593     if (typeof(depreciated_tpl) == 'undefined') {
13594         // new way.. - universal constructor.
13595         Roo.apply(this, config);
13596         this.el  = Roo.get(this.el);
13597     } else {
13598         // old format..
13599         this.el  = Roo.get(config);
13600         this.tpl = depreciated_tpl;
13601         Roo.apply(this, depreciated_config);
13602     }
13603     this.wrapEl  = this.el.wrap().wrap();
13604     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13605     
13606     
13607     if(typeof(this.tpl) == "string"){
13608         this.tpl = new Roo.Template(this.tpl);
13609     } else {
13610         // support xtype ctors..
13611         this.tpl = new Roo.factory(this.tpl, Roo);
13612     }
13613     
13614     
13615     this.tpl.compile();
13616     
13617     /** @private */
13618     this.addEvents({
13619         /**
13620          * @event beforeclick
13621          * Fires before a click is processed. Returns false to cancel the default action.
13622          * @param {Roo.View} this
13623          * @param {Number} index The index of the target node
13624          * @param {HTMLElement} node The target node
13625          * @param {Roo.EventObject} e The raw event object
13626          */
13627             "beforeclick" : true,
13628         /**
13629          * @event click
13630          * Fires when a template node is clicked.
13631          * @param {Roo.View} this
13632          * @param {Number} index The index of the target node
13633          * @param {HTMLElement} node The target node
13634          * @param {Roo.EventObject} e The raw event object
13635          */
13636             "click" : true,
13637         /**
13638          * @event dblclick
13639          * Fires when a template node is double clicked.
13640          * @param {Roo.View} this
13641          * @param {Number} index The index of the target node
13642          * @param {HTMLElement} node The target node
13643          * @param {Roo.EventObject} e The raw event object
13644          */
13645             "dblclick" : true,
13646         /**
13647          * @event contextmenu
13648          * Fires when a template node is right clicked.
13649          * @param {Roo.View} this
13650          * @param {Number} index The index of the target node
13651          * @param {HTMLElement} node The target node
13652          * @param {Roo.EventObject} e The raw event object
13653          */
13654             "contextmenu" : true,
13655         /**
13656          * @event selectionchange
13657          * Fires when the selected nodes change.
13658          * @param {Roo.View} this
13659          * @param {Array} selections Array of the selected nodes
13660          */
13661             "selectionchange" : true,
13662     
13663         /**
13664          * @event beforeselect
13665          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13666          * @param {Roo.View} this
13667          * @param {HTMLElement} node The node to be selected
13668          * @param {Array} selections Array of currently selected nodes
13669          */
13670             "beforeselect" : true,
13671         /**
13672          * @event preparedata
13673          * Fires on every row to render, to allow you to change the data.
13674          * @param {Roo.View} this
13675          * @param {Object} data to be rendered (change this)
13676          */
13677           "preparedata" : true
13678           
13679           
13680         });
13681
13682
13683
13684     this.el.on({
13685         "click": this.onClick,
13686         "dblclick": this.onDblClick,
13687         "contextmenu": this.onContextMenu,
13688         scope:this
13689     });
13690
13691     this.selections = [];
13692     this.nodes = [];
13693     this.cmp = new Roo.CompositeElementLite([]);
13694     if(this.store){
13695         this.store = Roo.factory(this.store, Roo.data);
13696         this.setStore(this.store, true);
13697     }
13698     
13699     if ( this.footer && this.footer.xtype) {
13700            
13701          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13702         
13703         this.footer.dataSource = this.store
13704         this.footer.container = fctr;
13705         this.footer = Roo.factory(this.footer, Roo);
13706         fctr.insertFirst(this.el);
13707         
13708         // this is a bit insane - as the paging toolbar seems to detach the el..
13709 //        dom.parentNode.parentNode.parentNode
13710          // they get detached?
13711     }
13712     
13713     
13714     Roo.View.superclass.constructor.call(this);
13715     
13716     
13717 };
13718
13719 Roo.extend(Roo.View, Roo.util.Observable, {
13720     
13721      /**
13722      * @cfg {Roo.data.Store} store Data store to load data from.
13723      */
13724     store : false,
13725     
13726     /**
13727      * @cfg {String|Roo.Element} el The container element.
13728      */
13729     el : '',
13730     
13731     /**
13732      * @cfg {String|Roo.Template} tpl The template used by this View 
13733      */
13734     tpl : false,
13735     /**
13736      * @cfg {String} dataName the named area of the template to use as the data area
13737      *                          Works with domtemplates roo-name="name"
13738      */
13739     dataName: false,
13740     /**
13741      * @cfg {String} selectedClass The css class to add to selected nodes
13742      */
13743     selectedClass : "x-view-selected",
13744      /**
13745      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13746      */
13747     emptyText : "",
13748     
13749     /**
13750      * @cfg {String} text to display on mask (default Loading)
13751      */
13752     mask : false,
13753     /**
13754      * @cfg {Boolean} multiSelect Allow multiple selection
13755      */
13756     multiSelect : false,
13757     /**
13758      * @cfg {Boolean} singleSelect Allow single selection
13759      */
13760     singleSelect:  false,
13761     
13762     /**
13763      * @cfg {Boolean} toggleSelect - selecting 
13764      */
13765     toggleSelect : false,
13766     
13767     /**
13768      * @cfg {Boolean} tickable - selecting 
13769      */
13770     tickable : false,
13771     
13772     /**
13773      * Returns the element this view is bound to.
13774      * @return {Roo.Element}
13775      */
13776     getEl : function(){
13777         return this.wrapEl;
13778     },
13779     
13780     
13781
13782     /**
13783      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13784      */
13785     refresh : function(){
13786         //Roo.log('refresh');
13787         var t = this.tpl;
13788         
13789         // if we are using something like 'domtemplate', then
13790         // the what gets used is:
13791         // t.applySubtemplate(NAME, data, wrapping data..)
13792         // the outer template then get' applied with
13793         //     the store 'extra data'
13794         // and the body get's added to the
13795         //      roo-name="data" node?
13796         //      <span class='roo-tpl-{name}'></span> ?????
13797         
13798         
13799         
13800         this.clearSelections();
13801         this.el.update("");
13802         var html = [];
13803         var records = this.store.getRange();
13804         if(records.length < 1) {
13805             
13806             // is this valid??  = should it render a template??
13807             
13808             this.el.update(this.emptyText);
13809             return;
13810         }
13811         var el = this.el;
13812         if (this.dataName) {
13813             this.el.update(t.apply(this.store.meta)); //????
13814             el = this.el.child('.roo-tpl-' + this.dataName);
13815         }
13816         
13817         for(var i = 0, len = records.length; i < len; i++){
13818             var data = this.prepareData(records[i].data, i, records[i]);
13819             this.fireEvent("preparedata", this, data, i, records[i]);
13820             
13821             var d = Roo.apply({}, data);
13822             
13823             if(this.tickable){
13824                 Roo.apply(d, {'roo-id' : Roo.id()});
13825                 
13826                 var _this = this;
13827             
13828                 Roo.each(this.parent.item, function(item){
13829                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13830                         return;
13831                     }
13832                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13833                 });
13834             }
13835             
13836             html[html.length] = Roo.util.Format.trim(
13837                 this.dataName ?
13838                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13839                     t.apply(d)
13840             );
13841         }
13842         
13843         
13844         
13845         el.update(html.join(""));
13846         this.nodes = el.dom.childNodes;
13847         this.updateIndexes(0);
13848     },
13849     
13850
13851     /**
13852      * Function to override to reformat the data that is sent to
13853      * the template for each node.
13854      * DEPRICATED - use the preparedata event handler.
13855      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13856      * a JSON object for an UpdateManager bound view).
13857      */
13858     prepareData : function(data, index, record)
13859     {
13860         this.fireEvent("preparedata", this, data, index, record);
13861         return data;
13862     },
13863
13864     onUpdate : function(ds, record){
13865         // Roo.log('on update');   
13866         this.clearSelections();
13867         var index = this.store.indexOf(record);
13868         var n = this.nodes[index];
13869         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13870         n.parentNode.removeChild(n);
13871         this.updateIndexes(index, index);
13872     },
13873
13874     
13875     
13876 // --------- FIXME     
13877     onAdd : function(ds, records, index)
13878     {
13879         //Roo.log(['on Add', ds, records, index] );        
13880         this.clearSelections();
13881         if(this.nodes.length == 0){
13882             this.refresh();
13883             return;
13884         }
13885         var n = this.nodes[index];
13886         for(var i = 0, len = records.length; i < len; i++){
13887             var d = this.prepareData(records[i].data, i, records[i]);
13888             if(n){
13889                 this.tpl.insertBefore(n, d);
13890             }else{
13891                 
13892                 this.tpl.append(this.el, d);
13893             }
13894         }
13895         this.updateIndexes(index);
13896     },
13897
13898     onRemove : function(ds, record, index){
13899        // Roo.log('onRemove');
13900         this.clearSelections();
13901         var el = this.dataName  ?
13902             this.el.child('.roo-tpl-' + this.dataName) :
13903             this.el; 
13904         
13905         el.dom.removeChild(this.nodes[index]);
13906         this.updateIndexes(index);
13907     },
13908
13909     /**
13910      * Refresh an individual node.
13911      * @param {Number} index
13912      */
13913     refreshNode : function(index){
13914         this.onUpdate(this.store, this.store.getAt(index));
13915     },
13916
13917     updateIndexes : function(startIndex, endIndex){
13918         var ns = this.nodes;
13919         startIndex = startIndex || 0;
13920         endIndex = endIndex || ns.length - 1;
13921         for(var i = startIndex; i <= endIndex; i++){
13922             ns[i].nodeIndex = i;
13923         }
13924     },
13925
13926     /**
13927      * Changes the data store this view uses and refresh the view.
13928      * @param {Store} store
13929      */
13930     setStore : function(store, initial){
13931         if(!initial && this.store){
13932             this.store.un("datachanged", this.refresh);
13933             this.store.un("add", this.onAdd);
13934             this.store.un("remove", this.onRemove);
13935             this.store.un("update", this.onUpdate);
13936             this.store.un("clear", this.refresh);
13937             this.store.un("beforeload", this.onBeforeLoad);
13938             this.store.un("load", this.onLoad);
13939             this.store.un("loadexception", this.onLoad);
13940         }
13941         if(store){
13942           
13943             store.on("datachanged", this.refresh, this);
13944             store.on("add", this.onAdd, this);
13945             store.on("remove", this.onRemove, this);
13946             store.on("update", this.onUpdate, this);
13947             store.on("clear", this.refresh, this);
13948             store.on("beforeload", this.onBeforeLoad, this);
13949             store.on("load", this.onLoad, this);
13950             store.on("loadexception", this.onLoad, this);
13951         }
13952         
13953         if(store){
13954             this.refresh();
13955         }
13956     },
13957     /**
13958      * onbeforeLoad - masks the loading area.
13959      *
13960      */
13961     onBeforeLoad : function(store,opts)
13962     {
13963          //Roo.log('onBeforeLoad');   
13964         if (!opts.add) {
13965             this.el.update("");
13966         }
13967         this.el.mask(this.mask ? this.mask : "Loading" ); 
13968     },
13969     onLoad : function ()
13970     {
13971         this.el.unmask();
13972     },
13973     
13974
13975     /**
13976      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13977      * @param {HTMLElement} node
13978      * @return {HTMLElement} The template node
13979      */
13980     findItemFromChild : function(node){
13981         var el = this.dataName  ?
13982             this.el.child('.roo-tpl-' + this.dataName,true) :
13983             this.el.dom; 
13984         
13985         if(!node || node.parentNode == el){
13986                     return node;
13987             }
13988             var p = node.parentNode;
13989             while(p && p != el){
13990             if(p.parentNode == el){
13991                 return p;
13992             }
13993             p = p.parentNode;
13994         }
13995             return null;
13996     },
13997
13998     /** @ignore */
13999     onClick : function(e){
14000         var item = this.findItemFromChild(e.getTarget());
14001         if(item){
14002             var index = this.indexOf(item);
14003             if(this.onItemClick(item, index, e) !== false){
14004                 this.fireEvent("click", this, index, item, e);
14005             }
14006         }else{
14007             this.clearSelections();
14008         }
14009     },
14010
14011     /** @ignore */
14012     onContextMenu : function(e){
14013         var item = this.findItemFromChild(e.getTarget());
14014         if(item){
14015             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14016         }
14017     },
14018
14019     /** @ignore */
14020     onDblClick : function(e){
14021         var item = this.findItemFromChild(e.getTarget());
14022         if(item){
14023             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14024         }
14025     },
14026
14027     onItemClick : function(item, index, e)
14028     {
14029         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14030             return false;
14031         }
14032         if (this.toggleSelect) {
14033             var m = this.isSelected(item) ? 'unselect' : 'select';
14034             //Roo.log(m);
14035             var _t = this;
14036             _t[m](item, true, false);
14037             return true;
14038         }
14039         if(this.multiSelect || this.singleSelect){
14040             if(this.multiSelect && e.shiftKey && this.lastSelection){
14041                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14042             }else{
14043                 this.select(item, this.multiSelect && e.ctrlKey);
14044                 this.lastSelection = item;
14045             }
14046             
14047             if(!this.tickable){
14048                 e.preventDefault();
14049             }
14050             
14051         }
14052         return true;
14053     },
14054
14055     /**
14056      * Get the number of selected nodes.
14057      * @return {Number}
14058      */
14059     getSelectionCount : function(){
14060         return this.selections.length;
14061     },
14062
14063     /**
14064      * Get the currently selected nodes.
14065      * @return {Array} An array of HTMLElements
14066      */
14067     getSelectedNodes : function(){
14068         return this.selections;
14069     },
14070
14071     /**
14072      * Get the indexes of the selected nodes.
14073      * @return {Array}
14074      */
14075     getSelectedIndexes : function(){
14076         var indexes = [], s = this.selections;
14077         for(var i = 0, len = s.length; i < len; i++){
14078             indexes.push(s[i].nodeIndex);
14079         }
14080         return indexes;
14081     },
14082
14083     /**
14084      * Clear all selections
14085      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14086      */
14087     clearSelections : function(suppressEvent){
14088         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14089             this.cmp.elements = this.selections;
14090             this.cmp.removeClass(this.selectedClass);
14091             this.selections = [];
14092             if(!suppressEvent){
14093                 this.fireEvent("selectionchange", this, this.selections);
14094             }
14095         }
14096     },
14097
14098     /**
14099      * Returns true if the passed node is selected
14100      * @param {HTMLElement/Number} node The node or node index
14101      * @return {Boolean}
14102      */
14103     isSelected : function(node){
14104         var s = this.selections;
14105         if(s.length < 1){
14106             return false;
14107         }
14108         node = this.getNode(node);
14109         return s.indexOf(node) !== -1;
14110     },
14111
14112     /**
14113      * Selects nodes.
14114      * @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
14115      * @param {Boolean} keepExisting (optional) true to keep existing selections
14116      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14117      */
14118     select : function(nodeInfo, keepExisting, suppressEvent){
14119         if(nodeInfo instanceof Array){
14120             if(!keepExisting){
14121                 this.clearSelections(true);
14122             }
14123             for(var i = 0, len = nodeInfo.length; i < len; i++){
14124                 this.select(nodeInfo[i], true, true);
14125             }
14126             return;
14127         } 
14128         var node = this.getNode(nodeInfo);
14129         if(!node || this.isSelected(node)){
14130             return; // already selected.
14131         }
14132         if(!keepExisting){
14133             this.clearSelections(true);
14134         }
14135         
14136         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14137             Roo.fly(node).addClass(this.selectedClass);
14138             this.selections.push(node);
14139             if(!suppressEvent){
14140                 this.fireEvent("selectionchange", this, this.selections);
14141             }
14142         }
14143         
14144         
14145     },
14146       /**
14147      * Unselects nodes.
14148      * @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
14149      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14150      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14151      */
14152     unselect : function(nodeInfo, keepExisting, suppressEvent)
14153     {
14154         if(nodeInfo instanceof Array){
14155             Roo.each(this.selections, function(s) {
14156                 this.unselect(s, nodeInfo);
14157             }, this);
14158             return;
14159         }
14160         var node = this.getNode(nodeInfo);
14161         if(!node || !this.isSelected(node)){
14162             //Roo.log("not selected");
14163             return; // not selected.
14164         }
14165         // fireevent???
14166         var ns = [];
14167         Roo.each(this.selections, function(s) {
14168             if (s == node ) {
14169                 Roo.fly(node).removeClass(this.selectedClass);
14170
14171                 return;
14172             }
14173             ns.push(s);
14174         },this);
14175         
14176         this.selections= ns;
14177         this.fireEvent("selectionchange", this, this.selections);
14178     },
14179
14180     /**
14181      * Gets a template node.
14182      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14183      * @return {HTMLElement} The node or null if it wasn't found
14184      */
14185     getNode : function(nodeInfo){
14186         if(typeof nodeInfo == "string"){
14187             return document.getElementById(nodeInfo);
14188         }else if(typeof nodeInfo == "number"){
14189             return this.nodes[nodeInfo];
14190         }
14191         return nodeInfo;
14192     },
14193
14194     /**
14195      * Gets a range template nodes.
14196      * @param {Number} startIndex
14197      * @param {Number} endIndex
14198      * @return {Array} An array of nodes
14199      */
14200     getNodes : function(start, end){
14201         var ns = this.nodes;
14202         start = start || 0;
14203         end = typeof end == "undefined" ? ns.length - 1 : end;
14204         var nodes = [];
14205         if(start <= end){
14206             for(var i = start; i <= end; i++){
14207                 nodes.push(ns[i]);
14208             }
14209         } else{
14210             for(var i = start; i >= end; i--){
14211                 nodes.push(ns[i]);
14212             }
14213         }
14214         return nodes;
14215     },
14216
14217     /**
14218      * Finds the index of the passed node
14219      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14220      * @return {Number} The index of the node or -1
14221      */
14222     indexOf : function(node){
14223         node = this.getNode(node);
14224         if(typeof node.nodeIndex == "number"){
14225             return node.nodeIndex;
14226         }
14227         var ns = this.nodes;
14228         for(var i = 0, len = ns.length; i < len; i++){
14229             if(ns[i] == node){
14230                 return i;
14231             }
14232         }
14233         return -1;
14234     }
14235 });
14236 /*
14237  * - LGPL
14238  *
14239  * based on jquery fullcalendar
14240  * 
14241  */
14242
14243 Roo.bootstrap = Roo.bootstrap || {};
14244 /**
14245  * @class Roo.bootstrap.Calendar
14246  * @extends Roo.bootstrap.Component
14247  * Bootstrap Calendar class
14248  * @cfg {Boolean} loadMask (true|false) default false
14249  * @cfg {Object} header generate the user specific header of the calendar, default false
14250
14251  * @constructor
14252  * Create a new Container
14253  * @param {Object} config The config object
14254  */
14255
14256
14257
14258 Roo.bootstrap.Calendar = function(config){
14259     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14260      this.addEvents({
14261         /**
14262              * @event select
14263              * Fires when a date is selected
14264              * @param {DatePicker} this
14265              * @param {Date} date The selected date
14266              */
14267         'select': true,
14268         /**
14269              * @event monthchange
14270              * Fires when the displayed month changes 
14271              * @param {DatePicker} this
14272              * @param {Date} date The selected month
14273              */
14274         'monthchange': true,
14275         /**
14276              * @event evententer
14277              * Fires when mouse over an event
14278              * @param {Calendar} this
14279              * @param {event} Event
14280              */
14281         'evententer': true,
14282         /**
14283              * @event eventleave
14284              * Fires when the mouse leaves an
14285              * @param {Calendar} this
14286              * @param {event}
14287              */
14288         'eventleave': true,
14289         /**
14290              * @event eventclick
14291              * Fires when the mouse click an
14292              * @param {Calendar} this
14293              * @param {event}
14294              */
14295         'eventclick': true
14296         
14297     });
14298
14299 };
14300
14301 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14302     
14303      /**
14304      * @cfg {Number} startDay
14305      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14306      */
14307     startDay : 0,
14308     
14309     loadMask : false,
14310     
14311     header : false,
14312       
14313     getAutoCreate : function(){
14314         
14315         
14316         var fc_button = function(name, corner, style, content ) {
14317             return Roo.apply({},{
14318                 tag : 'span',
14319                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14320                          (corner.length ?
14321                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14322                             ''
14323                         ),
14324                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14325                 unselectable: 'on'
14326             });
14327         };
14328         
14329         var header = {};
14330         
14331         if(!this.header){
14332             header = {
14333                 tag : 'table',
14334                 cls : 'fc-header',
14335                 style : 'width:100%',
14336                 cn : [
14337                     {
14338                         tag: 'tr',
14339                         cn : [
14340                             {
14341                                 tag : 'td',
14342                                 cls : 'fc-header-left',
14343                                 cn : [
14344                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14345                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14346                                     { tag: 'span', cls: 'fc-header-space' },
14347                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14348
14349
14350                                 ]
14351                             },
14352
14353                             {
14354                                 tag : 'td',
14355                                 cls : 'fc-header-center',
14356                                 cn : [
14357                                     {
14358                                         tag: 'span',
14359                                         cls: 'fc-header-title',
14360                                         cn : {
14361                                             tag: 'H2',
14362                                             html : 'month / year'
14363                                         }
14364                                     }
14365
14366                                 ]
14367                             },
14368                             {
14369                                 tag : 'td',
14370                                 cls : 'fc-header-right',
14371                                 cn : [
14372                               /*      fc_button('month', 'left', '', 'month' ),
14373                                     fc_button('week', '', '', 'week' ),
14374                                     fc_button('day', 'right', '', 'day' )
14375                                 */    
14376
14377                                 ]
14378                             }
14379
14380                         ]
14381                     }
14382                 ]
14383             };
14384         }
14385         
14386         header = this.header;
14387         
14388        
14389         var cal_heads = function() {
14390             var ret = [];
14391             // fixme - handle this.
14392             
14393             for (var i =0; i < Date.dayNames.length; i++) {
14394                 var d = Date.dayNames[i];
14395                 ret.push({
14396                     tag: 'th',
14397                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14398                     html : d.substring(0,3)
14399                 });
14400                 
14401             }
14402             ret[0].cls += ' fc-first';
14403             ret[6].cls += ' fc-last';
14404             return ret;
14405         };
14406         var cal_cell = function(n) {
14407             return  {
14408                 tag: 'td',
14409                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14410                 cn : [
14411                     {
14412                         cn : [
14413                             {
14414                                 cls: 'fc-day-number',
14415                                 html: 'D'
14416                             },
14417                             {
14418                                 cls: 'fc-day-content',
14419                              
14420                                 cn : [
14421                                      {
14422                                         style: 'position: relative;' // height: 17px;
14423                                     }
14424                                 ]
14425                             }
14426                             
14427                             
14428                         ]
14429                     }
14430                 ]
14431                 
14432             }
14433         };
14434         var cal_rows = function() {
14435             
14436             var ret = [];
14437             for (var r = 0; r < 6; r++) {
14438                 var row= {
14439                     tag : 'tr',
14440                     cls : 'fc-week',
14441                     cn : []
14442                 };
14443                 
14444                 for (var i =0; i < Date.dayNames.length; i++) {
14445                     var d = Date.dayNames[i];
14446                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14447
14448                 }
14449                 row.cn[0].cls+=' fc-first';
14450                 row.cn[0].cn[0].style = 'min-height:90px';
14451                 row.cn[6].cls+=' fc-last';
14452                 ret.push(row);
14453                 
14454             }
14455             ret[0].cls += ' fc-first';
14456             ret[4].cls += ' fc-prev-last';
14457             ret[5].cls += ' fc-last';
14458             return ret;
14459             
14460         };
14461         
14462         var cal_table = {
14463             tag: 'table',
14464             cls: 'fc-border-separate',
14465             style : 'width:100%',
14466             cellspacing  : 0,
14467             cn : [
14468                 { 
14469                     tag: 'thead',
14470                     cn : [
14471                         { 
14472                             tag: 'tr',
14473                             cls : 'fc-first fc-last',
14474                             cn : cal_heads()
14475                         }
14476                     ]
14477                 },
14478                 { 
14479                     tag: 'tbody',
14480                     cn : cal_rows()
14481                 }
14482                   
14483             ]
14484         };
14485          
14486          var cfg = {
14487             cls : 'fc fc-ltr',
14488             cn : [
14489                 header,
14490                 {
14491                     cls : 'fc-content',
14492                     style : "position: relative;",
14493                     cn : [
14494                         {
14495                             cls : 'fc-view fc-view-month fc-grid',
14496                             style : 'position: relative',
14497                             unselectable : 'on',
14498                             cn : [
14499                                 {
14500                                     cls : 'fc-event-container',
14501                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14502                                 },
14503                                 cal_table
14504                             ]
14505                         }
14506                     ]
14507     
14508                 }
14509            ] 
14510             
14511         };
14512         
14513          
14514         
14515         return cfg;
14516     },
14517     
14518     
14519     initEvents : function()
14520     {
14521         if(!this.store){
14522             throw "can not find store for calendar";
14523         }
14524         
14525         var mark = {
14526             tag: "div",
14527             cls:"x-dlg-mask",
14528             style: "text-align:center",
14529             cn: [
14530                 {
14531                     tag: "div",
14532                     style: "background-color:white;width:50%;margin:250 auto",
14533                     cn: [
14534                         {
14535                             tag: "img",
14536                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14537                         },
14538                         {
14539                             tag: "span",
14540                             html: "Loading"
14541                         }
14542                         
14543                     ]
14544                 }
14545             ]
14546         }
14547         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14548         
14549         var size = this.el.select('.fc-content', true).first().getSize();
14550         this.maskEl.setSize(size.width, size.height);
14551         this.maskEl.enableDisplayMode("block");
14552         if(!this.loadMask){
14553             this.maskEl.hide();
14554         }
14555         
14556         this.store = Roo.factory(this.store, Roo.data);
14557         this.store.on('load', this.onLoad, this);
14558         this.store.on('beforeload', this.onBeforeLoad, this);
14559         
14560         this.resize();
14561         
14562         this.cells = this.el.select('.fc-day',true);
14563         //Roo.log(this.cells);
14564         this.textNodes = this.el.query('.fc-day-number');
14565         this.cells.addClassOnOver('fc-state-hover');
14566         
14567         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14568         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14569         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14570         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14571         
14572         this.on('monthchange', this.onMonthChange, this);
14573         
14574         this.update(new Date().clearTime());
14575     },
14576     
14577     resize : function() {
14578         var sz  = this.el.getSize();
14579         
14580         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14581         this.el.select('.fc-day-content div',true).setHeight(34);
14582     },
14583     
14584     
14585     // private
14586     showPrevMonth : function(e){
14587         this.update(this.activeDate.add("mo", -1));
14588     },
14589     showToday : function(e){
14590         this.update(new Date().clearTime());
14591     },
14592     // private
14593     showNextMonth : function(e){
14594         this.update(this.activeDate.add("mo", 1));
14595     },
14596
14597     // private
14598     showPrevYear : function(){
14599         this.update(this.activeDate.add("y", -1));
14600     },
14601
14602     // private
14603     showNextYear : function(){
14604         this.update(this.activeDate.add("y", 1));
14605     },
14606
14607     
14608    // private
14609     update : function(date)
14610     {
14611         var vd = this.activeDate;
14612         this.activeDate = date;
14613 //        if(vd && this.el){
14614 //            var t = date.getTime();
14615 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14616 //                Roo.log('using add remove');
14617 //                
14618 //                this.fireEvent('monthchange', this, date);
14619 //                
14620 //                this.cells.removeClass("fc-state-highlight");
14621 //                this.cells.each(function(c){
14622 //                   if(c.dateValue == t){
14623 //                       c.addClass("fc-state-highlight");
14624 //                       setTimeout(function(){
14625 //                            try{c.dom.firstChild.focus();}catch(e){}
14626 //                       }, 50);
14627 //                       return false;
14628 //                   }
14629 //                   return true;
14630 //                });
14631 //                return;
14632 //            }
14633 //        }
14634         
14635         var days = date.getDaysInMonth();
14636         
14637         var firstOfMonth = date.getFirstDateOfMonth();
14638         var startingPos = firstOfMonth.getDay()-this.startDay;
14639         
14640         if(startingPos < this.startDay){
14641             startingPos += 7;
14642         }
14643         
14644         var pm = date.add(Date.MONTH, -1);
14645         var prevStart = pm.getDaysInMonth()-startingPos;
14646 //        
14647         this.cells = this.el.select('.fc-day',true);
14648         this.textNodes = this.el.query('.fc-day-number');
14649         this.cells.addClassOnOver('fc-state-hover');
14650         
14651         var cells = this.cells.elements;
14652         var textEls = this.textNodes;
14653         
14654         Roo.each(cells, function(cell){
14655             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14656         });
14657         
14658         days += startingPos;
14659
14660         // convert everything to numbers so it's fast
14661         var day = 86400000;
14662         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14663         //Roo.log(d);
14664         //Roo.log(pm);
14665         //Roo.log(prevStart);
14666         
14667         var today = new Date().clearTime().getTime();
14668         var sel = date.clearTime().getTime();
14669         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14670         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14671         var ddMatch = this.disabledDatesRE;
14672         var ddText = this.disabledDatesText;
14673         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14674         var ddaysText = this.disabledDaysText;
14675         var format = this.format;
14676         
14677         var setCellClass = function(cal, cell){
14678             cell.row = 0;
14679             cell.events = [];
14680             cell.more = [];
14681             //Roo.log('set Cell Class');
14682             cell.title = "";
14683             var t = d.getTime();
14684             
14685             //Roo.log(d);
14686             
14687             cell.dateValue = t;
14688             if(t == today){
14689                 cell.className += " fc-today";
14690                 cell.className += " fc-state-highlight";
14691                 cell.title = cal.todayText;
14692             }
14693             if(t == sel){
14694                 // disable highlight in other month..
14695                 //cell.className += " fc-state-highlight";
14696                 
14697             }
14698             // disabling
14699             if(t < min) {
14700                 cell.className = " fc-state-disabled";
14701                 cell.title = cal.minText;
14702                 return;
14703             }
14704             if(t > max) {
14705                 cell.className = " fc-state-disabled";
14706                 cell.title = cal.maxText;
14707                 return;
14708             }
14709             if(ddays){
14710                 if(ddays.indexOf(d.getDay()) != -1){
14711                     cell.title = ddaysText;
14712                     cell.className = " fc-state-disabled";
14713                 }
14714             }
14715             if(ddMatch && format){
14716                 var fvalue = d.dateFormat(format);
14717                 if(ddMatch.test(fvalue)){
14718                     cell.title = ddText.replace("%0", fvalue);
14719                     cell.className = " fc-state-disabled";
14720                 }
14721             }
14722             
14723             if (!cell.initialClassName) {
14724                 cell.initialClassName = cell.dom.className;
14725             }
14726             
14727             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14728         };
14729
14730         var i = 0;
14731         
14732         for(; i < startingPos; i++) {
14733             textEls[i].innerHTML = (++prevStart);
14734             d.setDate(d.getDate()+1);
14735             
14736             cells[i].className = "fc-past fc-other-month";
14737             setCellClass(this, cells[i]);
14738         }
14739         
14740         var intDay = 0;
14741         
14742         for(; i < days; i++){
14743             intDay = i - startingPos + 1;
14744             textEls[i].innerHTML = (intDay);
14745             d.setDate(d.getDate()+1);
14746             
14747             cells[i].className = ''; // "x-date-active";
14748             setCellClass(this, cells[i]);
14749         }
14750         var extraDays = 0;
14751         
14752         for(; i < 42; i++) {
14753             textEls[i].innerHTML = (++extraDays);
14754             d.setDate(d.getDate()+1);
14755             
14756             cells[i].className = "fc-future fc-other-month";
14757             setCellClass(this, cells[i]);
14758         }
14759         
14760         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14761         
14762         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14763         
14764         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14765         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14766         
14767         if(totalRows != 6){
14768             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14769             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14770         }
14771         
14772         this.fireEvent('monthchange', this, date);
14773         
14774         
14775         /*
14776         if(!this.internalRender){
14777             var main = this.el.dom.firstChild;
14778             var w = main.offsetWidth;
14779             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14780             Roo.fly(main).setWidth(w);
14781             this.internalRender = true;
14782             // opera does not respect the auto grow header center column
14783             // then, after it gets a width opera refuses to recalculate
14784             // without a second pass
14785             if(Roo.isOpera && !this.secondPass){
14786                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14787                 this.secondPass = true;
14788                 this.update.defer(10, this, [date]);
14789             }
14790         }
14791         */
14792         
14793     },
14794     
14795     findCell : function(dt) {
14796         dt = dt.clearTime().getTime();
14797         var ret = false;
14798         this.cells.each(function(c){
14799             //Roo.log("check " +c.dateValue + '?=' + dt);
14800             if(c.dateValue == dt){
14801                 ret = c;
14802                 return false;
14803             }
14804             return true;
14805         });
14806         
14807         return ret;
14808     },
14809     
14810     findCells : function(ev) {
14811         var s = ev.start.clone().clearTime().getTime();
14812        // Roo.log(s);
14813         var e= ev.end.clone().clearTime().getTime();
14814        // Roo.log(e);
14815         var ret = [];
14816         this.cells.each(function(c){
14817              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14818             
14819             if(c.dateValue > e){
14820                 return ;
14821             }
14822             if(c.dateValue < s){
14823                 return ;
14824             }
14825             ret.push(c);
14826         });
14827         
14828         return ret;    
14829     },
14830     
14831 //    findBestRow: function(cells)
14832 //    {
14833 //        var ret = 0;
14834 //        
14835 //        for (var i =0 ; i < cells.length;i++) {
14836 //            ret  = Math.max(cells[i].rows || 0,ret);
14837 //        }
14838 //        return ret;
14839 //        
14840 //    },
14841     
14842     
14843     addItem : function(ev)
14844     {
14845         // look for vertical location slot in
14846         var cells = this.findCells(ev);
14847         
14848 //        ev.row = this.findBestRow(cells);
14849         
14850         // work out the location.
14851         
14852         var crow = false;
14853         var rows = [];
14854         for(var i =0; i < cells.length; i++) {
14855             
14856             cells[i].row = cells[0].row;
14857             
14858             if(i == 0){
14859                 cells[i].row = cells[i].row + 1;
14860             }
14861             
14862             if (!crow) {
14863                 crow = {
14864                     start : cells[i],
14865                     end :  cells[i]
14866                 };
14867                 continue;
14868             }
14869             if (crow.start.getY() == cells[i].getY()) {
14870                 // on same row.
14871                 crow.end = cells[i];
14872                 continue;
14873             }
14874             // different row.
14875             rows.push(crow);
14876             crow = {
14877                 start: cells[i],
14878                 end : cells[i]
14879             };
14880             
14881         }
14882         
14883         rows.push(crow);
14884         ev.els = [];
14885         ev.rows = rows;
14886         ev.cells = cells;
14887         
14888         cells[0].events.push(ev);
14889         
14890         this.calevents.push(ev);
14891     },
14892     
14893     clearEvents: function() {
14894         
14895         if(!this.calevents){
14896             return;
14897         }
14898         
14899         Roo.each(this.cells.elements, function(c){
14900             c.row = 0;
14901             c.events = [];
14902             c.more = [];
14903         });
14904         
14905         Roo.each(this.calevents, function(e) {
14906             Roo.each(e.els, function(el) {
14907                 el.un('mouseenter' ,this.onEventEnter, this);
14908                 el.un('mouseleave' ,this.onEventLeave, this);
14909                 el.remove();
14910             },this);
14911         },this);
14912         
14913         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14914             e.remove();
14915         });
14916         
14917     },
14918     
14919     renderEvents: function()
14920     {   
14921         var _this = this;
14922         
14923         this.cells.each(function(c) {
14924             
14925             if(c.row < 5){
14926                 return;
14927             }
14928             
14929             var ev = c.events;
14930             
14931             var r = 4;
14932             if(c.row != c.events.length){
14933                 r = 4 - (4 - (c.row - c.events.length));
14934             }
14935             
14936             c.events = ev.slice(0, r);
14937             c.more = ev.slice(r);
14938             
14939             if(c.more.length && c.more.length == 1){
14940                 c.events.push(c.more.pop());
14941             }
14942             
14943             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14944             
14945         });
14946             
14947         this.cells.each(function(c) {
14948             
14949             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14950             
14951             
14952             for (var e = 0; e < c.events.length; e++){
14953                 var ev = c.events[e];
14954                 var rows = ev.rows;
14955                 
14956                 for(var i = 0; i < rows.length; i++) {
14957                 
14958                     // how many rows should it span..
14959
14960                     var  cfg = {
14961                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14962                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14963
14964                         unselectable : "on",
14965                         cn : [
14966                             {
14967                                 cls: 'fc-event-inner',
14968                                 cn : [
14969     //                                {
14970     //                                  tag:'span',
14971     //                                  cls: 'fc-event-time',
14972     //                                  html : cells.length > 1 ? '' : ev.time
14973     //                                },
14974                                     {
14975                                       tag:'span',
14976                                       cls: 'fc-event-title',
14977                                       html : String.format('{0}', ev.title)
14978                                     }
14979
14980
14981                                 ]
14982                             },
14983                             {
14984                                 cls: 'ui-resizable-handle ui-resizable-e',
14985                                 html : '&nbsp;&nbsp;&nbsp'
14986                             }
14987
14988                         ]
14989                     };
14990
14991                     if (i == 0) {
14992                         cfg.cls += ' fc-event-start';
14993                     }
14994                     if ((i+1) == rows.length) {
14995                         cfg.cls += ' fc-event-end';
14996                     }
14997
14998                     var ctr = _this.el.select('.fc-event-container',true).first();
14999                     var cg = ctr.createChild(cfg);
15000
15001                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15002                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15003
15004                     var r = (c.more.length) ? 1 : 0;
15005                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15006                     cg.setWidth(ebox.right - sbox.x -2);
15007
15008                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15009                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15010                     cg.on('click', _this.onEventClick, _this, ev);
15011
15012                     ev.els.push(cg);
15013                     
15014                 }
15015                 
15016             }
15017             
15018             
15019             if(c.more.length){
15020                 var  cfg = {
15021                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15022                     style : 'position: absolute',
15023                     unselectable : "on",
15024                     cn : [
15025                         {
15026                             cls: 'fc-event-inner',
15027                             cn : [
15028                                 {
15029                                   tag:'span',
15030                                   cls: 'fc-event-title',
15031                                   html : 'More'
15032                                 }
15033
15034
15035                             ]
15036                         },
15037                         {
15038                             cls: 'ui-resizable-handle ui-resizable-e',
15039                             html : '&nbsp;&nbsp;&nbsp'
15040                         }
15041
15042                     ]
15043                 };
15044
15045                 var ctr = _this.el.select('.fc-event-container',true).first();
15046                 var cg = ctr.createChild(cfg);
15047
15048                 var sbox = c.select('.fc-day-content',true).first().getBox();
15049                 var ebox = c.select('.fc-day-content',true).first().getBox();
15050                 //Roo.log(cg);
15051                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15052                 cg.setWidth(ebox.right - sbox.x -2);
15053
15054                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15055                 
15056             }
15057             
15058         });
15059         
15060         
15061         
15062     },
15063     
15064     onEventEnter: function (e, el,event,d) {
15065         this.fireEvent('evententer', this, el, event);
15066     },
15067     
15068     onEventLeave: function (e, el,event,d) {
15069         this.fireEvent('eventleave', this, el, event);
15070     },
15071     
15072     onEventClick: function (e, el,event,d) {
15073         this.fireEvent('eventclick', this, el, event);
15074     },
15075     
15076     onMonthChange: function () {
15077         this.store.load();
15078     },
15079     
15080     onMoreEventClick: function(e, el, more)
15081     {
15082         var _this = this;
15083         
15084         this.calpopover.placement = 'right';
15085         this.calpopover.setTitle('More');
15086         
15087         this.calpopover.setContent('');
15088         
15089         var ctr = this.calpopover.el.select('.popover-content', true).first();
15090         
15091         Roo.each(more, function(m){
15092             var cfg = {
15093                 cls : 'fc-event-hori fc-event-draggable',
15094                 html : m.title
15095             }
15096             var cg = ctr.createChild(cfg);
15097             
15098             cg.on('click', _this.onEventClick, _this, m);
15099         });
15100         
15101         this.calpopover.show(el);
15102         
15103         
15104     },
15105     
15106     onLoad: function () 
15107     {   
15108         this.calevents = [];
15109         var cal = this;
15110         
15111         if(this.store.getCount() > 0){
15112             this.store.data.each(function(d){
15113                cal.addItem({
15114                     id : d.data.id,
15115                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15116                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15117                     time : d.data.start_time,
15118                     title : d.data.title,
15119                     description : d.data.description,
15120                     venue : d.data.venue
15121                 });
15122             });
15123         }
15124         
15125         this.renderEvents();
15126         
15127         if(this.calevents.length && this.loadMask){
15128             this.maskEl.hide();
15129         }
15130     },
15131     
15132     onBeforeLoad: function()
15133     {
15134         this.clearEvents();
15135         if(this.loadMask){
15136             this.maskEl.show();
15137         }
15138     }
15139 });
15140
15141  
15142  /*
15143  * - LGPL
15144  *
15145  * element
15146  * 
15147  */
15148
15149 /**
15150  * @class Roo.bootstrap.Popover
15151  * @extends Roo.bootstrap.Component
15152  * Bootstrap Popover class
15153  * @cfg {String} html contents of the popover   (or false to use children..)
15154  * @cfg {String} title of popover (or false to hide)
15155  * @cfg {String} placement how it is placed
15156  * @cfg {String} trigger click || hover (or false to trigger manually)
15157  * @cfg {String} over what (parent or false to trigger manually.)
15158  * @cfg {Number} delay - delay before showing
15159  
15160  * @constructor
15161  * Create a new Popover
15162  * @param {Object} config The config object
15163  */
15164
15165 Roo.bootstrap.Popover = function(config){
15166     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15167 };
15168
15169 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15170     
15171     title: 'Fill in a title',
15172     html: false,
15173     
15174     placement : 'right',
15175     trigger : 'hover', // hover
15176     
15177     delay : 0,
15178     
15179     over: 'parent',
15180     
15181     can_build_overlaid : false,
15182     
15183     getChildContainer : function()
15184     {
15185         return this.el.select('.popover-content',true).first();
15186     },
15187     
15188     getAutoCreate : function(){
15189          Roo.log('make popover?');
15190         var cfg = {
15191            cls : 'popover roo-dynamic',
15192            style: 'display:block',
15193            cn : [
15194                 {
15195                     cls : 'arrow'
15196                 },
15197                 {
15198                     cls : 'popover-inner',
15199                     cn : [
15200                         {
15201                             tag: 'h3',
15202                             cls: 'popover-title',
15203                             html : this.title
15204                         },
15205                         {
15206                             cls : 'popover-content',
15207                             html : this.html
15208                         }
15209                     ]
15210                     
15211                 }
15212            ]
15213         };
15214         
15215         return cfg;
15216     },
15217     setTitle: function(str)
15218     {
15219         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15220     },
15221     setContent: function(str)
15222     {
15223         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15224     },
15225     // as it get's added to the bottom of the page.
15226     onRender : function(ct, position)
15227     {
15228         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15229         if(!this.el){
15230             var cfg = Roo.apply({},  this.getAutoCreate());
15231             cfg.id = Roo.id();
15232             
15233             if (this.cls) {
15234                 cfg.cls += ' ' + this.cls;
15235             }
15236             if (this.style) {
15237                 cfg.style = this.style;
15238             }
15239             Roo.log("adding to ")
15240             this.el = Roo.get(document.body).createChild(cfg, position);
15241             Roo.log(this.el);
15242         }
15243         this.initEvents();
15244     },
15245     
15246     initEvents : function()
15247     {
15248         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15249         this.el.enableDisplayMode('block');
15250         this.el.hide();
15251         if (this.over === false) {
15252             return; 
15253         }
15254         if (this.triggers === false) {
15255             return;
15256         }
15257         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15258         var triggers = this.trigger ? this.trigger.split(' ') : [];
15259         Roo.each(triggers, function(trigger) {
15260         
15261             if (trigger == 'click') {
15262                 on_el.on('click', this.toggle, this);
15263             } else if (trigger != 'manual') {
15264                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15265                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15266       
15267                 on_el.on(eventIn  ,this.enter, this);
15268                 on_el.on(eventOut, this.leave, this);
15269             }
15270         }, this);
15271         
15272     },
15273     
15274     
15275     // private
15276     timeout : null,
15277     hoverState : null,
15278     
15279     toggle : function () {
15280         this.hoverState == 'in' ? this.leave() : this.enter();
15281     },
15282     
15283     enter : function () {
15284        
15285     
15286         clearTimeout(this.timeout);
15287     
15288         this.hoverState = 'in';
15289     
15290         if (!this.delay || !this.delay.show) {
15291             this.show();
15292             return;
15293         }
15294         var _t = this;
15295         this.timeout = setTimeout(function () {
15296             if (_t.hoverState == 'in') {
15297                 _t.show();
15298             }
15299         }, this.delay.show)
15300     },
15301     leave : function() {
15302         clearTimeout(this.timeout);
15303     
15304         this.hoverState = 'out';
15305     
15306         if (!this.delay || !this.delay.hide) {
15307             this.hide();
15308             return;
15309         }
15310         var _t = this;
15311         this.timeout = setTimeout(function () {
15312             if (_t.hoverState == 'out') {
15313                 _t.hide();
15314             }
15315         }, this.delay.hide)
15316     },
15317     
15318     show : function (on_el)
15319     {
15320         if (!on_el) {
15321             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15322         }
15323         // set content.
15324         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15325         if (this.html !== false) {
15326             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
15327         }
15328         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15329         if (!this.title.length) {
15330             this.el.select('.popover-title',true).hide();
15331         }
15332         
15333         var placement = typeof this.placement == 'function' ?
15334             this.placement.call(this, this.el, on_el) :
15335             this.placement;
15336             
15337         var autoToken = /\s?auto?\s?/i;
15338         var autoPlace = autoToken.test(placement);
15339         if (autoPlace) {
15340             placement = placement.replace(autoToken, '') || 'top';
15341         }
15342         
15343         //this.el.detach()
15344         //this.el.setXY([0,0]);
15345         this.el.show();
15346         this.el.dom.style.display='block';
15347         this.el.addClass(placement);
15348         
15349         //this.el.appendTo(on_el);
15350         
15351         var p = this.getPosition();
15352         var box = this.el.getBox();
15353         
15354         if (autoPlace) {
15355             // fixme..
15356         }
15357         var align = Roo.bootstrap.Popover.alignment[placement];
15358         this.el.alignTo(on_el, align[0],align[1]);
15359         //var arrow = this.el.select('.arrow',true).first();
15360         //arrow.set(align[2], 
15361         
15362         this.el.addClass('in');
15363         this.hoverState = null;
15364         
15365         if (this.el.hasClass('fade')) {
15366             // fade it?
15367         }
15368         
15369     },
15370     hide : function()
15371     {
15372         this.el.setXY([0,0]);
15373         this.el.removeClass('in');
15374         this.el.hide();
15375         
15376     }
15377     
15378 });
15379
15380 Roo.bootstrap.Popover.alignment = {
15381     'left' : ['r-l', [-10,0], 'right'],
15382     'right' : ['l-r', [10,0], 'left'],
15383     'bottom' : ['t-b', [0,10], 'top'],
15384     'top' : [ 'b-t', [0,-10], 'bottom']
15385 };
15386
15387  /*
15388  * - LGPL
15389  *
15390  * Progress
15391  * 
15392  */
15393
15394 /**
15395  * @class Roo.bootstrap.Progress
15396  * @extends Roo.bootstrap.Component
15397  * Bootstrap Progress class
15398  * @cfg {Boolean} striped striped of the progress bar
15399  * @cfg {Boolean} active animated of the progress bar
15400  * 
15401  * 
15402  * @constructor
15403  * Create a new Progress
15404  * @param {Object} config The config object
15405  */
15406
15407 Roo.bootstrap.Progress = function(config){
15408     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15409 };
15410
15411 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15412     
15413     striped : false,
15414     active: false,
15415     
15416     getAutoCreate : function(){
15417         var cfg = {
15418             tag: 'div',
15419             cls: 'progress'
15420         };
15421         
15422         
15423         if(this.striped){
15424             cfg.cls += ' progress-striped';
15425         }
15426       
15427         if(this.active){
15428             cfg.cls += ' active';
15429         }
15430         
15431         
15432         return cfg;
15433     }
15434    
15435 });
15436
15437  
15438
15439  /*
15440  * - LGPL
15441  *
15442  * ProgressBar
15443  * 
15444  */
15445
15446 /**
15447  * @class Roo.bootstrap.ProgressBar
15448  * @extends Roo.bootstrap.Component
15449  * Bootstrap ProgressBar class
15450  * @cfg {Number} aria_valuenow aria-value now
15451  * @cfg {Number} aria_valuemin aria-value min
15452  * @cfg {Number} aria_valuemax aria-value max
15453  * @cfg {String} label label for the progress bar
15454  * @cfg {String} panel (success | info | warning | danger )
15455  * @cfg {String} role role of the progress bar
15456  * @cfg {String} sr_only text
15457  * 
15458  * 
15459  * @constructor
15460  * Create a new ProgressBar
15461  * @param {Object} config The config object
15462  */
15463
15464 Roo.bootstrap.ProgressBar = function(config){
15465     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15466 };
15467
15468 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15469     
15470     aria_valuenow : 0,
15471     aria_valuemin : 0,
15472     aria_valuemax : 100,
15473     label : false,
15474     panel : false,
15475     role : false,
15476     sr_only: false,
15477     
15478     getAutoCreate : function()
15479     {
15480         
15481         var cfg = {
15482             tag: 'div',
15483             cls: 'progress-bar',
15484             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15485         };
15486         
15487         if(this.sr_only){
15488             cfg.cn = {
15489                 tag: 'span',
15490                 cls: 'sr-only',
15491                 html: this.sr_only
15492             }
15493         }
15494         
15495         if(this.role){
15496             cfg.role = this.role;
15497         }
15498         
15499         if(this.aria_valuenow){
15500             cfg['aria-valuenow'] = this.aria_valuenow;
15501         }
15502         
15503         if(this.aria_valuemin){
15504             cfg['aria-valuemin'] = this.aria_valuemin;
15505         }
15506         
15507         if(this.aria_valuemax){
15508             cfg['aria-valuemax'] = this.aria_valuemax;
15509         }
15510         
15511         if(this.label && !this.sr_only){
15512             cfg.html = this.label;
15513         }
15514         
15515         if(this.panel){
15516             cfg.cls += ' progress-bar-' + this.panel;
15517         }
15518         
15519         return cfg;
15520     },
15521     
15522     update : function(aria_valuenow)
15523     {
15524         this.aria_valuenow = aria_valuenow;
15525         
15526         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15527     }
15528    
15529 });
15530
15531  
15532
15533  /*
15534  * - LGPL
15535  *
15536  * column
15537  * 
15538  */
15539
15540 /**
15541  * @class Roo.bootstrap.TabGroup
15542  * @extends Roo.bootstrap.Column
15543  * Bootstrap Column class
15544  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15545  * @cfg {Boolean} carousel true to make the group behave like a carousel
15546  * @cfg {Number} bullets show the panel pointer.. default 0
15547  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15548  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15549  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15550  * 
15551  * @constructor
15552  * Create a new TabGroup
15553  * @param {Object} config The config object
15554  */
15555
15556 Roo.bootstrap.TabGroup = function(config){
15557     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15558     if (!this.navId) {
15559         this.navId = Roo.id();
15560     }
15561     this.tabs = [];
15562     Roo.bootstrap.TabGroup.register(this);
15563     
15564 };
15565
15566 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15567     
15568     carousel : false,
15569     transition : false,
15570     bullets : 0,
15571     timer : 0,
15572     autoslide : false,
15573     slideFn : false,
15574     slideOnTouch : false,
15575     
15576     getAutoCreate : function()
15577     {
15578         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15579         
15580         cfg.cls += ' tab-content';
15581         
15582         Roo.log('get auto create...............');
15583         
15584         if (this.carousel) {
15585             cfg.cls += ' carousel slide';
15586             
15587             cfg.cn = [{
15588                cls : 'carousel-inner'
15589             }];
15590         
15591             if(this.bullets > 0 && !Roo.isTouch){
15592                 
15593                 var bullets = {
15594                     cls : 'carousel-bullets',
15595                     cn : []
15596                 };
15597                 
15598                 if(this.bullets_cls){
15599                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15600                 }
15601                 
15602                 for (var i = 0; i < this.bullets; i++){
15603                     bullets.cn.push({
15604                         cls : 'bullet bullet-' + i
15605                     });
15606                 }
15607                 
15608                 bullets.cn.push({
15609                     cls : 'clear'
15610                 });
15611                 
15612                 cfg.cn[0].cn = bullets;
15613             }
15614         }
15615         
15616         return cfg;
15617     },
15618     
15619     initEvents:  function()
15620     {
15621         Roo.log('-------- init events on tab group ---------');
15622         
15623         if(this.bullets > 0 && !Roo.isTouch){
15624             this.initBullet();
15625         }
15626         
15627         Roo.log(this);
15628         
15629         if(Roo.isTouch && this.slideOnTouch){
15630             this.el.on("touchstart", this.onTouchStart, this);
15631         }
15632         
15633         if(this.autoslide){
15634             var _this = this;
15635             
15636             this.slideFn = window.setInterval(function() {
15637                 _this.showPanelNext();
15638             }, this.timer);
15639         }
15640         
15641     },
15642     
15643     onTouchStart : function(e, el, o)
15644     {
15645         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15646             return;
15647         }
15648         
15649         this.showPanelNext();
15650     },
15651     
15652     getChildContainer : function()
15653     {
15654         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15655     },
15656     
15657     /**
15658     * register a Navigation item
15659     * @param {Roo.bootstrap.NavItem} the navitem to add
15660     */
15661     register : function(item)
15662     {
15663         this.tabs.push( item);
15664         item.navId = this.navId; // not really needed..
15665     
15666     },
15667     
15668     getActivePanel : function()
15669     {
15670         var r = false;
15671         Roo.each(this.tabs, function(t) {
15672             if (t.active) {
15673                 r = t;
15674                 return false;
15675             }
15676             return null;
15677         });
15678         return r;
15679         
15680     },
15681     getPanelByName : function(n)
15682     {
15683         var r = false;
15684         Roo.each(this.tabs, function(t) {
15685             if (t.tabId == n) {
15686                 r = t;
15687                 return false;
15688             }
15689             return null;
15690         });
15691         return r;
15692     },
15693     indexOfPanel : function(p)
15694     {
15695         var r = false;
15696         Roo.each(this.tabs, function(t,i) {
15697             if (t.tabId == p.tabId) {
15698                 r = i;
15699                 return false;
15700             }
15701             return null;
15702         });
15703         return r;
15704     },
15705     /**
15706      * show a specific panel
15707      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15708      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15709      */
15710     showPanel : function (pan)
15711     {
15712         if(this.transition){
15713             Roo.log("waiting for the transitionend");
15714             return;
15715         }
15716         
15717         if (typeof(pan) == 'number') {
15718             pan = this.tabs[pan];
15719         }
15720         if (typeof(pan) == 'string') {
15721             pan = this.getPanelByName(pan);
15722         }
15723         if (pan.tabId == this.getActivePanel().tabId) {
15724             return true;
15725         }
15726         var cur = this.getActivePanel();
15727         
15728         if (false === cur.fireEvent('beforedeactivate')) {
15729             return false;
15730         }
15731         
15732         if(this.bullets > 0 && !Roo.isTouch){
15733             this.setActiveBullet(this.indexOfPanel(pan));
15734         }
15735         
15736         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15737             
15738             this.transition = true;
15739             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15740             var lr = dir == 'next' ? 'left' : 'right';
15741             pan.el.addClass(dir); // or prev
15742             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15743             cur.el.addClass(lr); // or right
15744             pan.el.addClass(lr);
15745             
15746             var _this = this;
15747             cur.el.on('transitionend', function() {
15748                 Roo.log("trans end?");
15749                 
15750                 pan.el.removeClass([lr,dir]);
15751                 pan.setActive(true);
15752                 
15753                 cur.el.removeClass([lr]);
15754                 cur.setActive(false);
15755                 
15756                 _this.transition = false;
15757                 
15758             }, this, { single:  true } );
15759             
15760             return true;
15761         }
15762         
15763         cur.setActive(false);
15764         pan.setActive(true);
15765         
15766         return true;
15767         
15768     },
15769     showPanelNext : function()
15770     {
15771         var i = this.indexOfPanel(this.getActivePanel());
15772         
15773         if (i >= this.tabs.length - 1 && !this.autoslide) {
15774             return;
15775         }
15776         
15777         if (i >= this.tabs.length - 1 && this.autoslide) {
15778             i = -1;
15779         }
15780         
15781         this.showPanel(this.tabs[i+1]);
15782     },
15783     
15784     showPanelPrev : function()
15785     {
15786         var i = this.indexOfPanel(this.getActivePanel());
15787         
15788         if (i  < 1 && !this.autoslide) {
15789             return;
15790         }
15791         
15792         if (i < 1 && this.autoslide) {
15793             i = this.tabs.length;
15794         }
15795         
15796         this.showPanel(this.tabs[i-1]);
15797     },
15798     
15799     initBullet : function()
15800     {
15801         if(Roo.isTouch){
15802             return;
15803         }
15804         
15805         var _this = this;
15806         
15807         for (var i = 0; i < this.bullets; i++){
15808             var bullet = this.el.select('.bullet-' + i, true).first();
15809
15810             if(!bullet){
15811                 continue;
15812             }
15813
15814             bullet.on('click', (function(e, el, o, ii, t){
15815
15816                 e.preventDefault();
15817
15818                 _this.showPanel(ii);
15819
15820                 if(_this.autoslide && _this.slideFn){
15821                     clearInterval(_this.slideFn);
15822                     _this.slideFn = window.setInterval(function() {
15823                         _this.showPanelNext();
15824                     }, _this.timer);
15825                 }
15826
15827             }).createDelegate(this, [i, bullet], true));
15828         }
15829     },
15830     
15831     setActiveBullet : function(i)
15832     {
15833         if(Roo.isTouch){
15834             return;
15835         }
15836         
15837         Roo.each(this.el.select('.bullet', true).elements, function(el){
15838             el.removeClass('selected');
15839         });
15840
15841         var bullet = this.el.select('.bullet-' + i, true).first();
15842         
15843         if(!bullet){
15844             return;
15845         }
15846         
15847         bullet.addClass('selected');
15848     }
15849     
15850     
15851   
15852 });
15853
15854  
15855
15856  
15857  
15858 Roo.apply(Roo.bootstrap.TabGroup, {
15859     
15860     groups: {},
15861      /**
15862     * register a Navigation Group
15863     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15864     */
15865     register : function(navgrp)
15866     {
15867         this.groups[navgrp.navId] = navgrp;
15868         
15869     },
15870     /**
15871     * fetch a Navigation Group based on the navigation ID
15872     * if one does not exist , it will get created.
15873     * @param {string} the navgroup to add
15874     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15875     */
15876     get: function(navId) {
15877         if (typeof(this.groups[navId]) == 'undefined') {
15878             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15879         }
15880         return this.groups[navId] ;
15881     }
15882     
15883     
15884     
15885 });
15886
15887  /*
15888  * - LGPL
15889  *
15890  * TabPanel
15891  * 
15892  */
15893
15894 /**
15895  * @class Roo.bootstrap.TabPanel
15896  * @extends Roo.bootstrap.Component
15897  * Bootstrap TabPanel class
15898  * @cfg {Boolean} active panel active
15899  * @cfg {String} html panel content
15900  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15901  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15902  * 
15903  * 
15904  * @constructor
15905  * Create a new TabPanel
15906  * @param {Object} config The config object
15907  */
15908
15909 Roo.bootstrap.TabPanel = function(config){
15910     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15911     this.addEvents({
15912         /**
15913              * @event changed
15914              * Fires when the active status changes
15915              * @param {Roo.bootstrap.TabPanel} this
15916              * @param {Boolean} state the new state
15917             
15918          */
15919         'changed': true,
15920         /**
15921              * @event beforedeactivate
15922              * Fires before a tab is de-activated - can be used to do validation on a form.
15923              * @param {Roo.bootstrap.TabPanel} this
15924              * @return {Boolean} false if there is an error
15925             
15926          */
15927         'beforedeactivate': true
15928      });
15929     
15930     this.tabId = this.tabId || Roo.id();
15931   
15932 };
15933
15934 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15935     
15936     active: false,
15937     html: false,
15938     tabId: false,
15939     navId : false,
15940     
15941     getAutoCreate : function(){
15942         var cfg = {
15943             tag: 'div',
15944             // item is needed for carousel - not sure if it has any effect otherwise
15945             cls: 'tab-pane item',
15946             html: this.html || ''
15947         };
15948         
15949         if(this.active){
15950             cfg.cls += ' active';
15951         }
15952         
15953         if(this.tabId){
15954             cfg.tabId = this.tabId;
15955         }
15956         
15957         
15958         return cfg;
15959     },
15960     
15961     initEvents:  function()
15962     {
15963         Roo.log('-------- init events on tab panel ---------');
15964         
15965         var p = this.parent();
15966         this.navId = this.navId || p.navId;
15967         
15968         if (typeof(this.navId) != 'undefined') {
15969             // not really needed.. but just in case.. parent should be a NavGroup.
15970             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15971             Roo.log(['register', tg, this]);
15972             tg.register(this);
15973             
15974             var i = tg.tabs.length - 1;
15975             
15976             if(this.active && tg.bullets > 0 && i < tg.bullets){
15977                 tg.setActiveBullet(i);
15978             }
15979         }
15980         
15981     },
15982     
15983     
15984     onRender : function(ct, position)
15985     {
15986        // Roo.log("Call onRender: " + this.xtype);
15987         
15988         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15989         
15990         
15991         
15992         
15993         
15994     },
15995     
15996     setActive: function(state)
15997     {
15998         Roo.log("panel - set active " + this.tabId + "=" + state);
15999         
16000         this.active = state;
16001         if (!state) {
16002             this.el.removeClass('active');
16003             
16004         } else  if (!this.el.hasClass('active')) {
16005             this.el.addClass('active');
16006         }
16007         
16008         this.fireEvent('changed', this, state);
16009     }
16010     
16011     
16012 });
16013  
16014
16015  
16016
16017  /*
16018  * - LGPL
16019  *
16020  * DateField
16021  * 
16022  */
16023
16024 /**
16025  * @class Roo.bootstrap.DateField
16026  * @extends Roo.bootstrap.Input
16027  * Bootstrap DateField class
16028  * @cfg {Number} weekStart default 0
16029  * @cfg {String} viewMode default empty, (months|years)
16030  * @cfg {String} minViewMode default empty, (months|years)
16031  * @cfg {Number} startDate default -Infinity
16032  * @cfg {Number} endDate default Infinity
16033  * @cfg {Boolean} todayHighlight default false
16034  * @cfg {Boolean} todayBtn default false
16035  * @cfg {Boolean} calendarWeeks default false
16036  * @cfg {Object} daysOfWeekDisabled default empty
16037  * @cfg {Boolean} singleMode default false (true | false)
16038  * 
16039  * @cfg {Boolean} keyboardNavigation default true
16040  * @cfg {String} language default en
16041  * 
16042  * @constructor
16043  * Create a new DateField
16044  * @param {Object} config The config object
16045  */
16046
16047 Roo.bootstrap.DateField = function(config){
16048     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16049      this.addEvents({
16050             /**
16051              * @event show
16052              * Fires when this field show.
16053              * @param {Roo.bootstrap.DateField} this
16054              * @param {Mixed} date The date value
16055              */
16056             show : true,
16057             /**
16058              * @event show
16059              * Fires when this field hide.
16060              * @param {Roo.bootstrap.DateField} this
16061              * @param {Mixed} date The date value
16062              */
16063             hide : true,
16064             /**
16065              * @event select
16066              * Fires when select a date.
16067              * @param {Roo.bootstrap.DateField} this
16068              * @param {Mixed} date The date value
16069              */
16070             select : true
16071         });
16072 };
16073
16074 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16075     
16076     /**
16077      * @cfg {String} format
16078      * The default date format string which can be overriden for localization support.  The format must be
16079      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16080      */
16081     format : "m/d/y",
16082     /**
16083      * @cfg {String} altFormats
16084      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16085      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16086      */
16087     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16088     
16089     weekStart : 0,
16090     
16091     viewMode : '',
16092     
16093     minViewMode : '',
16094     
16095     todayHighlight : false,
16096     
16097     todayBtn: false,
16098     
16099     language: 'en',
16100     
16101     keyboardNavigation: true,
16102     
16103     calendarWeeks: false,
16104     
16105     startDate: -Infinity,
16106     
16107     endDate: Infinity,
16108     
16109     daysOfWeekDisabled: [],
16110     
16111     _events: [],
16112     
16113     singleMode : false,
16114     
16115     UTCDate: function()
16116     {
16117         return new Date(Date.UTC.apply(Date, arguments));
16118     },
16119     
16120     UTCToday: function()
16121     {
16122         var today = new Date();
16123         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16124     },
16125     
16126     getDate: function() {
16127             var d = this.getUTCDate();
16128             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16129     },
16130     
16131     getUTCDate: function() {
16132             return this.date;
16133     },
16134     
16135     setDate: function(d) {
16136             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16137     },
16138     
16139     setUTCDate: function(d) {
16140             this.date = d;
16141             this.setValue(this.formatDate(this.date));
16142     },
16143         
16144     onRender: function(ct, position)
16145     {
16146         
16147         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16148         
16149         this.language = this.language || 'en';
16150         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16151         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16152         
16153         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16154         this.format = this.format || 'm/d/y';
16155         this.isInline = false;
16156         this.isInput = true;
16157         this.component = this.el.select('.add-on', true).first() || false;
16158         this.component = (this.component && this.component.length === 0) ? false : this.component;
16159         this.hasInput = this.component && this.inputEL().length;
16160         
16161         if (typeof(this.minViewMode === 'string')) {
16162             switch (this.minViewMode) {
16163                 case 'months':
16164                     this.minViewMode = 1;
16165                     break;
16166                 case 'years':
16167                     this.minViewMode = 2;
16168                     break;
16169                 default:
16170                     this.minViewMode = 0;
16171                     break;
16172             }
16173         }
16174         
16175         if (typeof(this.viewMode === 'string')) {
16176             switch (this.viewMode) {
16177                 case 'months':
16178                     this.viewMode = 1;
16179                     break;
16180                 case 'years':
16181                     this.viewMode = 2;
16182                     break;
16183                 default:
16184                     this.viewMode = 0;
16185                     break;
16186             }
16187         }
16188                 
16189         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16190         
16191 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16192         
16193         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16194         
16195         this.picker().on('mousedown', this.onMousedown, this);
16196         this.picker().on('click', this.onClick, this);
16197         
16198         this.picker().addClass('datepicker-dropdown');
16199         
16200         this.startViewMode = this.viewMode;
16201         
16202         if(this.singleMode){
16203             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16204                 v.setVisibilityMode(Roo.Element.DISPLAY)
16205                 v.hide();
16206             });
16207             
16208             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16209                 v.setStyle('width', '189px');
16210             });
16211         }
16212         
16213         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16214             if(!this.calendarWeeks){
16215                 v.remove();
16216                 return;
16217             }
16218             
16219             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16220             v.attr('colspan', function(i, val){
16221                 return parseInt(val) + 1;
16222             });
16223         })
16224                         
16225         
16226         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16227         
16228         this.setStartDate(this.startDate);
16229         this.setEndDate(this.endDate);
16230         
16231         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16232         
16233         this.fillDow();
16234         this.fillMonths();
16235         this.update();
16236         this.showMode();
16237         
16238         if(this.isInline) {
16239             this.show();
16240         }
16241     },
16242     
16243     picker : function()
16244     {
16245         return this.pickerEl;
16246 //        return this.el.select('.datepicker', true).first();
16247     },
16248     
16249     fillDow: function()
16250     {
16251         var dowCnt = this.weekStart;
16252         
16253         var dow = {
16254             tag: 'tr',
16255             cn: [
16256                 
16257             ]
16258         };
16259         
16260         if(this.calendarWeeks){
16261             dow.cn.push({
16262                 tag: 'th',
16263                 cls: 'cw',
16264                 html: '&nbsp;'
16265             })
16266         }
16267         
16268         while (dowCnt < this.weekStart + 7) {
16269             dow.cn.push({
16270                 tag: 'th',
16271                 cls: 'dow',
16272                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16273             });
16274         }
16275         
16276         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16277     },
16278     
16279     fillMonths: function()
16280     {    
16281         var i = 0;
16282         var months = this.picker().select('>.datepicker-months td', true).first();
16283         
16284         months.dom.innerHTML = '';
16285         
16286         while (i < 12) {
16287             var month = {
16288                 tag: 'span',
16289                 cls: 'month',
16290                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16291             }
16292             
16293             months.createChild(month);
16294         }
16295         
16296     },
16297     
16298     update: function()
16299     {
16300         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;
16301         
16302         if (this.date < this.startDate) {
16303             this.viewDate = new Date(this.startDate);
16304         } else if (this.date > this.endDate) {
16305             this.viewDate = new Date(this.endDate);
16306         } else {
16307             this.viewDate = new Date(this.date);
16308         }
16309         
16310         this.fill();
16311     },
16312     
16313     fill: function() 
16314     {
16315         var d = new Date(this.viewDate),
16316                 year = d.getUTCFullYear(),
16317                 month = d.getUTCMonth(),
16318                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16319                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16320                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16321                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16322                 currentDate = this.date && this.date.valueOf(),
16323                 today = this.UTCToday();
16324         
16325         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16326         
16327 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16328         
16329 //        this.picker.select('>tfoot th.today').
16330 //                                              .text(dates[this.language].today)
16331 //                                              .toggle(this.todayBtn !== false);
16332     
16333         this.updateNavArrows();
16334         this.fillMonths();
16335                                                 
16336         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16337         
16338         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16339          
16340         prevMonth.setUTCDate(day);
16341         
16342         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16343         
16344         var nextMonth = new Date(prevMonth);
16345         
16346         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16347         
16348         nextMonth = nextMonth.valueOf();
16349         
16350         var fillMonths = false;
16351         
16352         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16353         
16354         while(prevMonth.valueOf() < nextMonth) {
16355             var clsName = '';
16356             
16357             if (prevMonth.getUTCDay() === this.weekStart) {
16358                 if(fillMonths){
16359                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16360                 }
16361                     
16362                 fillMonths = {
16363                     tag: 'tr',
16364                     cn: []
16365                 };
16366                 
16367                 if(this.calendarWeeks){
16368                     // ISO 8601: First week contains first thursday.
16369                     // ISO also states week starts on Monday, but we can be more abstract here.
16370                     var
16371                     // Start of current week: based on weekstart/current date
16372                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16373                     // Thursday of this week
16374                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16375                     // First Thursday of year, year from thursday
16376                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16377                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16378                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16379                     
16380                     fillMonths.cn.push({
16381                         tag: 'td',
16382                         cls: 'cw',
16383                         html: calWeek
16384                     });
16385                 }
16386             }
16387             
16388             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16389                 clsName += ' old';
16390             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16391                 clsName += ' new';
16392             }
16393             if (this.todayHighlight &&
16394                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16395                 prevMonth.getUTCMonth() == today.getMonth() &&
16396                 prevMonth.getUTCDate() == today.getDate()) {
16397                 clsName += ' today';
16398             }
16399             
16400             if (currentDate && prevMonth.valueOf() === currentDate) {
16401                 clsName += ' active';
16402             }
16403             
16404             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16405                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16406                     clsName += ' disabled';
16407             }
16408             
16409             fillMonths.cn.push({
16410                 tag: 'td',
16411                 cls: 'day ' + clsName,
16412                 html: prevMonth.getDate()
16413             })
16414             
16415             prevMonth.setDate(prevMonth.getDate()+1);
16416         }
16417           
16418         var currentYear = this.date && this.date.getUTCFullYear();
16419         var currentMonth = this.date && this.date.getUTCMonth();
16420         
16421         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16422         
16423         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16424             v.removeClass('active');
16425             
16426             if(currentYear === year && k === currentMonth){
16427                 v.addClass('active');
16428             }
16429             
16430             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16431                 v.addClass('disabled');
16432             }
16433             
16434         });
16435         
16436         
16437         year = parseInt(year/10, 10) * 10;
16438         
16439         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16440         
16441         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16442         
16443         year -= 1;
16444         for (var i = -1; i < 11; i++) {
16445             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16446                 tag: 'span',
16447                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16448                 html: year
16449             })
16450             
16451             year += 1;
16452         }
16453     },
16454     
16455     showMode: function(dir) 
16456     {
16457         if (dir) {
16458             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16459         }
16460         
16461         Roo.each(this.picker().select('>div',true).elements, function(v){
16462             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16463             v.hide();
16464         });
16465         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16466     },
16467     
16468     place: function()
16469     {
16470         if(this.isInline) return;
16471         
16472         this.picker().removeClass(['bottom', 'top']);
16473         
16474         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16475             /*
16476              * place to the top of element!
16477              *
16478              */
16479             
16480             this.picker().addClass('top');
16481             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16482             
16483             return;
16484         }
16485         
16486         this.picker().addClass('bottom');
16487         
16488         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16489     },
16490     
16491     parseDate : function(value)
16492     {
16493         if(!value || value instanceof Date){
16494             return value;
16495         }
16496         var v = Date.parseDate(value, this.format);
16497         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16498             v = Date.parseDate(value, 'Y-m-d');
16499         }
16500         if(!v && this.altFormats){
16501             if(!this.altFormatsArray){
16502                 this.altFormatsArray = this.altFormats.split("|");
16503             }
16504             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16505                 v = Date.parseDate(value, this.altFormatsArray[i]);
16506             }
16507         }
16508         return v;
16509     },
16510     
16511     formatDate : function(date, fmt)
16512     {   
16513         return (!date || !(date instanceof Date)) ?
16514         date : date.dateFormat(fmt || this.format);
16515     },
16516     
16517     onFocus : function()
16518     {
16519         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16520         this.show();
16521     },
16522     
16523     onBlur : function()
16524     {
16525         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16526         
16527         var d = this.inputEl().getValue();
16528         
16529         this.setValue(d);
16530                 
16531         this.hide();
16532     },
16533     
16534     show : function()
16535     {
16536         this.picker().show();
16537         this.update();
16538         this.place();
16539         
16540         this.fireEvent('show', this, this.date);
16541     },
16542     
16543     hide : function()
16544     {
16545         if(this.isInline) return;
16546         this.picker().hide();
16547         this.viewMode = this.startViewMode;
16548         this.showMode();
16549         
16550         this.fireEvent('hide', this, this.date);
16551         
16552     },
16553     
16554     onMousedown: function(e)
16555     {
16556         e.stopPropagation();
16557         e.preventDefault();
16558     },
16559     
16560     keyup: function(e)
16561     {
16562         Roo.bootstrap.DateField.superclass.keyup.call(this);
16563         this.update();
16564     },
16565
16566     setValue: function(v)
16567     {
16568         
16569         // v can be a string or a date..
16570         
16571         
16572         var d = new Date(this.parseDate(v) ).clearTime();
16573         
16574         if(isNaN(d.getTime())){
16575             this.date = this.viewDate = '';
16576             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16577             return;
16578         }
16579         
16580         v = this.formatDate(d);
16581         
16582         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16583         
16584         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16585      
16586         this.update();
16587
16588         this.fireEvent('select', this, this.date);
16589         
16590     },
16591     
16592     getValue: function()
16593     {
16594         return this.formatDate(this.date);
16595     },
16596     
16597     fireKey: function(e)
16598     {
16599         if (!this.picker().isVisible()){
16600             if (e.keyCode == 27) // allow escape to hide and re-show picker
16601                 this.show();
16602             return;
16603         }
16604         
16605         var dateChanged = false,
16606         dir, day, month,
16607         newDate, newViewDate;
16608         
16609         switch(e.keyCode){
16610             case 27: // escape
16611                 this.hide();
16612                 e.preventDefault();
16613                 break;
16614             case 37: // left
16615             case 39: // right
16616                 if (!this.keyboardNavigation) break;
16617                 dir = e.keyCode == 37 ? -1 : 1;
16618                 
16619                 if (e.ctrlKey){
16620                     newDate = this.moveYear(this.date, dir);
16621                     newViewDate = this.moveYear(this.viewDate, dir);
16622                 } else if (e.shiftKey){
16623                     newDate = this.moveMonth(this.date, dir);
16624                     newViewDate = this.moveMonth(this.viewDate, dir);
16625                 } else {
16626                     newDate = new Date(this.date);
16627                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16628                     newViewDate = new Date(this.viewDate);
16629                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16630                 }
16631                 if (this.dateWithinRange(newDate)){
16632                     this.date = newDate;
16633                     this.viewDate = newViewDate;
16634                     this.setValue(this.formatDate(this.date));
16635 //                    this.update();
16636                     e.preventDefault();
16637                     dateChanged = true;
16638                 }
16639                 break;
16640             case 38: // up
16641             case 40: // down
16642                 if (!this.keyboardNavigation) break;
16643                 dir = e.keyCode == 38 ? -1 : 1;
16644                 if (e.ctrlKey){
16645                     newDate = this.moveYear(this.date, dir);
16646                     newViewDate = this.moveYear(this.viewDate, dir);
16647                 } else if (e.shiftKey){
16648                     newDate = this.moveMonth(this.date, dir);
16649                     newViewDate = this.moveMonth(this.viewDate, dir);
16650                 } else {
16651                     newDate = new Date(this.date);
16652                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16653                     newViewDate = new Date(this.viewDate);
16654                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16655                 }
16656                 if (this.dateWithinRange(newDate)){
16657                     this.date = newDate;
16658                     this.viewDate = newViewDate;
16659                     this.setValue(this.formatDate(this.date));
16660 //                    this.update();
16661                     e.preventDefault();
16662                     dateChanged = true;
16663                 }
16664                 break;
16665             case 13: // enter
16666                 this.setValue(this.formatDate(this.date));
16667                 this.hide();
16668                 e.preventDefault();
16669                 break;
16670             case 9: // tab
16671                 this.setValue(this.formatDate(this.date));
16672                 this.hide();
16673                 break;
16674             case 16: // shift
16675             case 17: // ctrl
16676             case 18: // alt
16677                 break;
16678             default :
16679                 this.hide();
16680                 
16681         }
16682     },
16683     
16684     
16685     onClick: function(e) 
16686     {
16687         e.stopPropagation();
16688         e.preventDefault();
16689         
16690         var target = e.getTarget();
16691         
16692         if(target.nodeName.toLowerCase() === 'i'){
16693             target = Roo.get(target).dom.parentNode;
16694         }
16695         
16696         var nodeName = target.nodeName;
16697         var className = target.className;
16698         var html = target.innerHTML;
16699         //Roo.log(nodeName);
16700         
16701         switch(nodeName.toLowerCase()) {
16702             case 'th':
16703                 switch(className) {
16704                     case 'switch':
16705                         this.showMode(1);
16706                         break;
16707                     case 'prev':
16708                     case 'next':
16709                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16710                         switch(this.viewMode){
16711                                 case 0:
16712                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16713                                         break;
16714                                 case 1:
16715                                 case 2:
16716                                         this.viewDate = this.moveYear(this.viewDate, dir);
16717                                         break;
16718                         }
16719                         this.fill();
16720                         break;
16721                     case 'today':
16722                         var date = new Date();
16723                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16724 //                        this.fill()
16725                         this.setValue(this.formatDate(this.date));
16726                         
16727                         this.hide();
16728                         break;
16729                 }
16730                 break;
16731             case 'span':
16732                 if (className.indexOf('disabled') < 0) {
16733                     this.viewDate.setUTCDate(1);
16734                     if (className.indexOf('month') > -1) {
16735                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16736                     } else {
16737                         var year = parseInt(html, 10) || 0;
16738                         this.viewDate.setUTCFullYear(year);
16739                         
16740                     }
16741                     
16742                     if(this.singleMode){
16743                         this.setValue(this.formatDate(this.viewDate));
16744                         this.hide();
16745                         return;
16746                     }
16747                     
16748                     this.showMode(-1);
16749                     this.fill();
16750                 }
16751                 break;
16752                 
16753             case 'td':
16754                 //Roo.log(className);
16755                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16756                     var day = parseInt(html, 10) || 1;
16757                     var year = this.viewDate.getUTCFullYear(),
16758                         month = this.viewDate.getUTCMonth();
16759
16760                     if (className.indexOf('old') > -1) {
16761                         if(month === 0 ){
16762                             month = 11;
16763                             year -= 1;
16764                         }else{
16765                             month -= 1;
16766                         }
16767                     } else if (className.indexOf('new') > -1) {
16768                         if (month == 11) {
16769                             month = 0;
16770                             year += 1;
16771                         } else {
16772                             month += 1;
16773                         }
16774                     }
16775                     //Roo.log([year,month,day]);
16776                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16777                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16778 //                    this.fill();
16779                     //Roo.log(this.formatDate(this.date));
16780                     this.setValue(this.formatDate(this.date));
16781                     this.hide();
16782                 }
16783                 break;
16784         }
16785     },
16786     
16787     setStartDate: function(startDate)
16788     {
16789         this.startDate = startDate || -Infinity;
16790         if (this.startDate !== -Infinity) {
16791             this.startDate = this.parseDate(this.startDate);
16792         }
16793         this.update();
16794         this.updateNavArrows();
16795     },
16796
16797     setEndDate: function(endDate)
16798     {
16799         this.endDate = endDate || Infinity;
16800         if (this.endDate !== Infinity) {
16801             this.endDate = this.parseDate(this.endDate);
16802         }
16803         this.update();
16804         this.updateNavArrows();
16805     },
16806     
16807     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16808     {
16809         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16810         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16811             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16812         }
16813         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16814             return parseInt(d, 10);
16815         });
16816         this.update();
16817         this.updateNavArrows();
16818     },
16819     
16820     updateNavArrows: function() 
16821     {
16822         if(this.singleMode){
16823             return;
16824         }
16825         
16826         var d = new Date(this.viewDate),
16827         year = d.getUTCFullYear(),
16828         month = d.getUTCMonth();
16829         
16830         Roo.each(this.picker().select('.prev', true).elements, function(v){
16831             v.show();
16832             switch (this.viewMode) {
16833                 case 0:
16834
16835                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16836                         v.hide();
16837                     }
16838                     break;
16839                 case 1:
16840                 case 2:
16841                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16842                         v.hide();
16843                     }
16844                     break;
16845             }
16846         });
16847         
16848         Roo.each(this.picker().select('.next', true).elements, function(v){
16849             v.show();
16850             switch (this.viewMode) {
16851                 case 0:
16852
16853                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16854                         v.hide();
16855                     }
16856                     break;
16857                 case 1:
16858                 case 2:
16859                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16860                         v.hide();
16861                     }
16862                     break;
16863             }
16864         })
16865     },
16866     
16867     moveMonth: function(date, dir)
16868     {
16869         if (!dir) return date;
16870         var new_date = new Date(date.valueOf()),
16871         day = new_date.getUTCDate(),
16872         month = new_date.getUTCMonth(),
16873         mag = Math.abs(dir),
16874         new_month, test;
16875         dir = dir > 0 ? 1 : -1;
16876         if (mag == 1){
16877             test = dir == -1
16878             // If going back one month, make sure month is not current month
16879             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16880             ? function(){
16881                 return new_date.getUTCMonth() == month;
16882             }
16883             // If going forward one month, make sure month is as expected
16884             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16885             : function(){
16886                 return new_date.getUTCMonth() != new_month;
16887             };
16888             new_month = month + dir;
16889             new_date.setUTCMonth(new_month);
16890             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16891             if (new_month < 0 || new_month > 11)
16892                 new_month = (new_month + 12) % 12;
16893         } else {
16894             // For magnitudes >1, move one month at a time...
16895             for (var i=0; i<mag; i++)
16896                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16897                 new_date = this.moveMonth(new_date, dir);
16898             // ...then reset the day, keeping it in the new month
16899             new_month = new_date.getUTCMonth();
16900             new_date.setUTCDate(day);
16901             test = function(){
16902                 return new_month != new_date.getUTCMonth();
16903             };
16904         }
16905         // Common date-resetting loop -- if date is beyond end of month, make it
16906         // end of month
16907         while (test()){
16908             new_date.setUTCDate(--day);
16909             new_date.setUTCMonth(new_month);
16910         }
16911         return new_date;
16912     },
16913
16914     moveYear: function(date, dir)
16915     {
16916         return this.moveMonth(date, dir*12);
16917     },
16918
16919     dateWithinRange: function(date)
16920     {
16921         return date >= this.startDate && date <= this.endDate;
16922     },
16923
16924     
16925     remove: function() 
16926     {
16927         this.picker().remove();
16928     }
16929    
16930 });
16931
16932 Roo.apply(Roo.bootstrap.DateField,  {
16933     
16934     head : {
16935         tag: 'thead',
16936         cn: [
16937         {
16938             tag: 'tr',
16939             cn: [
16940             {
16941                 tag: 'th',
16942                 cls: 'prev',
16943                 html: '<i class="fa fa-arrow-left"/>'
16944             },
16945             {
16946                 tag: 'th',
16947                 cls: 'switch',
16948                 colspan: '5'
16949             },
16950             {
16951                 tag: 'th',
16952                 cls: 'next',
16953                 html: '<i class="fa fa-arrow-right"/>'
16954             }
16955
16956             ]
16957         }
16958         ]
16959     },
16960     
16961     content : {
16962         tag: 'tbody',
16963         cn: [
16964         {
16965             tag: 'tr',
16966             cn: [
16967             {
16968                 tag: 'td',
16969                 colspan: '7'
16970             }
16971             ]
16972         }
16973         ]
16974     },
16975     
16976     footer : {
16977         tag: 'tfoot',
16978         cn: [
16979         {
16980             tag: 'tr',
16981             cn: [
16982             {
16983                 tag: 'th',
16984                 colspan: '7',
16985                 cls: 'today'
16986             }
16987                     
16988             ]
16989         }
16990         ]
16991     },
16992     
16993     dates:{
16994         en: {
16995             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16996             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16997             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16998             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16999             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17000             today: "Today"
17001         }
17002     },
17003     
17004     modes: [
17005     {
17006         clsName: 'days',
17007         navFnc: 'Month',
17008         navStep: 1
17009     },
17010     {
17011         clsName: 'months',
17012         navFnc: 'FullYear',
17013         navStep: 1
17014     },
17015     {
17016         clsName: 'years',
17017         navFnc: 'FullYear',
17018         navStep: 10
17019     }]
17020 });
17021
17022 Roo.apply(Roo.bootstrap.DateField,  {
17023   
17024     template : {
17025         tag: 'div',
17026         cls: 'datepicker dropdown-menu roo-dynamic',
17027         cn: [
17028         {
17029             tag: 'div',
17030             cls: 'datepicker-days',
17031             cn: [
17032             {
17033                 tag: 'table',
17034                 cls: 'table-condensed',
17035                 cn:[
17036                 Roo.bootstrap.DateField.head,
17037                 {
17038                     tag: 'tbody'
17039                 },
17040                 Roo.bootstrap.DateField.footer
17041                 ]
17042             }
17043             ]
17044         },
17045         {
17046             tag: 'div',
17047             cls: 'datepicker-months',
17048             cn: [
17049             {
17050                 tag: 'table',
17051                 cls: 'table-condensed',
17052                 cn:[
17053                 Roo.bootstrap.DateField.head,
17054                 Roo.bootstrap.DateField.content,
17055                 Roo.bootstrap.DateField.footer
17056                 ]
17057             }
17058             ]
17059         },
17060         {
17061             tag: 'div',
17062             cls: 'datepicker-years',
17063             cn: [
17064             {
17065                 tag: 'table',
17066                 cls: 'table-condensed',
17067                 cn:[
17068                 Roo.bootstrap.DateField.head,
17069                 Roo.bootstrap.DateField.content,
17070                 Roo.bootstrap.DateField.footer
17071                 ]
17072             }
17073             ]
17074         }
17075         ]
17076     }
17077 });
17078
17079  
17080
17081  /*
17082  * - LGPL
17083  *
17084  * TimeField
17085  * 
17086  */
17087
17088 /**
17089  * @class Roo.bootstrap.TimeField
17090  * @extends Roo.bootstrap.Input
17091  * Bootstrap DateField class
17092  * 
17093  * 
17094  * @constructor
17095  * Create a new TimeField
17096  * @param {Object} config The config object
17097  */
17098
17099 Roo.bootstrap.TimeField = function(config){
17100     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17101     this.addEvents({
17102             /**
17103              * @event show
17104              * Fires when this field show.
17105              * @param {Roo.bootstrap.DateField} thisthis
17106              * @param {Mixed} date The date value
17107              */
17108             show : true,
17109             /**
17110              * @event show
17111              * Fires when this field hide.
17112              * @param {Roo.bootstrap.DateField} this
17113              * @param {Mixed} date The date value
17114              */
17115             hide : true,
17116             /**
17117              * @event select
17118              * Fires when select a date.
17119              * @param {Roo.bootstrap.DateField} this
17120              * @param {Mixed} date The date value
17121              */
17122             select : true
17123         });
17124 };
17125
17126 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17127     
17128     /**
17129      * @cfg {String} format
17130      * The default time format string which can be overriden for localization support.  The format must be
17131      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17132      */
17133     format : "H:i",
17134        
17135     onRender: function(ct, position)
17136     {
17137         
17138         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17139                 
17140         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17141         
17142         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17143         
17144         this.pop = this.picker().select('>.datepicker-time',true).first();
17145         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17146         
17147         this.picker().on('mousedown', this.onMousedown, this);
17148         this.picker().on('click', this.onClick, this);
17149         
17150         this.picker().addClass('datepicker-dropdown');
17151     
17152         this.fillTime();
17153         this.update();
17154             
17155         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17156         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17157         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17158         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17159         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17160         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17161
17162     },
17163     
17164     fireKey: function(e){
17165         if (!this.picker().isVisible()){
17166             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17167                 this.show();
17168             }
17169             return;
17170         }
17171
17172         e.preventDefault();
17173         
17174         switch(e.keyCode){
17175             case 27: // escape
17176                 this.hide();
17177                 break;
17178             case 37: // left
17179             case 39: // right
17180                 this.onTogglePeriod();
17181                 break;
17182             case 38: // up
17183                 this.onIncrementMinutes();
17184                 break;
17185             case 40: // down
17186                 this.onDecrementMinutes();
17187                 break;
17188             case 13: // enter
17189             case 9: // tab
17190                 this.setTime();
17191                 break;
17192         }
17193     },
17194     
17195     onClick: function(e) {
17196         e.stopPropagation();
17197         e.preventDefault();
17198     },
17199     
17200     picker : function()
17201     {
17202         return this.el.select('.datepicker', true).first();
17203     },
17204     
17205     fillTime: function()
17206     {    
17207         var time = this.pop.select('tbody', true).first();
17208         
17209         time.dom.innerHTML = '';
17210         
17211         time.createChild({
17212             tag: 'tr',
17213             cn: [
17214                 {
17215                     tag: 'td',
17216                     cn: [
17217                         {
17218                             tag: 'a',
17219                             href: '#',
17220                             cls: 'btn',
17221                             cn: [
17222                                 {
17223                                     tag: 'span',
17224                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17225                                 }
17226                             ]
17227                         } 
17228                     ]
17229                 },
17230                 {
17231                     tag: 'td',
17232                     cls: 'separator'
17233                 },
17234                 {
17235                     tag: 'td',
17236                     cn: [
17237                         {
17238                             tag: 'a',
17239                             href: '#',
17240                             cls: 'btn',
17241                             cn: [
17242                                 {
17243                                     tag: 'span',
17244                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17245                                 }
17246                             ]
17247                         }
17248                     ]
17249                 },
17250                 {
17251                     tag: 'td',
17252                     cls: 'separator'
17253                 }
17254             ]
17255         });
17256         
17257         time.createChild({
17258             tag: 'tr',
17259             cn: [
17260                 {
17261                     tag: 'td',
17262                     cn: [
17263                         {
17264                             tag: 'span',
17265                             cls: 'timepicker-hour',
17266                             html: '00'
17267                         }  
17268                     ]
17269                 },
17270                 {
17271                     tag: 'td',
17272                     cls: 'separator',
17273                     html: ':'
17274                 },
17275                 {
17276                     tag: 'td',
17277                     cn: [
17278                         {
17279                             tag: 'span',
17280                             cls: 'timepicker-minute',
17281                             html: '00'
17282                         }  
17283                     ]
17284                 },
17285                 {
17286                     tag: 'td',
17287                     cls: 'separator'
17288                 },
17289                 {
17290                     tag: 'td',
17291                     cn: [
17292                         {
17293                             tag: 'button',
17294                             type: 'button',
17295                             cls: 'btn btn-primary period',
17296                             html: 'AM'
17297                             
17298                         }
17299                     ]
17300                 }
17301             ]
17302         });
17303         
17304         time.createChild({
17305             tag: 'tr',
17306             cn: [
17307                 {
17308                     tag: 'td',
17309                     cn: [
17310                         {
17311                             tag: 'a',
17312                             href: '#',
17313                             cls: 'btn',
17314                             cn: [
17315                                 {
17316                                     tag: 'span',
17317                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17318                                 }
17319                             ]
17320                         }
17321                     ]
17322                 },
17323                 {
17324                     tag: 'td',
17325                     cls: 'separator'
17326                 },
17327                 {
17328                     tag: 'td',
17329                     cn: [
17330                         {
17331                             tag: 'a',
17332                             href: '#',
17333                             cls: 'btn',
17334                             cn: [
17335                                 {
17336                                     tag: 'span',
17337                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17338                                 }
17339                             ]
17340                         }
17341                     ]
17342                 },
17343                 {
17344                     tag: 'td',
17345                     cls: 'separator'
17346                 }
17347             ]
17348         });
17349         
17350     },
17351     
17352     update: function()
17353     {
17354         
17355         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17356         
17357         this.fill();
17358     },
17359     
17360     fill: function() 
17361     {
17362         var hours = this.time.getHours();
17363         var minutes = this.time.getMinutes();
17364         var period = 'AM';
17365         
17366         if(hours > 11){
17367             period = 'PM';
17368         }
17369         
17370         if(hours == 0){
17371             hours = 12;
17372         }
17373         
17374         
17375         if(hours > 12){
17376             hours = hours - 12;
17377         }
17378         
17379         if(hours < 10){
17380             hours = '0' + hours;
17381         }
17382         
17383         if(minutes < 10){
17384             minutes = '0' + minutes;
17385         }
17386         
17387         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17388         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17389         this.pop.select('button', true).first().dom.innerHTML = period;
17390         
17391     },
17392     
17393     place: function()
17394     {   
17395         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17396         
17397         var cls = ['bottom'];
17398         
17399         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17400             cls.pop();
17401             cls.push('top');
17402         }
17403         
17404         cls.push('right');
17405         
17406         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17407             cls.pop();
17408             cls.push('left');
17409         }
17410         
17411         this.picker().addClass(cls.join('-'));
17412         
17413         var _this = this;
17414         
17415         Roo.each(cls, function(c){
17416             if(c == 'bottom'){
17417                 _this.picker().setTop(_this.inputEl().getHeight());
17418                 return;
17419             }
17420             if(c == 'top'){
17421                 _this.picker().setTop(0 - _this.picker().getHeight());
17422                 return;
17423             }
17424             
17425             if(c == 'left'){
17426                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17427                 return;
17428             }
17429             if(c == 'right'){
17430                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17431                 return;
17432             }
17433         });
17434         
17435     },
17436   
17437     onFocus : function()
17438     {
17439         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17440         this.show();
17441     },
17442     
17443     onBlur : function()
17444     {
17445         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17446         this.hide();
17447     },
17448     
17449     show : function()
17450     {
17451         this.picker().show();
17452         this.pop.show();
17453         this.update();
17454         this.place();
17455         
17456         this.fireEvent('show', this, this.date);
17457     },
17458     
17459     hide : function()
17460     {
17461         this.picker().hide();
17462         this.pop.hide();
17463         
17464         this.fireEvent('hide', this, this.date);
17465     },
17466     
17467     setTime : function()
17468     {
17469         this.hide();
17470         this.setValue(this.time.format(this.format));
17471         
17472         this.fireEvent('select', this, this.date);
17473         
17474         
17475     },
17476     
17477     onMousedown: function(e){
17478         e.stopPropagation();
17479         e.preventDefault();
17480     },
17481     
17482     onIncrementHours: function()
17483     {
17484         Roo.log('onIncrementHours');
17485         this.time = this.time.add(Date.HOUR, 1);
17486         this.update();
17487         
17488     },
17489     
17490     onDecrementHours: function()
17491     {
17492         Roo.log('onDecrementHours');
17493         this.time = this.time.add(Date.HOUR, -1);
17494         this.update();
17495     },
17496     
17497     onIncrementMinutes: function()
17498     {
17499         Roo.log('onIncrementMinutes');
17500         this.time = this.time.add(Date.MINUTE, 1);
17501         this.update();
17502     },
17503     
17504     onDecrementMinutes: function()
17505     {
17506         Roo.log('onDecrementMinutes');
17507         this.time = this.time.add(Date.MINUTE, -1);
17508         this.update();
17509     },
17510     
17511     onTogglePeriod: function()
17512     {
17513         Roo.log('onTogglePeriod');
17514         this.time = this.time.add(Date.HOUR, 12);
17515         this.update();
17516     }
17517     
17518    
17519 });
17520
17521 Roo.apply(Roo.bootstrap.TimeField,  {
17522     
17523     content : {
17524         tag: 'tbody',
17525         cn: [
17526             {
17527                 tag: 'tr',
17528                 cn: [
17529                 {
17530                     tag: 'td',
17531                     colspan: '7'
17532                 }
17533                 ]
17534             }
17535         ]
17536     },
17537     
17538     footer : {
17539         tag: 'tfoot',
17540         cn: [
17541             {
17542                 tag: 'tr',
17543                 cn: [
17544                 {
17545                     tag: 'th',
17546                     colspan: '7',
17547                     cls: '',
17548                     cn: [
17549                         {
17550                             tag: 'button',
17551                             cls: 'btn btn-info ok',
17552                             html: 'OK'
17553                         }
17554                     ]
17555                 }
17556
17557                 ]
17558             }
17559         ]
17560     }
17561 });
17562
17563 Roo.apply(Roo.bootstrap.TimeField,  {
17564   
17565     template : {
17566         tag: 'div',
17567         cls: 'datepicker dropdown-menu',
17568         cn: [
17569             {
17570                 tag: 'div',
17571                 cls: 'datepicker-time',
17572                 cn: [
17573                 {
17574                     tag: 'table',
17575                     cls: 'table-condensed',
17576                     cn:[
17577                     Roo.bootstrap.TimeField.content,
17578                     Roo.bootstrap.TimeField.footer
17579                     ]
17580                 }
17581                 ]
17582             }
17583         ]
17584     }
17585 });
17586
17587  
17588
17589  /*
17590  * - LGPL
17591  *
17592  * MonthField
17593  * 
17594  */
17595
17596 /**
17597  * @class Roo.bootstrap.MonthField
17598  * @extends Roo.bootstrap.Input
17599  * Bootstrap MonthField class
17600  * 
17601  * @cfg {String} language default en
17602  * 
17603  * @constructor
17604  * Create a new MonthField
17605  * @param {Object} config The config object
17606  */
17607
17608 Roo.bootstrap.MonthField = function(config){
17609     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17610     
17611     this.addEvents({
17612         /**
17613          * @event show
17614          * Fires when this field show.
17615          * @param {Roo.bootstrap.MonthField} this
17616          * @param {Mixed} date The date value
17617          */
17618         show : true,
17619         /**
17620          * @event show
17621          * Fires when this field hide.
17622          * @param {Roo.bootstrap.MonthField} this
17623          * @param {Mixed} date The date value
17624          */
17625         hide : true,
17626         /**
17627          * @event select
17628          * Fires when select a date.
17629          * @param {Roo.bootstrap.MonthField} this
17630          * @param {String} oldvalue The old value
17631          * @param {String} newvalue The new value
17632          */
17633         select : true
17634     });
17635 };
17636
17637 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17638     
17639     onRender: function(ct, position)
17640     {
17641         
17642         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17643         
17644         this.language = this.language || 'en';
17645         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17646         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17647         
17648         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17649         this.isInline = false;
17650         this.isInput = true;
17651         this.component = this.el.select('.add-on', true).first() || false;
17652         this.component = (this.component && this.component.length === 0) ? false : this.component;
17653         this.hasInput = this.component && this.inputEL().length;
17654         
17655         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17656         
17657         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17658         
17659         this.picker().on('mousedown', this.onMousedown, this);
17660         this.picker().on('click', this.onClick, this);
17661         
17662         this.picker().addClass('datepicker-dropdown');
17663         
17664         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17665             v.setStyle('width', '189px');
17666         });
17667         
17668         this.fillMonths();
17669         
17670         this.update();
17671         
17672         if(this.isInline) {
17673             this.show();
17674         }
17675         
17676     },
17677     
17678     setValue: function(v, suppressEvent)
17679     {   
17680         var o = this.getValue();
17681         
17682         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17683         
17684         this.update();
17685
17686         if(suppressEvent !== true){
17687             this.fireEvent('select', this, o, v);
17688         }
17689         
17690     },
17691     
17692     getValue: function()
17693     {
17694         return this.value;
17695     },
17696     
17697     onClick: function(e) 
17698     {
17699         e.stopPropagation();
17700         e.preventDefault();
17701         
17702         var target = e.getTarget();
17703         
17704         if(target.nodeName.toLowerCase() === 'i'){
17705             target = Roo.get(target).dom.parentNode;
17706         }
17707         
17708         var nodeName = target.nodeName;
17709         var className = target.className;
17710         var html = target.innerHTML;
17711         
17712         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17713             return;
17714         }
17715         
17716         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17717         
17718         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17719         
17720         this.hide();
17721                         
17722     },
17723     
17724     picker : function()
17725     {
17726         return this.pickerEl;
17727     },
17728     
17729     fillMonths: function()
17730     {    
17731         var i = 0;
17732         var months = this.picker().select('>.datepicker-months td', true).first();
17733         
17734         months.dom.innerHTML = '';
17735         
17736         while (i < 12) {
17737             var month = {
17738                 tag: 'span',
17739                 cls: 'month',
17740                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17741             }
17742             
17743             months.createChild(month);
17744         }
17745         
17746     },
17747     
17748     update: function()
17749     {
17750         var _this = this;
17751         
17752         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17753             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17754         }
17755         
17756         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17757             e.removeClass('active');
17758             
17759             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17760                 e.addClass('active');
17761             }
17762         })
17763     },
17764     
17765     place: function()
17766     {
17767         if(this.isInline) return;
17768         
17769         this.picker().removeClass(['bottom', 'top']);
17770         
17771         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17772             /*
17773              * place to the top of element!
17774              *
17775              */
17776             
17777             this.picker().addClass('top');
17778             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17779             
17780             return;
17781         }
17782         
17783         this.picker().addClass('bottom');
17784         
17785         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17786     },
17787     
17788     onFocus : function()
17789     {
17790         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17791         this.show();
17792     },
17793     
17794     onBlur : function()
17795     {
17796         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17797         
17798         var d = this.inputEl().getValue();
17799         
17800         this.setValue(d);
17801                 
17802         this.hide();
17803     },
17804     
17805     show : function()
17806     {
17807         this.picker().show();
17808         this.picker().select('>.datepicker-months', true).first().show();
17809         this.update();
17810         this.place();
17811         
17812         this.fireEvent('show', this, this.date);
17813     },
17814     
17815     hide : function()
17816     {
17817         if(this.isInline) return;
17818         this.picker().hide();
17819         this.fireEvent('hide', this, this.date);
17820         
17821     },
17822     
17823     onMousedown: function(e)
17824     {
17825         e.stopPropagation();
17826         e.preventDefault();
17827     },
17828     
17829     keyup: function(e)
17830     {
17831         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17832         this.update();
17833     },
17834
17835     fireKey: function(e)
17836     {
17837         if (!this.picker().isVisible()){
17838             if (e.keyCode == 27) // allow escape to hide and re-show picker
17839                 this.show();
17840             return;
17841         }
17842         
17843         var dir;
17844         
17845         switch(e.keyCode){
17846             case 27: // escape
17847                 this.hide();
17848                 e.preventDefault();
17849                 break;
17850             case 37: // left
17851             case 39: // right
17852                 dir = e.keyCode == 37 ? -1 : 1;
17853                 
17854                 this.vIndex = this.vIndex + dir;
17855                 
17856                 if(this.vIndex < 0){
17857                     this.vIndex = 0;
17858                 }
17859                 
17860                 if(this.vIndex > 11){
17861                     this.vIndex = 11;
17862                 }
17863                 
17864                 if(isNaN(this.vIndex)){
17865                     this.vIndex = 0;
17866                 }
17867                 
17868                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17869                 
17870                 break;
17871             case 38: // up
17872             case 40: // down
17873                 
17874                 dir = e.keyCode == 38 ? -1 : 1;
17875                 
17876                 this.vIndex = this.vIndex + dir * 4;
17877                 
17878                 if(this.vIndex < 0){
17879                     this.vIndex = 0;
17880                 }
17881                 
17882                 if(this.vIndex > 11){
17883                     this.vIndex = 11;
17884                 }
17885                 
17886                 if(isNaN(this.vIndex)){
17887                     this.vIndex = 0;
17888                 }
17889                 
17890                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17891                 break;
17892                 
17893             case 13: // enter
17894                 
17895                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17896                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17897                 }
17898                 
17899                 this.hide();
17900                 e.preventDefault();
17901                 break;
17902             case 9: // tab
17903                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17904                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17905                 }
17906                 this.hide();
17907                 break;
17908             case 16: // shift
17909             case 17: // ctrl
17910             case 18: // alt
17911                 break;
17912             default :
17913                 this.hide();
17914                 
17915         }
17916     },
17917     
17918     remove: function() 
17919     {
17920         this.picker().remove();
17921     }
17922    
17923 });
17924
17925 Roo.apply(Roo.bootstrap.MonthField,  {
17926     
17927     content : {
17928         tag: 'tbody',
17929         cn: [
17930         {
17931             tag: 'tr',
17932             cn: [
17933             {
17934                 tag: 'td',
17935                 colspan: '7'
17936             }
17937             ]
17938         }
17939         ]
17940     },
17941     
17942     dates:{
17943         en: {
17944             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17945             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17946         }
17947     }
17948 });
17949
17950 Roo.apply(Roo.bootstrap.MonthField,  {
17951   
17952     template : {
17953         tag: 'div',
17954         cls: 'datepicker dropdown-menu roo-dynamic',
17955         cn: [
17956             {
17957                 tag: 'div',
17958                 cls: 'datepicker-months',
17959                 cn: [
17960                 {
17961                     tag: 'table',
17962                     cls: 'table-condensed',
17963                     cn:[
17964                         Roo.bootstrap.DateField.content
17965                     ]
17966                 }
17967                 ]
17968             }
17969         ]
17970     }
17971 });
17972
17973  
17974
17975  
17976  /*
17977  * - LGPL
17978  *
17979  * CheckBox
17980  * 
17981  */
17982
17983 /**
17984  * @class Roo.bootstrap.CheckBox
17985  * @extends Roo.bootstrap.Input
17986  * Bootstrap CheckBox class
17987  * 
17988  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17989  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17990  * @cfg {String} boxLabel The text that appears beside the checkbox
17991  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17992  * @cfg {Boolean} checked initnal the element
17993  * @cfg {Boolean} inline inline the element (default false)
17994  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17995  * 
17996  * @constructor
17997  * Create a new CheckBox
17998  * @param {Object} config The config object
17999  */
18000
18001 Roo.bootstrap.CheckBox = function(config){
18002     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18003    
18004     this.addEvents({
18005         /**
18006         * @event check
18007         * Fires when the element is checked or unchecked.
18008         * @param {Roo.bootstrap.CheckBox} this This input
18009         * @param {Boolean} checked The new checked value
18010         */
18011        check : true
18012     });
18013     
18014 };
18015
18016 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18017   
18018     inputType: 'checkbox',
18019     inputValue: 1,
18020     valueOff: 0,
18021     boxLabel: false,
18022     checked: false,
18023     weight : false,
18024     inline: false,
18025     
18026     getAutoCreate : function()
18027     {
18028         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18029         
18030         var id = Roo.id();
18031         
18032         var cfg = {};
18033         
18034         cfg.cls = 'form-group ' + this.inputType; //input-group
18035         
18036         if(this.inline){
18037             cfg.cls += ' ' + this.inputType + '-inline';
18038         }
18039         
18040         var input =  {
18041             tag: 'input',
18042             id : id,
18043             type : this.inputType,
18044             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18045             cls : 'roo-' + this.inputType, //'form-box',
18046             placeholder : this.placeholder || ''
18047             
18048         };
18049         
18050         if (this.weight) { // Validity check?
18051             cfg.cls += " " + this.inputType + "-" + this.weight;
18052         }
18053         
18054         if (this.disabled) {
18055             input.disabled=true;
18056         }
18057         
18058         if(this.checked){
18059             input.checked = this.checked;
18060         }
18061         
18062         if (this.name) {
18063             input.name = this.name;
18064         }
18065         
18066         if (this.size) {
18067             input.cls += ' input-' + this.size;
18068         }
18069         
18070         var settings=this;
18071         
18072         ['xs','sm','md','lg'].map(function(size){
18073             if (settings[size]) {
18074                 cfg.cls += ' col-' + size + '-' + settings[size];
18075             }
18076         });
18077         
18078         var inputblock = input;
18079          
18080         if (this.before || this.after) {
18081             
18082             inputblock = {
18083                 cls : 'input-group',
18084                 cn :  [] 
18085             };
18086             
18087             if (this.before) {
18088                 inputblock.cn.push({
18089                     tag :'span',
18090                     cls : 'input-group-addon',
18091                     html : this.before
18092                 });
18093             }
18094             
18095             inputblock.cn.push(input);
18096             
18097             if (this.after) {
18098                 inputblock.cn.push({
18099                     tag :'span',
18100                     cls : 'input-group-addon',
18101                     html : this.after
18102                 });
18103             }
18104             
18105         }
18106         
18107         if (align ==='left' && this.fieldLabel.length) {
18108                 Roo.log("left and has label");
18109                 cfg.cn = [
18110                     
18111                     {
18112                         tag: 'label',
18113                         'for' :  id,
18114                         cls : 'control-label col-md-' + this.labelWidth,
18115                         html : this.fieldLabel
18116                         
18117                     },
18118                     {
18119                         cls : "col-md-" + (12 - this.labelWidth), 
18120                         cn: [
18121                             inputblock
18122                         ]
18123                     }
18124                     
18125                 ];
18126         } else if ( this.fieldLabel.length) {
18127                 Roo.log(" label");
18128                 cfg.cn = [
18129                    
18130                     {
18131                         tag: this.boxLabel ? 'span' : 'label',
18132                         'for': id,
18133                         cls: 'control-label box-input-label',
18134                         //cls : 'input-group-addon',
18135                         html : this.fieldLabel
18136                         
18137                     },
18138                     
18139                     inputblock
18140                     
18141                 ];
18142
18143         } else {
18144             
18145                 Roo.log(" no label && no align");
18146                 cfg.cn = [  inputblock ] ;
18147                 
18148                 
18149         }
18150         if(this.boxLabel){
18151              var boxLabelCfg = {
18152                 tag: 'label',
18153                 //'for': id, // box label is handled by onclick - so no for...
18154                 cls: 'box-label',
18155                 html: this.boxLabel
18156             }
18157             
18158             if(this.tooltip){
18159                 boxLabelCfg.tooltip = this.tooltip;
18160             }
18161              
18162             cfg.cn.push(boxLabelCfg);
18163         }
18164         
18165         
18166        
18167         return cfg;
18168         
18169     },
18170     
18171     /**
18172      * return the real input element.
18173      */
18174     inputEl: function ()
18175     {
18176         return this.el.select('input.roo-' + this.inputType,true).first();
18177     },
18178     
18179     labelEl: function()
18180     {
18181         return this.el.select('label.control-label',true).first();
18182     },
18183     /* depricated... */
18184     
18185     label: function()
18186     {
18187         return this.labelEl();
18188     },
18189     
18190     initEvents : function()
18191     {
18192 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18193         
18194         this.inputEl().on('click', this.onClick,  this);
18195         
18196         if (this.boxLabel) { 
18197             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18198         }
18199         
18200         this.startValue = this.getValue();
18201         
18202         if(this.groupId){
18203             Roo.bootstrap.CheckBox.register(this);
18204         }
18205     },
18206     
18207     onClick : function()
18208     {   
18209         this.setChecked(!this.checked);
18210     },
18211     
18212     setChecked : function(state,suppressEvent)
18213     {
18214         this.startValue = this.getValue();
18215         
18216         if(this.inputType == 'radio'){
18217             
18218             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18219                 e.dom.checked = false;
18220             });
18221             
18222             this.inputEl().dom.checked = true;
18223             
18224             this.inputEl().dom.value = this.inputValue;
18225             
18226             if(suppressEvent !== true){
18227                 this.fireEvent('check', this, true);
18228             }
18229             
18230             this.validate();
18231             
18232             return;
18233         }
18234         
18235         this.checked = state;
18236         
18237         this.inputEl().dom.checked = state;
18238         
18239         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18240         
18241         if(suppressEvent !== true){
18242             this.fireEvent('check', this, state);
18243         }
18244         
18245         this.validate();
18246     },
18247     
18248     getValue : function()
18249     {
18250         if(this.inputType == 'radio'){
18251             return this.getGroupValue();
18252         }
18253         
18254         return this.inputEl().getValue();
18255         
18256     },
18257     
18258     getGroupValue : function()
18259     {
18260         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18261             return '';
18262         }
18263         
18264         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18265     },
18266     
18267     setValue : function(v,suppressEvent)
18268     {
18269         if(this.inputType == 'radio'){
18270             this.setGroupValue(v, suppressEvent);
18271             return;
18272         }
18273         
18274         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18275         
18276         this.validate();
18277     },
18278     
18279     setGroupValue : function(v, suppressEvent)
18280     {
18281         this.startValue = this.getValue();
18282         
18283         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18284             e.dom.checked = false;
18285             
18286             if(e.dom.value == v){
18287                 e.dom.checked = true;
18288             }
18289         });
18290         
18291         if(suppressEvent !== true){
18292             this.fireEvent('check', this, true);
18293         }
18294
18295         this.validate();
18296         
18297         return;
18298     },
18299     
18300     validate : function()
18301     {
18302         if(
18303                 this.disabled || 
18304                 (this.inputType == 'radio' && this.validateRadio()) ||
18305                 (this.inputType == 'checkbox' && this.validateCheckbox())
18306         ){
18307             this.markValid();
18308             return true;
18309         }
18310         
18311         this.markInvalid();
18312         return false;
18313     },
18314     
18315     validateRadio : function()
18316     {
18317         var valid = false;
18318         
18319         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18320             if(!e.dom.checked){
18321                 return;
18322             }
18323             
18324             valid = true;
18325             
18326             return false;
18327         });
18328         
18329         return valid;
18330     },
18331     
18332     validateCheckbox : function()
18333     {
18334         if(!this.groupId){
18335             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18336         }
18337         
18338         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18339         
18340         if(!group){
18341             return false;
18342         }
18343         
18344         var r = false;
18345         
18346         for(var i in group){
18347             if(r){
18348                 break;
18349             }
18350             
18351             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18352         }
18353         
18354         return r;
18355     },
18356     
18357     /**
18358      * Mark this field as valid
18359      */
18360     markValid : function()
18361     {
18362         if(this.allowBlank){
18363             return;
18364         }
18365         
18366         var _this = this;
18367         
18368         this.fireEvent('valid', this);
18369         
18370         if(this.inputType == 'radio'){
18371             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18372                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18373                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18374             });
18375             
18376             return;
18377         }
18378         
18379         if(!this.groupId){
18380             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18381             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18382             return;
18383         }
18384         
18385         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18386             
18387         if(!group){
18388             return;
18389         }
18390         
18391         for(var i in group){
18392             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18393             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18394         }
18395     },
18396     
18397      /**
18398      * Mark this field as invalid
18399      * @param {String} msg The validation message
18400      */
18401     markInvalid : function(msg)
18402     {
18403         if(this.allowBlank){
18404             return;
18405         }
18406         
18407         var _this = this;
18408         
18409         this.fireEvent('invalid', this, msg);
18410         
18411         if(this.inputType == 'radio'){
18412             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18413                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18414                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18415             });
18416             
18417             return;
18418         }
18419         
18420         if(!this.groupId){
18421             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18422             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18423             return;
18424         }
18425         
18426         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18427             
18428         if(!group){
18429             return;
18430         }
18431         
18432         for(var i in group){
18433             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18434             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18435         }
18436         
18437     }
18438     
18439 });
18440
18441 Roo.apply(Roo.bootstrap.CheckBox, {
18442     
18443     groups: {},
18444     
18445      /**
18446     * register a CheckBox Group
18447     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18448     */
18449     register : function(checkbox)
18450     {
18451         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18452             this.groups[checkbox.groupId] = {};
18453         }
18454         
18455         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18456             return;
18457         }
18458         
18459         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18460         
18461     },
18462     /**
18463     * fetch a CheckBox Group based on the group ID
18464     * @param {string} the group ID
18465     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18466     */
18467     get: function(groupId) {
18468         if (typeof(this.groups[groupId]) == 'undefined') {
18469             return false;
18470         }
18471         
18472         return this.groups[groupId] ;
18473     }
18474     
18475     
18476 });
18477 /*
18478  * - LGPL
18479  *
18480  * Radio
18481  *
18482  *
18483  * not inline
18484  *<div class="radio">
18485   <label>
18486     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18487     Option one is this and that&mdash;be sure to include why it's great
18488   </label>
18489 </div>
18490  *
18491  *
18492  *inline
18493  *<span>
18494  *<label class="radio-inline">fieldLabel</label>
18495  *<label class="radio-inline">
18496   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18497 </label>
18498 <span>
18499  * 
18500  * 
18501  */
18502
18503 /**
18504  * @class Roo.bootstrap.Radio
18505  * @extends Roo.bootstrap.CheckBox
18506  * Bootstrap Radio class
18507
18508  * @constructor
18509  * Create a new Radio
18510  * @param {Object} config The config object
18511  */
18512
18513 Roo.bootstrap.Radio = function(config){
18514     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18515    
18516 };
18517
18518 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18519     
18520     inputType: 'radio',
18521     inputValue: '',
18522     valueOff: '',
18523     
18524     getAutoCreate : function()
18525     {
18526         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18527         align = align || 'left'; // default...
18528         
18529         
18530         
18531         var id = Roo.id();
18532         
18533         var cfg = {
18534                 tag : this.inline ? 'span' : 'div',
18535                 cls : '',
18536                 cn : []
18537         };
18538         
18539         var inline = this.inline ? ' radio-inline' : '';
18540         
18541         var lbl = {
18542                 tag: 'label' ,
18543                 // does not need for, as we wrap the input with it..
18544                 'for' : id,
18545                 cls : 'control-label box-label' + inline,
18546                 cn : []
18547         };
18548         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18549         
18550         var fieldLabel = {
18551             tag: 'label' ,
18552             //cls : 'control-label' + inline,
18553             html : this.fieldLabel,
18554             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18555         };
18556         
18557  
18558         
18559         
18560         var input =  {
18561             tag: 'input',
18562             id : id,
18563             type : this.inputType,
18564             //value : (!this.checked) ? this.valueOff : this.inputValue,
18565             value : this.inputValue,
18566             cls : 'roo-radio',
18567             placeholder : this.placeholder || '' // ?? needed????
18568             
18569         };
18570         if (this.weight) { // Validity check?
18571             input.cls += " radio-" + this.weight;
18572         }
18573         if (this.disabled) {
18574             input.disabled=true;
18575         }
18576         
18577         if(this.checked){
18578             input.checked = this.checked;
18579         }
18580         
18581         if (this.name) {
18582             input.name = this.name;
18583         }
18584         
18585         if (this.size) {
18586             input.cls += ' input-' + this.size;
18587         }
18588         
18589         //?? can span's inline have a width??
18590         
18591         var settings=this;
18592         ['xs','sm','md','lg'].map(function(size){
18593             if (settings[size]) {
18594                 cfg.cls += ' col-' + size + '-' + settings[size];
18595             }
18596         });
18597         
18598         var inputblock = input;
18599         
18600         if (this.before || this.after) {
18601             
18602             inputblock = {
18603                 cls : 'input-group',
18604                 tag : 'span',
18605                 cn :  [] 
18606             };
18607             if (this.before) {
18608                 inputblock.cn.push({
18609                     tag :'span',
18610                     cls : 'input-group-addon',
18611                     html : this.before
18612                 });
18613             }
18614             inputblock.cn.push(input);
18615             if (this.after) {
18616                 inputblock.cn.push({
18617                     tag :'span',
18618                     cls : 'input-group-addon',
18619                     html : this.after
18620                 });
18621             }
18622             
18623         };
18624         
18625         
18626         if (this.fieldLabel && this.fieldLabel.length) {
18627             cfg.cn.push(fieldLabel);
18628         }
18629        
18630         // normal bootstrap puts the input inside the label.
18631         // however with our styled version - it has to go after the input.
18632        
18633         //lbl.cn.push(inputblock);
18634         
18635         var lblwrap =  {
18636             tag: 'span',
18637             cls: 'radio' + inline,
18638             cn: [
18639                 inputblock,
18640                 lbl
18641             ]
18642         };
18643         
18644         cfg.cn.push( lblwrap);
18645         
18646         if(this.boxLabel){
18647             lbl.cn.push({
18648                 tag: 'span',
18649                 html: this.boxLabel
18650             })
18651         }
18652          
18653         
18654         return cfg;
18655         
18656     },
18657     
18658     initEvents : function()
18659     {
18660 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18661         
18662         this.inputEl().on('click', this.onClick,  this);
18663         if (this.boxLabel) {
18664             Roo.log('find label')
18665             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18666         }
18667         
18668     },
18669     
18670     inputEl: function ()
18671     {
18672         return this.el.select('input.roo-radio',true).first();
18673     },
18674     onClick : function()
18675     {   
18676         Roo.log("click");
18677         this.setChecked(true);
18678     },
18679     
18680     setChecked : function(state,suppressEvent)
18681     {
18682         if(state){
18683             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18684                 v.dom.checked = false;
18685             });
18686         }
18687         Roo.log(this.inputEl().dom);
18688         this.checked = state;
18689         this.inputEl().dom.checked = state;
18690         
18691         if(suppressEvent !== true){
18692             this.fireEvent('check', this, state);
18693         }
18694         
18695         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18696         
18697     },
18698     
18699     getGroupValue : function()
18700     {
18701         var value = '';
18702         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18703             if(v.dom.checked == true){
18704                 value = v.dom.value;
18705             }
18706         });
18707         
18708         return value;
18709     },
18710     
18711     /**
18712      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18713      * @return {Mixed} value The field value
18714      */
18715     getValue : function(){
18716         return this.getGroupValue();
18717     }
18718     
18719 });
18720
18721  
18722 //<script type="text/javascript">
18723
18724 /*
18725  * Based  Ext JS Library 1.1.1
18726  * Copyright(c) 2006-2007, Ext JS, LLC.
18727  * LGPL
18728  *
18729  */
18730  
18731 /**
18732  * @class Roo.HtmlEditorCore
18733  * @extends Roo.Component
18734  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18735  *
18736  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18737  */
18738
18739 Roo.HtmlEditorCore = function(config){
18740     
18741     
18742     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18743     
18744     
18745     this.addEvents({
18746         /**
18747          * @event initialize
18748          * Fires when the editor is fully initialized (including the iframe)
18749          * @param {Roo.HtmlEditorCore} this
18750          */
18751         initialize: true,
18752         /**
18753          * @event activate
18754          * Fires when the editor is first receives the focus. Any insertion must wait
18755          * until after this event.
18756          * @param {Roo.HtmlEditorCore} this
18757          */
18758         activate: true,
18759          /**
18760          * @event beforesync
18761          * Fires before the textarea is updated with content from the editor iframe. Return false
18762          * to cancel the sync.
18763          * @param {Roo.HtmlEditorCore} this
18764          * @param {String} html
18765          */
18766         beforesync: true,
18767          /**
18768          * @event beforepush
18769          * Fires before the iframe editor is updated with content from the textarea. Return false
18770          * to cancel the push.
18771          * @param {Roo.HtmlEditorCore} this
18772          * @param {String} html
18773          */
18774         beforepush: true,
18775          /**
18776          * @event sync
18777          * Fires when the textarea is updated with content from the editor iframe.
18778          * @param {Roo.HtmlEditorCore} this
18779          * @param {String} html
18780          */
18781         sync: true,
18782          /**
18783          * @event push
18784          * Fires when the iframe editor is updated with content from the textarea.
18785          * @param {Roo.HtmlEditorCore} this
18786          * @param {String} html
18787          */
18788         push: true,
18789         
18790         /**
18791          * @event editorevent
18792          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18793          * @param {Roo.HtmlEditorCore} this
18794          */
18795         editorevent: true
18796         
18797     });
18798     
18799     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18800     
18801     // defaults : white / black...
18802     this.applyBlacklists();
18803     
18804     
18805     
18806 };
18807
18808
18809 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18810
18811
18812      /**
18813      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18814      */
18815     
18816     owner : false,
18817     
18818      /**
18819      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18820      *                        Roo.resizable.
18821      */
18822     resizable : false,
18823      /**
18824      * @cfg {Number} height (in pixels)
18825      */   
18826     height: 300,
18827    /**
18828      * @cfg {Number} width (in pixels)
18829      */   
18830     width: 500,
18831     
18832     /**
18833      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18834      * 
18835      */
18836     stylesheets: false,
18837     
18838     // id of frame..
18839     frameId: false,
18840     
18841     // private properties
18842     validationEvent : false,
18843     deferHeight: true,
18844     initialized : false,
18845     activated : false,
18846     sourceEditMode : false,
18847     onFocus : Roo.emptyFn,
18848     iframePad:3,
18849     hideMode:'offsets',
18850     
18851     clearUp: true,
18852     
18853     // blacklist + whitelisted elements..
18854     black: false,
18855     white: false,
18856      
18857     
18858
18859     /**
18860      * Protected method that will not generally be called directly. It
18861      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18862      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18863      */
18864     getDocMarkup : function(){
18865         // body styles..
18866         var st = '';
18867         
18868         // inherit styels from page...?? 
18869         if (this.stylesheets === false) {
18870             
18871             Roo.get(document.head).select('style').each(function(node) {
18872                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18873             });
18874             
18875             Roo.get(document.head).select('link').each(function(node) { 
18876                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18877             });
18878             
18879         } else if (!this.stylesheets.length) {
18880                 // simple..
18881                 st = '<style type="text/css">' +
18882                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18883                    '</style>';
18884         } else { 
18885             
18886         }
18887         
18888         st +=  '<style type="text/css">' +
18889             'IMG { cursor: pointer } ' +
18890         '</style>';
18891
18892         
18893         return '<html><head>' + st  +
18894             //<style type="text/css">' +
18895             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18896             //'</style>' +
18897             ' </head><body class="roo-htmleditor-body"></body></html>';
18898     },
18899
18900     // private
18901     onRender : function(ct, position)
18902     {
18903         var _t = this;
18904         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18905         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18906         
18907         
18908         this.el.dom.style.border = '0 none';
18909         this.el.dom.setAttribute('tabIndex', -1);
18910         this.el.addClass('x-hidden hide');
18911         
18912         
18913         
18914         if(Roo.isIE){ // fix IE 1px bogus margin
18915             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18916         }
18917        
18918         
18919         this.frameId = Roo.id();
18920         
18921          
18922         
18923         var iframe = this.owner.wrap.createChild({
18924             tag: 'iframe',
18925             cls: 'form-control', // bootstrap..
18926             id: this.frameId,
18927             name: this.frameId,
18928             frameBorder : 'no',
18929             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18930         }, this.el
18931         );
18932         
18933         
18934         this.iframe = iframe.dom;
18935
18936          this.assignDocWin();
18937         
18938         this.doc.designMode = 'on';
18939        
18940         this.doc.open();
18941         this.doc.write(this.getDocMarkup());
18942         this.doc.close();
18943
18944         
18945         var task = { // must defer to wait for browser to be ready
18946             run : function(){
18947                 //console.log("run task?" + this.doc.readyState);
18948                 this.assignDocWin();
18949                 if(this.doc.body || this.doc.readyState == 'complete'){
18950                     try {
18951                         this.doc.designMode="on";
18952                     } catch (e) {
18953                         return;
18954                     }
18955                     Roo.TaskMgr.stop(task);
18956                     this.initEditor.defer(10, this);
18957                 }
18958             },
18959             interval : 10,
18960             duration: 10000,
18961             scope: this
18962         };
18963         Roo.TaskMgr.start(task);
18964
18965     },
18966
18967     // private
18968     onResize : function(w, h)
18969     {
18970          Roo.log('resize: ' +w + ',' + h );
18971         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18972         if(!this.iframe){
18973             return;
18974         }
18975         if(typeof w == 'number'){
18976             
18977             this.iframe.style.width = w + 'px';
18978         }
18979         if(typeof h == 'number'){
18980             
18981             this.iframe.style.height = h + 'px';
18982             if(this.doc){
18983                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18984             }
18985         }
18986         
18987     },
18988
18989     /**
18990      * Toggles the editor between standard and source edit mode.
18991      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18992      */
18993     toggleSourceEdit : function(sourceEditMode){
18994         
18995         this.sourceEditMode = sourceEditMode === true;
18996         
18997         if(this.sourceEditMode){
18998  
18999             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19000             
19001         }else{
19002             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19003             //this.iframe.className = '';
19004             this.deferFocus();
19005         }
19006         //this.setSize(this.owner.wrap.getSize());
19007         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19008     },
19009
19010     
19011   
19012
19013     /**
19014      * Protected method that will not generally be called directly. If you need/want
19015      * custom HTML cleanup, this is the method you should override.
19016      * @param {String} html The HTML to be cleaned
19017      * return {String} The cleaned HTML
19018      */
19019     cleanHtml : function(html){
19020         html = String(html);
19021         if(html.length > 5){
19022             if(Roo.isSafari){ // strip safari nonsense
19023                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19024             }
19025         }
19026         if(html == '&nbsp;'){
19027             html = '';
19028         }
19029         return html;
19030     },
19031
19032     /**
19033      * HTML Editor -> Textarea
19034      * Protected method that will not generally be called directly. Syncs the contents
19035      * of the editor iframe with the textarea.
19036      */
19037     syncValue : function(){
19038         if(this.initialized){
19039             var bd = (this.doc.body || this.doc.documentElement);
19040             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19041             var html = bd.innerHTML;
19042             if(Roo.isSafari){
19043                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19044                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19045                 if(m && m[1]){
19046                     html = '<div style="'+m[0]+'">' + html + '</div>';
19047                 }
19048             }
19049             html = this.cleanHtml(html);
19050             // fix up the special chars.. normaly like back quotes in word...
19051             // however we do not want to do this with chinese..
19052             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19053                 var cc = b.charCodeAt();
19054                 if (
19055                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19056                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19057                     (cc >= 0xf900 && cc < 0xfb00 )
19058                 ) {
19059                         return b;
19060                 }
19061                 return "&#"+cc+";" 
19062             });
19063             if(this.owner.fireEvent('beforesync', this, html) !== false){
19064                 this.el.dom.value = html;
19065                 this.owner.fireEvent('sync', this, html);
19066             }
19067         }
19068     },
19069
19070     /**
19071      * Protected method that will not generally be called directly. Pushes the value of the textarea
19072      * into the iframe editor.
19073      */
19074     pushValue : function(){
19075         if(this.initialized){
19076             var v = this.el.dom.value.trim();
19077             
19078 //            if(v.length < 1){
19079 //                v = '&#160;';
19080 //            }
19081             
19082             if(this.owner.fireEvent('beforepush', this, v) !== false){
19083                 var d = (this.doc.body || this.doc.documentElement);
19084                 d.innerHTML = v;
19085                 this.cleanUpPaste();
19086                 this.el.dom.value = d.innerHTML;
19087                 this.owner.fireEvent('push', this, v);
19088             }
19089         }
19090     },
19091
19092     // private
19093     deferFocus : function(){
19094         this.focus.defer(10, this);
19095     },
19096
19097     // doc'ed in Field
19098     focus : function(){
19099         if(this.win && !this.sourceEditMode){
19100             this.win.focus();
19101         }else{
19102             this.el.focus();
19103         }
19104     },
19105     
19106     assignDocWin: function()
19107     {
19108         var iframe = this.iframe;
19109         
19110          if(Roo.isIE){
19111             this.doc = iframe.contentWindow.document;
19112             this.win = iframe.contentWindow;
19113         } else {
19114 //            if (!Roo.get(this.frameId)) {
19115 //                return;
19116 //            }
19117 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19118 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19119             
19120             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19121                 return;
19122             }
19123             
19124             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19125             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19126         }
19127     },
19128     
19129     // private
19130     initEditor : function(){
19131         //console.log("INIT EDITOR");
19132         this.assignDocWin();
19133         
19134         
19135         
19136         this.doc.designMode="on";
19137         this.doc.open();
19138         this.doc.write(this.getDocMarkup());
19139         this.doc.close();
19140         
19141         var dbody = (this.doc.body || this.doc.documentElement);
19142         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19143         // this copies styles from the containing element into thsi one..
19144         // not sure why we need all of this..
19145         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19146         
19147         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19148         //ss['background-attachment'] = 'fixed'; // w3c
19149         dbody.bgProperties = 'fixed'; // ie
19150         //Roo.DomHelper.applyStyles(dbody, ss);
19151         Roo.EventManager.on(this.doc, {
19152             //'mousedown': this.onEditorEvent,
19153             'mouseup': this.onEditorEvent,
19154             'dblclick': this.onEditorEvent,
19155             'click': this.onEditorEvent,
19156             'keyup': this.onEditorEvent,
19157             buffer:100,
19158             scope: this
19159         });
19160         if(Roo.isGecko){
19161             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19162         }
19163         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19164             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19165         }
19166         this.initialized = true;
19167
19168         this.owner.fireEvent('initialize', this);
19169         this.pushValue();
19170     },
19171
19172     // private
19173     onDestroy : function(){
19174         
19175         
19176         
19177         if(this.rendered){
19178             
19179             //for (var i =0; i < this.toolbars.length;i++) {
19180             //    // fixme - ask toolbars for heights?
19181             //    this.toolbars[i].onDestroy();
19182            // }
19183             
19184             //this.wrap.dom.innerHTML = '';
19185             //this.wrap.remove();
19186         }
19187     },
19188
19189     // private
19190     onFirstFocus : function(){
19191         
19192         this.assignDocWin();
19193         
19194         
19195         this.activated = true;
19196          
19197     
19198         if(Roo.isGecko){ // prevent silly gecko errors
19199             this.win.focus();
19200             var s = this.win.getSelection();
19201             if(!s.focusNode || s.focusNode.nodeType != 3){
19202                 var r = s.getRangeAt(0);
19203                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19204                 r.collapse(true);
19205                 this.deferFocus();
19206             }
19207             try{
19208                 this.execCmd('useCSS', true);
19209                 this.execCmd('styleWithCSS', false);
19210             }catch(e){}
19211         }
19212         this.owner.fireEvent('activate', this);
19213     },
19214
19215     // private
19216     adjustFont: function(btn){
19217         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19218         //if(Roo.isSafari){ // safari
19219         //    adjust *= 2;
19220        // }
19221         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19222         if(Roo.isSafari){ // safari
19223             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19224             v =  (v < 10) ? 10 : v;
19225             v =  (v > 48) ? 48 : v;
19226             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19227             
19228         }
19229         
19230         
19231         v = Math.max(1, v+adjust);
19232         
19233         this.execCmd('FontSize', v  );
19234     },
19235
19236     onEditorEvent : function(e)
19237     {
19238         this.owner.fireEvent('editorevent', this, e);
19239       //  this.updateToolbar();
19240         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19241     },
19242
19243     insertTag : function(tg)
19244     {
19245         // could be a bit smarter... -> wrap the current selected tRoo..
19246         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19247             
19248             range = this.createRange(this.getSelection());
19249             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19250             wrappingNode.appendChild(range.extractContents());
19251             range.insertNode(wrappingNode);
19252
19253             return;
19254             
19255             
19256             
19257         }
19258         this.execCmd("formatblock",   tg);
19259         
19260     },
19261     
19262     insertText : function(txt)
19263     {
19264         
19265         
19266         var range = this.createRange();
19267         range.deleteContents();
19268                //alert(Sender.getAttribute('label'));
19269                
19270         range.insertNode(this.doc.createTextNode(txt));
19271     } ,
19272     
19273      
19274
19275     /**
19276      * Executes a Midas editor command on the editor document and performs necessary focus and
19277      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19278      * @param {String} cmd The Midas command
19279      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19280      */
19281     relayCmd : function(cmd, value){
19282         this.win.focus();
19283         this.execCmd(cmd, value);
19284         this.owner.fireEvent('editorevent', this);
19285         //this.updateToolbar();
19286         this.owner.deferFocus();
19287     },
19288
19289     /**
19290      * Executes a Midas editor command directly on the editor document.
19291      * For visual commands, you should use {@link #relayCmd} instead.
19292      * <b>This should only be called after the editor is initialized.</b>
19293      * @param {String} cmd The Midas command
19294      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19295      */
19296     execCmd : function(cmd, value){
19297         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19298         this.syncValue();
19299     },
19300  
19301  
19302    
19303     /**
19304      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19305      * to insert tRoo.
19306      * @param {String} text | dom node.. 
19307      */
19308     insertAtCursor : function(text)
19309     {
19310         
19311         
19312         
19313         if(!this.activated){
19314             return;
19315         }
19316         /*
19317         if(Roo.isIE){
19318             this.win.focus();
19319             var r = this.doc.selection.createRange();
19320             if(r){
19321                 r.collapse(true);
19322                 r.pasteHTML(text);
19323                 this.syncValue();
19324                 this.deferFocus();
19325             
19326             }
19327             return;
19328         }
19329         */
19330         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19331             this.win.focus();
19332             
19333             
19334             // from jquery ui (MIT licenced)
19335             var range, node;
19336             var win = this.win;
19337             
19338             if (win.getSelection && win.getSelection().getRangeAt) {
19339                 range = win.getSelection().getRangeAt(0);
19340                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19341                 range.insertNode(node);
19342             } else if (win.document.selection && win.document.selection.createRange) {
19343                 // no firefox support
19344                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19345                 win.document.selection.createRange().pasteHTML(txt);
19346             } else {
19347                 // no firefox support
19348                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19349                 this.execCmd('InsertHTML', txt);
19350             } 
19351             
19352             this.syncValue();
19353             
19354             this.deferFocus();
19355         }
19356     },
19357  // private
19358     mozKeyPress : function(e){
19359         if(e.ctrlKey){
19360             var c = e.getCharCode(), cmd;
19361           
19362             if(c > 0){
19363                 c = String.fromCharCode(c).toLowerCase();
19364                 switch(c){
19365                     case 'b':
19366                         cmd = 'bold';
19367                         break;
19368                     case 'i':
19369                         cmd = 'italic';
19370                         break;
19371                     
19372                     case 'u':
19373                         cmd = 'underline';
19374                         break;
19375                     
19376                     case 'v':
19377                         this.cleanUpPaste.defer(100, this);
19378                         return;
19379                         
19380                 }
19381                 if(cmd){
19382                     this.win.focus();
19383                     this.execCmd(cmd);
19384                     this.deferFocus();
19385                     e.preventDefault();
19386                 }
19387                 
19388             }
19389         }
19390     },
19391
19392     // private
19393     fixKeys : function(){ // load time branching for fastest keydown performance
19394         if(Roo.isIE){
19395             return function(e){
19396                 var k = e.getKey(), r;
19397                 if(k == e.TAB){
19398                     e.stopEvent();
19399                     r = this.doc.selection.createRange();
19400                     if(r){
19401                         r.collapse(true);
19402                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19403                         this.deferFocus();
19404                     }
19405                     return;
19406                 }
19407                 
19408                 if(k == e.ENTER){
19409                     r = this.doc.selection.createRange();
19410                     if(r){
19411                         var target = r.parentElement();
19412                         if(!target || target.tagName.toLowerCase() != 'li'){
19413                             e.stopEvent();
19414                             r.pasteHTML('<br />');
19415                             r.collapse(false);
19416                             r.select();
19417                         }
19418                     }
19419                 }
19420                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19421                     this.cleanUpPaste.defer(100, this);
19422                     return;
19423                 }
19424                 
19425                 
19426             };
19427         }else if(Roo.isOpera){
19428             return function(e){
19429                 var k = e.getKey();
19430                 if(k == e.TAB){
19431                     e.stopEvent();
19432                     this.win.focus();
19433                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19434                     this.deferFocus();
19435                 }
19436                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19437                     this.cleanUpPaste.defer(100, this);
19438                     return;
19439                 }
19440                 
19441             };
19442         }else if(Roo.isSafari){
19443             return function(e){
19444                 var k = e.getKey();
19445                 
19446                 if(k == e.TAB){
19447                     e.stopEvent();
19448                     this.execCmd('InsertText','\t');
19449                     this.deferFocus();
19450                     return;
19451                 }
19452                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19453                     this.cleanUpPaste.defer(100, this);
19454                     return;
19455                 }
19456                 
19457              };
19458         }
19459     }(),
19460     
19461     getAllAncestors: function()
19462     {
19463         var p = this.getSelectedNode();
19464         var a = [];
19465         if (!p) {
19466             a.push(p); // push blank onto stack..
19467             p = this.getParentElement();
19468         }
19469         
19470         
19471         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19472             a.push(p);
19473             p = p.parentNode;
19474         }
19475         a.push(this.doc.body);
19476         return a;
19477     },
19478     lastSel : false,
19479     lastSelNode : false,
19480     
19481     
19482     getSelection : function() 
19483     {
19484         this.assignDocWin();
19485         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19486     },
19487     
19488     getSelectedNode: function() 
19489     {
19490         // this may only work on Gecko!!!
19491         
19492         // should we cache this!!!!
19493         
19494         
19495         
19496          
19497         var range = this.createRange(this.getSelection()).cloneRange();
19498         
19499         if (Roo.isIE) {
19500             var parent = range.parentElement();
19501             while (true) {
19502                 var testRange = range.duplicate();
19503                 testRange.moveToElementText(parent);
19504                 if (testRange.inRange(range)) {
19505                     break;
19506                 }
19507                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19508                     break;
19509                 }
19510                 parent = parent.parentElement;
19511             }
19512             return parent;
19513         }
19514         
19515         // is ancestor a text element.
19516         var ac =  range.commonAncestorContainer;
19517         if (ac.nodeType == 3) {
19518             ac = ac.parentNode;
19519         }
19520         
19521         var ar = ac.childNodes;
19522          
19523         var nodes = [];
19524         var other_nodes = [];
19525         var has_other_nodes = false;
19526         for (var i=0;i<ar.length;i++) {
19527             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19528                 continue;
19529             }
19530             // fullly contained node.
19531             
19532             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19533                 nodes.push(ar[i]);
19534                 continue;
19535             }
19536             
19537             // probably selected..
19538             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19539                 other_nodes.push(ar[i]);
19540                 continue;
19541             }
19542             // outer..
19543             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19544                 continue;
19545             }
19546             
19547             
19548             has_other_nodes = true;
19549         }
19550         if (!nodes.length && other_nodes.length) {
19551             nodes= other_nodes;
19552         }
19553         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19554             return false;
19555         }
19556         
19557         return nodes[0];
19558     },
19559     createRange: function(sel)
19560     {
19561         // this has strange effects when using with 
19562         // top toolbar - not sure if it's a great idea.
19563         //this.editor.contentWindow.focus();
19564         if (typeof sel != "undefined") {
19565             try {
19566                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19567             } catch(e) {
19568                 return this.doc.createRange();
19569             }
19570         } else {
19571             return this.doc.createRange();
19572         }
19573     },
19574     getParentElement: function()
19575     {
19576         
19577         this.assignDocWin();
19578         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19579         
19580         var range = this.createRange(sel);
19581          
19582         try {
19583             var p = range.commonAncestorContainer;
19584             while (p.nodeType == 3) { // text node
19585                 p = p.parentNode;
19586             }
19587             return p;
19588         } catch (e) {
19589             return null;
19590         }
19591     
19592     },
19593     /***
19594      *
19595      * Range intersection.. the hard stuff...
19596      *  '-1' = before
19597      *  '0' = hits..
19598      *  '1' = after.
19599      *         [ -- selected range --- ]
19600      *   [fail]                        [fail]
19601      *
19602      *    basically..
19603      *      if end is before start or  hits it. fail.
19604      *      if start is after end or hits it fail.
19605      *
19606      *   if either hits (but other is outside. - then it's not 
19607      *   
19608      *    
19609      **/
19610     
19611     
19612     // @see http://www.thismuchiknow.co.uk/?p=64.
19613     rangeIntersectsNode : function(range, node)
19614     {
19615         var nodeRange = node.ownerDocument.createRange();
19616         try {
19617             nodeRange.selectNode(node);
19618         } catch (e) {
19619             nodeRange.selectNodeContents(node);
19620         }
19621     
19622         var rangeStartRange = range.cloneRange();
19623         rangeStartRange.collapse(true);
19624     
19625         var rangeEndRange = range.cloneRange();
19626         rangeEndRange.collapse(false);
19627     
19628         var nodeStartRange = nodeRange.cloneRange();
19629         nodeStartRange.collapse(true);
19630     
19631         var nodeEndRange = nodeRange.cloneRange();
19632         nodeEndRange.collapse(false);
19633     
19634         return rangeStartRange.compareBoundaryPoints(
19635                  Range.START_TO_START, nodeEndRange) == -1 &&
19636                rangeEndRange.compareBoundaryPoints(
19637                  Range.START_TO_START, nodeStartRange) == 1;
19638         
19639          
19640     },
19641     rangeCompareNode : function(range, node)
19642     {
19643         var nodeRange = node.ownerDocument.createRange();
19644         try {
19645             nodeRange.selectNode(node);
19646         } catch (e) {
19647             nodeRange.selectNodeContents(node);
19648         }
19649         
19650         
19651         range.collapse(true);
19652     
19653         nodeRange.collapse(true);
19654      
19655         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19656         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19657          
19658         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19659         
19660         var nodeIsBefore   =  ss == 1;
19661         var nodeIsAfter    = ee == -1;
19662         
19663         if (nodeIsBefore && nodeIsAfter)
19664             return 0; // outer
19665         if (!nodeIsBefore && nodeIsAfter)
19666             return 1; //right trailed.
19667         
19668         if (nodeIsBefore && !nodeIsAfter)
19669             return 2;  // left trailed.
19670         // fully contined.
19671         return 3;
19672     },
19673
19674     // private? - in a new class?
19675     cleanUpPaste :  function()
19676     {
19677         // cleans up the whole document..
19678         Roo.log('cleanuppaste');
19679         
19680         this.cleanUpChildren(this.doc.body);
19681         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19682         if (clean != this.doc.body.innerHTML) {
19683             this.doc.body.innerHTML = clean;
19684         }
19685         
19686     },
19687     
19688     cleanWordChars : function(input) {// change the chars to hex code
19689         var he = Roo.HtmlEditorCore;
19690         
19691         var output = input;
19692         Roo.each(he.swapCodes, function(sw) { 
19693             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19694             
19695             output = output.replace(swapper, sw[1]);
19696         });
19697         
19698         return output;
19699     },
19700     
19701     
19702     cleanUpChildren : function (n)
19703     {
19704         if (!n.childNodes.length) {
19705             return;
19706         }
19707         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19708            this.cleanUpChild(n.childNodes[i]);
19709         }
19710     },
19711     
19712     
19713         
19714     
19715     cleanUpChild : function (node)
19716     {
19717         var ed = this;
19718         //console.log(node);
19719         if (node.nodeName == "#text") {
19720             // clean up silly Windows -- stuff?
19721             return; 
19722         }
19723         if (node.nodeName == "#comment") {
19724             node.parentNode.removeChild(node);
19725             // clean up silly Windows -- stuff?
19726             return; 
19727         }
19728         var lcname = node.tagName.toLowerCase();
19729         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19730         // whitelist of tags..
19731         
19732         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19733             // remove node.
19734             node.parentNode.removeChild(node);
19735             return;
19736             
19737         }
19738         
19739         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19740         
19741         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19742         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19743         
19744         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19745         //    remove_keep_children = true;
19746         //}
19747         
19748         if (remove_keep_children) {
19749             this.cleanUpChildren(node);
19750             // inserts everything just before this node...
19751             while (node.childNodes.length) {
19752                 var cn = node.childNodes[0];
19753                 node.removeChild(cn);
19754                 node.parentNode.insertBefore(cn, node);
19755             }
19756             node.parentNode.removeChild(node);
19757             return;
19758         }
19759         
19760         if (!node.attributes || !node.attributes.length) {
19761             this.cleanUpChildren(node);
19762             return;
19763         }
19764         
19765         function cleanAttr(n,v)
19766         {
19767             
19768             if (v.match(/^\./) || v.match(/^\//)) {
19769                 return;
19770             }
19771             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19772                 return;
19773             }
19774             if (v.match(/^#/)) {
19775                 return;
19776             }
19777 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19778             node.removeAttribute(n);
19779             
19780         }
19781         
19782         var cwhite = this.cwhite;
19783         var cblack = this.cblack;
19784             
19785         function cleanStyle(n,v)
19786         {
19787             if (v.match(/expression/)) { //XSS?? should we even bother..
19788                 node.removeAttribute(n);
19789                 return;
19790             }
19791             
19792             var parts = v.split(/;/);
19793             var clean = [];
19794             
19795             Roo.each(parts, function(p) {
19796                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19797                 if (!p.length) {
19798                     return true;
19799                 }
19800                 var l = p.split(':').shift().replace(/\s+/g,'');
19801                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19802                 
19803                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19804 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19805                     //node.removeAttribute(n);
19806                     return true;
19807                 }
19808                 //Roo.log()
19809                 // only allow 'c whitelisted system attributes'
19810                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19811 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19812                     //node.removeAttribute(n);
19813                     return true;
19814                 }
19815                 
19816                 
19817                  
19818                 
19819                 clean.push(p);
19820                 return true;
19821             });
19822             if (clean.length) { 
19823                 node.setAttribute(n, clean.join(';'));
19824             } else {
19825                 node.removeAttribute(n);
19826             }
19827             
19828         }
19829         
19830         
19831         for (var i = node.attributes.length-1; i > -1 ; i--) {
19832             var a = node.attributes[i];
19833             //console.log(a);
19834             
19835             if (a.name.toLowerCase().substr(0,2)=='on')  {
19836                 node.removeAttribute(a.name);
19837                 continue;
19838             }
19839             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19840                 node.removeAttribute(a.name);
19841                 continue;
19842             }
19843             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19844                 cleanAttr(a.name,a.value); // fixme..
19845                 continue;
19846             }
19847             if (a.name == 'style') {
19848                 cleanStyle(a.name,a.value);
19849                 continue;
19850             }
19851             /// clean up MS crap..
19852             // tecnically this should be a list of valid class'es..
19853             
19854             
19855             if (a.name == 'class') {
19856                 if (a.value.match(/^Mso/)) {
19857                     node.className = '';
19858                 }
19859                 
19860                 if (a.value.match(/body/)) {
19861                     node.className = '';
19862                 }
19863                 continue;
19864             }
19865             
19866             // style cleanup!?
19867             // class cleanup?
19868             
19869         }
19870         
19871         
19872         this.cleanUpChildren(node);
19873         
19874         
19875     },
19876     
19877     /**
19878      * Clean up MS wordisms...
19879      */
19880     cleanWord : function(node)
19881     {
19882         
19883         
19884         if (!node) {
19885             this.cleanWord(this.doc.body);
19886             return;
19887         }
19888         if (node.nodeName == "#text") {
19889             // clean up silly Windows -- stuff?
19890             return; 
19891         }
19892         if (node.nodeName == "#comment") {
19893             node.parentNode.removeChild(node);
19894             // clean up silly Windows -- stuff?
19895             return; 
19896         }
19897         
19898         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19899             node.parentNode.removeChild(node);
19900             return;
19901         }
19902         
19903         // remove - but keep children..
19904         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19905             while (node.childNodes.length) {
19906                 var cn = node.childNodes[0];
19907                 node.removeChild(cn);
19908                 node.parentNode.insertBefore(cn, node);
19909             }
19910             node.parentNode.removeChild(node);
19911             this.iterateChildren(node, this.cleanWord);
19912             return;
19913         }
19914         // clean styles
19915         if (node.className.length) {
19916             
19917             var cn = node.className.split(/\W+/);
19918             var cna = [];
19919             Roo.each(cn, function(cls) {
19920                 if (cls.match(/Mso[a-zA-Z]+/)) {
19921                     return;
19922                 }
19923                 cna.push(cls);
19924             });
19925             node.className = cna.length ? cna.join(' ') : '';
19926             if (!cna.length) {
19927                 node.removeAttribute("class");
19928             }
19929         }
19930         
19931         if (node.hasAttribute("lang")) {
19932             node.removeAttribute("lang");
19933         }
19934         
19935         if (node.hasAttribute("style")) {
19936             
19937             var styles = node.getAttribute("style").split(";");
19938             var nstyle = [];
19939             Roo.each(styles, function(s) {
19940                 if (!s.match(/:/)) {
19941                     return;
19942                 }
19943                 var kv = s.split(":");
19944                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19945                     return;
19946                 }
19947                 // what ever is left... we allow.
19948                 nstyle.push(s);
19949             });
19950             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19951             if (!nstyle.length) {
19952                 node.removeAttribute('style');
19953             }
19954         }
19955         this.iterateChildren(node, this.cleanWord);
19956         
19957         
19958         
19959     },
19960     /**
19961      * iterateChildren of a Node, calling fn each time, using this as the scole..
19962      * @param {DomNode} node node to iterate children of.
19963      * @param {Function} fn method of this class to call on each item.
19964      */
19965     iterateChildren : function(node, fn)
19966     {
19967         if (!node.childNodes.length) {
19968                 return;
19969         }
19970         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19971            fn.call(this, node.childNodes[i])
19972         }
19973     },
19974     
19975     
19976     /**
19977      * cleanTableWidths.
19978      *
19979      * Quite often pasting from word etc.. results in tables with column and widths.
19980      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19981      *
19982      */
19983     cleanTableWidths : function(node)
19984     {
19985          
19986          
19987         if (!node) {
19988             this.cleanTableWidths(this.doc.body);
19989             return;
19990         }
19991         
19992         // ignore list...
19993         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19994             return; 
19995         }
19996         Roo.log(node.tagName);
19997         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19998             this.iterateChildren(node, this.cleanTableWidths);
19999             return;
20000         }
20001         if (node.hasAttribute('width')) {
20002             node.removeAttribute('width');
20003         }
20004         
20005          
20006         if (node.hasAttribute("style")) {
20007             // pretty basic...
20008             
20009             var styles = node.getAttribute("style").split(";");
20010             var nstyle = [];
20011             Roo.each(styles, function(s) {
20012                 if (!s.match(/:/)) {
20013                     return;
20014                 }
20015                 var kv = s.split(":");
20016                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20017                     return;
20018                 }
20019                 // what ever is left... we allow.
20020                 nstyle.push(s);
20021             });
20022             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20023             if (!nstyle.length) {
20024                 node.removeAttribute('style');
20025             }
20026         }
20027         
20028         this.iterateChildren(node, this.cleanTableWidths);
20029         
20030         
20031     },
20032     
20033     
20034     
20035     
20036     domToHTML : function(currentElement, depth, nopadtext) {
20037         
20038         depth = depth || 0;
20039         nopadtext = nopadtext || false;
20040     
20041         if (!currentElement) {
20042             return this.domToHTML(this.doc.body);
20043         }
20044         
20045         //Roo.log(currentElement);
20046         var j;
20047         var allText = false;
20048         var nodeName = currentElement.nodeName;
20049         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20050         
20051         if  (nodeName == '#text') {
20052             
20053             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20054         }
20055         
20056         
20057         var ret = '';
20058         if (nodeName != 'BODY') {
20059              
20060             var i = 0;
20061             // Prints the node tagName, such as <A>, <IMG>, etc
20062             if (tagName) {
20063                 var attr = [];
20064                 for(i = 0; i < currentElement.attributes.length;i++) {
20065                     // quoting?
20066                     var aname = currentElement.attributes.item(i).name;
20067                     if (!currentElement.attributes.item(i).value.length) {
20068                         continue;
20069                     }
20070                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20071                 }
20072                 
20073                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20074             } 
20075             else {
20076                 
20077                 // eack
20078             }
20079         } else {
20080             tagName = false;
20081         }
20082         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20083             return ret;
20084         }
20085         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20086             nopadtext = true;
20087         }
20088         
20089         
20090         // Traverse the tree
20091         i = 0;
20092         var currentElementChild = currentElement.childNodes.item(i);
20093         var allText = true;
20094         var innerHTML  = '';
20095         lastnode = '';
20096         while (currentElementChild) {
20097             // Formatting code (indent the tree so it looks nice on the screen)
20098             var nopad = nopadtext;
20099             if (lastnode == 'SPAN') {
20100                 nopad  = true;
20101             }
20102             // text
20103             if  (currentElementChild.nodeName == '#text') {
20104                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20105                 toadd = nopadtext ? toadd : toadd.trim();
20106                 if (!nopad && toadd.length > 80) {
20107                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20108                 }
20109                 innerHTML  += toadd;
20110                 
20111                 i++;
20112                 currentElementChild = currentElement.childNodes.item(i);
20113                 lastNode = '';
20114                 continue;
20115             }
20116             allText = false;
20117             
20118             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20119                 
20120             // Recursively traverse the tree structure of the child node
20121             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20122             lastnode = currentElementChild.nodeName;
20123             i++;
20124             currentElementChild=currentElement.childNodes.item(i);
20125         }
20126         
20127         ret += innerHTML;
20128         
20129         if (!allText) {
20130                 // The remaining code is mostly for formatting the tree
20131             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20132         }
20133         
20134         
20135         if (tagName) {
20136             ret+= "</"+tagName+">";
20137         }
20138         return ret;
20139         
20140     },
20141         
20142     applyBlacklists : function()
20143     {
20144         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20145         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20146         
20147         this.white = [];
20148         this.black = [];
20149         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20150             if (b.indexOf(tag) > -1) {
20151                 return;
20152             }
20153             this.white.push(tag);
20154             
20155         }, this);
20156         
20157         Roo.each(w, function(tag) {
20158             if (b.indexOf(tag) > -1) {
20159                 return;
20160             }
20161             if (this.white.indexOf(tag) > -1) {
20162                 return;
20163             }
20164             this.white.push(tag);
20165             
20166         }, this);
20167         
20168         
20169         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20170             if (w.indexOf(tag) > -1) {
20171                 return;
20172             }
20173             this.black.push(tag);
20174             
20175         }, this);
20176         
20177         Roo.each(b, function(tag) {
20178             if (w.indexOf(tag) > -1) {
20179                 return;
20180             }
20181             if (this.black.indexOf(tag) > -1) {
20182                 return;
20183             }
20184             this.black.push(tag);
20185             
20186         }, this);
20187         
20188         
20189         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20190         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20191         
20192         this.cwhite = [];
20193         this.cblack = [];
20194         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20195             if (b.indexOf(tag) > -1) {
20196                 return;
20197             }
20198             this.cwhite.push(tag);
20199             
20200         }, this);
20201         
20202         Roo.each(w, function(tag) {
20203             if (b.indexOf(tag) > -1) {
20204                 return;
20205             }
20206             if (this.cwhite.indexOf(tag) > -1) {
20207                 return;
20208             }
20209             this.cwhite.push(tag);
20210             
20211         }, this);
20212         
20213         
20214         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20215             if (w.indexOf(tag) > -1) {
20216                 return;
20217             }
20218             this.cblack.push(tag);
20219             
20220         }, this);
20221         
20222         Roo.each(b, function(tag) {
20223             if (w.indexOf(tag) > -1) {
20224                 return;
20225             }
20226             if (this.cblack.indexOf(tag) > -1) {
20227                 return;
20228             }
20229             this.cblack.push(tag);
20230             
20231         }, this);
20232     },
20233     
20234     setStylesheets : function(stylesheets)
20235     {
20236         if(typeof(stylesheets) == 'string'){
20237             Roo.get(this.iframe.contentDocument.head).createChild({
20238                 tag : 'link',
20239                 rel : 'stylesheet',
20240                 type : 'text/css',
20241                 href : stylesheets
20242             });
20243             
20244             return;
20245         }
20246         var _this = this;
20247      
20248         Roo.each(stylesheets, function(s) {
20249             if(!s.length){
20250                 return;
20251             }
20252             
20253             Roo.get(_this.iframe.contentDocument.head).createChild({
20254                 tag : 'link',
20255                 rel : 'stylesheet',
20256                 type : 'text/css',
20257                 href : s
20258             });
20259         });
20260
20261         
20262     },
20263     
20264     removeStylesheets : function()
20265     {
20266         var _this = this;
20267         
20268         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20269             s.remove();
20270         });
20271     }
20272     
20273     // hide stuff that is not compatible
20274     /**
20275      * @event blur
20276      * @hide
20277      */
20278     /**
20279      * @event change
20280      * @hide
20281      */
20282     /**
20283      * @event focus
20284      * @hide
20285      */
20286     /**
20287      * @event specialkey
20288      * @hide
20289      */
20290     /**
20291      * @cfg {String} fieldClass @hide
20292      */
20293     /**
20294      * @cfg {String} focusClass @hide
20295      */
20296     /**
20297      * @cfg {String} autoCreate @hide
20298      */
20299     /**
20300      * @cfg {String} inputType @hide
20301      */
20302     /**
20303      * @cfg {String} invalidClass @hide
20304      */
20305     /**
20306      * @cfg {String} invalidText @hide
20307      */
20308     /**
20309      * @cfg {String} msgFx @hide
20310      */
20311     /**
20312      * @cfg {String} validateOnBlur @hide
20313      */
20314 });
20315
20316 Roo.HtmlEditorCore.white = [
20317         'area', 'br', 'img', 'input', 'hr', 'wbr',
20318         
20319        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20320        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20321        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20322        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20323        'table',   'ul',         'xmp', 
20324        
20325        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20326       'thead',   'tr', 
20327      
20328       'dir', 'menu', 'ol', 'ul', 'dl',
20329        
20330       'embed',  'object'
20331 ];
20332
20333
20334 Roo.HtmlEditorCore.black = [
20335     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20336         'applet', // 
20337         'base',   'basefont', 'bgsound', 'blink',  'body', 
20338         'frame',  'frameset', 'head',    'html',   'ilayer', 
20339         'iframe', 'layer',  'link',     'meta',    'object',   
20340         'script', 'style' ,'title',  'xml' // clean later..
20341 ];
20342 Roo.HtmlEditorCore.clean = [
20343     'script', 'style', 'title', 'xml'
20344 ];
20345 Roo.HtmlEditorCore.remove = [
20346     'font'
20347 ];
20348 // attributes..
20349
20350 Roo.HtmlEditorCore.ablack = [
20351     'on'
20352 ];
20353     
20354 Roo.HtmlEditorCore.aclean = [ 
20355     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20356 ];
20357
20358 // protocols..
20359 Roo.HtmlEditorCore.pwhite= [
20360         'http',  'https',  'mailto'
20361 ];
20362
20363 // white listed style attributes.
20364 Roo.HtmlEditorCore.cwhite= [
20365       //  'text-align', /// default is to allow most things..
20366       
20367          
20368 //        'font-size'//??
20369 ];
20370
20371 // black listed style attributes.
20372 Roo.HtmlEditorCore.cblack= [
20373       //  'font-size' -- this can be set by the project 
20374 ];
20375
20376
20377 Roo.HtmlEditorCore.swapCodes   =[ 
20378     [    8211, "--" ], 
20379     [    8212, "--" ], 
20380     [    8216,  "'" ],  
20381     [    8217, "'" ],  
20382     [    8220, '"' ],  
20383     [    8221, '"' ],  
20384     [    8226, "*" ],  
20385     [    8230, "..." ]
20386 ]; 
20387
20388     /*
20389  * - LGPL
20390  *
20391  * HtmlEditor
20392  * 
20393  */
20394
20395 /**
20396  * @class Roo.bootstrap.HtmlEditor
20397  * @extends Roo.bootstrap.TextArea
20398  * Bootstrap HtmlEditor class
20399
20400  * @constructor
20401  * Create a new HtmlEditor
20402  * @param {Object} config The config object
20403  */
20404
20405 Roo.bootstrap.HtmlEditor = function(config){
20406     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20407     if (!this.toolbars) {
20408         this.toolbars = [];
20409     }
20410     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20411     this.addEvents({
20412             /**
20413              * @event initialize
20414              * Fires when the editor is fully initialized (including the iframe)
20415              * @param {HtmlEditor} this
20416              */
20417             initialize: true,
20418             /**
20419              * @event activate
20420              * Fires when the editor is first receives the focus. Any insertion must wait
20421              * until after this event.
20422              * @param {HtmlEditor} this
20423              */
20424             activate: true,
20425              /**
20426              * @event beforesync
20427              * Fires before the textarea is updated with content from the editor iframe. Return false
20428              * to cancel the sync.
20429              * @param {HtmlEditor} this
20430              * @param {String} html
20431              */
20432             beforesync: true,
20433              /**
20434              * @event beforepush
20435              * Fires before the iframe editor is updated with content from the textarea. Return false
20436              * to cancel the push.
20437              * @param {HtmlEditor} this
20438              * @param {String} html
20439              */
20440             beforepush: true,
20441              /**
20442              * @event sync
20443              * Fires when the textarea is updated with content from the editor iframe.
20444              * @param {HtmlEditor} this
20445              * @param {String} html
20446              */
20447             sync: true,
20448              /**
20449              * @event push
20450              * Fires when the iframe editor is updated with content from the textarea.
20451              * @param {HtmlEditor} this
20452              * @param {String} html
20453              */
20454             push: true,
20455              /**
20456              * @event editmodechange
20457              * Fires when the editor switches edit modes
20458              * @param {HtmlEditor} this
20459              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20460              */
20461             editmodechange: true,
20462             /**
20463              * @event editorevent
20464              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20465              * @param {HtmlEditor} this
20466              */
20467             editorevent: true,
20468             /**
20469              * @event firstfocus
20470              * Fires when on first focus - needed by toolbars..
20471              * @param {HtmlEditor} this
20472              */
20473             firstfocus: true,
20474             /**
20475              * @event autosave
20476              * Auto save the htmlEditor value as a file into Events
20477              * @param {HtmlEditor} this
20478              */
20479             autosave: true,
20480             /**
20481              * @event savedpreview
20482              * preview the saved version of htmlEditor
20483              * @param {HtmlEditor} this
20484              */
20485             savedpreview: true
20486         });
20487 };
20488
20489
20490 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20491     
20492     
20493       /**
20494      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20495      */
20496     toolbars : false,
20497    
20498      /**
20499      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20500      *                        Roo.resizable.
20501      */
20502     resizable : false,
20503      /**
20504      * @cfg {Number} height (in pixels)
20505      */   
20506     height: 300,
20507    /**
20508      * @cfg {Number} width (in pixels)
20509      */   
20510     width: false,
20511     
20512     /**
20513      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20514      * 
20515      */
20516     stylesheets: false,
20517     
20518     // id of frame..
20519     frameId: false,
20520     
20521     // private properties
20522     validationEvent : false,
20523     deferHeight: true,
20524     initialized : false,
20525     activated : false,
20526     
20527     onFocus : Roo.emptyFn,
20528     iframePad:3,
20529     hideMode:'offsets',
20530     
20531     
20532     tbContainer : false,
20533     
20534     toolbarContainer :function() {
20535         return this.wrap.select('.x-html-editor-tb',true).first();
20536     },
20537
20538     /**
20539      * Protected method that will not generally be called directly. It
20540      * is called when the editor creates its toolbar. Override this method if you need to
20541      * add custom toolbar buttons.
20542      * @param {HtmlEditor} editor
20543      */
20544     createToolbar : function(){
20545         
20546         Roo.log("create toolbars");
20547         
20548         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20549         this.toolbars[0].render(this.toolbarContainer());
20550         
20551         return;
20552         
20553 //        if (!editor.toolbars || !editor.toolbars.length) {
20554 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20555 //        }
20556 //        
20557 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20558 //            editor.toolbars[i] = Roo.factory(
20559 //                    typeof(editor.toolbars[i]) == 'string' ?
20560 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20561 //                Roo.bootstrap.HtmlEditor);
20562 //            editor.toolbars[i].init(editor);
20563 //        }
20564     },
20565
20566      
20567     // private
20568     onRender : function(ct, position)
20569     {
20570        // Roo.log("Call onRender: " + this.xtype);
20571         var _t = this;
20572         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20573       
20574         this.wrap = this.inputEl().wrap({
20575             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20576         });
20577         
20578         this.editorcore.onRender(ct, position);
20579          
20580         if (this.resizable) {
20581             this.resizeEl = new Roo.Resizable(this.wrap, {
20582                 pinned : true,
20583                 wrap: true,
20584                 dynamic : true,
20585                 minHeight : this.height,
20586                 height: this.height,
20587                 handles : this.resizable,
20588                 width: this.width,
20589                 listeners : {
20590                     resize : function(r, w, h) {
20591                         _t.onResize(w,h); // -something
20592                     }
20593                 }
20594             });
20595             
20596         }
20597         this.createToolbar(this);
20598        
20599         
20600         if(!this.width && this.resizable){
20601             this.setSize(this.wrap.getSize());
20602         }
20603         if (this.resizeEl) {
20604             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20605             // should trigger onReize..
20606         }
20607         
20608     },
20609
20610     // private
20611     onResize : function(w, h)
20612     {
20613         Roo.log('resize: ' +w + ',' + h );
20614         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20615         var ew = false;
20616         var eh = false;
20617         
20618         if(this.inputEl() ){
20619             if(typeof w == 'number'){
20620                 var aw = w - this.wrap.getFrameWidth('lr');
20621                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20622                 ew = aw;
20623             }
20624             if(typeof h == 'number'){
20625                  var tbh = -11;  // fixme it needs to tool bar size!
20626                 for (var i =0; i < this.toolbars.length;i++) {
20627                     // fixme - ask toolbars for heights?
20628                     tbh += this.toolbars[i].el.getHeight();
20629                     //if (this.toolbars[i].footer) {
20630                     //    tbh += this.toolbars[i].footer.el.getHeight();
20631                     //}
20632                 }
20633               
20634                 
20635                 
20636                 
20637                 
20638                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20639                 ah -= 5; // knock a few pixes off for look..
20640                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20641                 var eh = ah;
20642             }
20643         }
20644         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20645         this.editorcore.onResize(ew,eh);
20646         
20647     },
20648
20649     /**
20650      * Toggles the editor between standard and source edit mode.
20651      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20652      */
20653     toggleSourceEdit : function(sourceEditMode)
20654     {
20655         this.editorcore.toggleSourceEdit(sourceEditMode);
20656         
20657         if(this.editorcore.sourceEditMode){
20658             Roo.log('editor - showing textarea');
20659             
20660 //            Roo.log('in');
20661 //            Roo.log(this.syncValue());
20662             this.syncValue();
20663             this.inputEl().removeClass(['hide', 'x-hidden']);
20664             this.inputEl().dom.removeAttribute('tabIndex');
20665             this.inputEl().focus();
20666         }else{
20667             Roo.log('editor - hiding textarea');
20668 //            Roo.log('out')
20669 //            Roo.log(this.pushValue()); 
20670             this.pushValue();
20671             
20672             this.inputEl().addClass(['hide', 'x-hidden']);
20673             this.inputEl().dom.setAttribute('tabIndex', -1);
20674             //this.deferFocus();
20675         }
20676          
20677         if(this.resizable){
20678             this.setSize(this.wrap.getSize());
20679         }
20680         
20681         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20682     },
20683  
20684     // private (for BoxComponent)
20685     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20686
20687     // private (for BoxComponent)
20688     getResizeEl : function(){
20689         return this.wrap;
20690     },
20691
20692     // private (for BoxComponent)
20693     getPositionEl : function(){
20694         return this.wrap;
20695     },
20696
20697     // private
20698     initEvents : function(){
20699         this.originalValue = this.getValue();
20700     },
20701
20702 //    /**
20703 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20704 //     * @method
20705 //     */
20706 //    markInvalid : Roo.emptyFn,
20707 //    /**
20708 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20709 //     * @method
20710 //     */
20711 //    clearInvalid : Roo.emptyFn,
20712
20713     setValue : function(v){
20714         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20715         this.editorcore.pushValue();
20716     },
20717
20718      
20719     // private
20720     deferFocus : function(){
20721         this.focus.defer(10, this);
20722     },
20723
20724     // doc'ed in Field
20725     focus : function(){
20726         this.editorcore.focus();
20727         
20728     },
20729       
20730
20731     // private
20732     onDestroy : function(){
20733         
20734         
20735         
20736         if(this.rendered){
20737             
20738             for (var i =0; i < this.toolbars.length;i++) {
20739                 // fixme - ask toolbars for heights?
20740                 this.toolbars[i].onDestroy();
20741             }
20742             
20743             this.wrap.dom.innerHTML = '';
20744             this.wrap.remove();
20745         }
20746     },
20747
20748     // private
20749     onFirstFocus : function(){
20750         //Roo.log("onFirstFocus");
20751         this.editorcore.onFirstFocus();
20752          for (var i =0; i < this.toolbars.length;i++) {
20753             this.toolbars[i].onFirstFocus();
20754         }
20755         
20756     },
20757     
20758     // private
20759     syncValue : function()
20760     {   
20761         this.editorcore.syncValue();
20762     },
20763     
20764     pushValue : function()
20765     {   
20766         this.editorcore.pushValue();
20767     }
20768      
20769     
20770     // hide stuff that is not compatible
20771     /**
20772      * @event blur
20773      * @hide
20774      */
20775     /**
20776      * @event change
20777      * @hide
20778      */
20779     /**
20780      * @event focus
20781      * @hide
20782      */
20783     /**
20784      * @event specialkey
20785      * @hide
20786      */
20787     /**
20788      * @cfg {String} fieldClass @hide
20789      */
20790     /**
20791      * @cfg {String} focusClass @hide
20792      */
20793     /**
20794      * @cfg {String} autoCreate @hide
20795      */
20796     /**
20797      * @cfg {String} inputType @hide
20798      */
20799     /**
20800      * @cfg {String} invalidClass @hide
20801      */
20802     /**
20803      * @cfg {String} invalidText @hide
20804      */
20805     /**
20806      * @cfg {String} msgFx @hide
20807      */
20808     /**
20809      * @cfg {String} validateOnBlur @hide
20810      */
20811 });
20812  
20813     
20814    
20815    
20816    
20817       
20818 Roo.namespace('Roo.bootstrap.htmleditor');
20819 /**
20820  * @class Roo.bootstrap.HtmlEditorToolbar1
20821  * Basic Toolbar
20822  * 
20823  * Usage:
20824  *
20825  new Roo.bootstrap.HtmlEditor({
20826     ....
20827     toolbars : [
20828         new Roo.bootstrap.HtmlEditorToolbar1({
20829             disable : { fonts: 1 , format: 1, ..., ... , ...],
20830             btns : [ .... ]
20831         })
20832     }
20833      
20834  * 
20835  * @cfg {Object} disable List of elements to disable..
20836  * @cfg {Array} btns List of additional buttons.
20837  * 
20838  * 
20839  * NEEDS Extra CSS? 
20840  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20841  */
20842  
20843 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20844 {
20845     
20846     Roo.apply(this, config);
20847     
20848     // default disabled, based on 'good practice'..
20849     this.disable = this.disable || {};
20850     Roo.applyIf(this.disable, {
20851         fontSize : true,
20852         colors : true,
20853         specialElements : true
20854     });
20855     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20856     
20857     this.editor = config.editor;
20858     this.editorcore = config.editor.editorcore;
20859     
20860     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20861     
20862     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20863     // dont call parent... till later.
20864 }
20865 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20866      
20867     bar : true,
20868     
20869     editor : false,
20870     editorcore : false,
20871     
20872     
20873     formats : [
20874         "p" ,  
20875         "h1","h2","h3","h4","h5","h6", 
20876         "pre", "code", 
20877         "abbr", "acronym", "address", "cite", "samp", "var",
20878         'div','span'
20879     ],
20880     
20881     onRender : function(ct, position)
20882     {
20883        // Roo.log("Call onRender: " + this.xtype);
20884         
20885        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20886        Roo.log(this.el);
20887        this.el.dom.style.marginBottom = '0';
20888        var _this = this;
20889        var editorcore = this.editorcore;
20890        var editor= this.editor;
20891        
20892        var children = [];
20893        var btn = function(id,cmd , toggle, handler){
20894        
20895             var  event = toggle ? 'toggle' : 'click';
20896        
20897             var a = {
20898                 size : 'sm',
20899                 xtype: 'Button',
20900                 xns: Roo.bootstrap,
20901                 glyphicon : id,
20902                 cmd : id || cmd,
20903                 enableToggle:toggle !== false,
20904                 //html : 'submit'
20905                 pressed : toggle ? false : null,
20906                 listeners : {}
20907             };
20908             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20909                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20910             };
20911             children.push(a);
20912             return a;
20913        }
20914         
20915         var style = {
20916                 xtype: 'Button',
20917                 size : 'sm',
20918                 xns: Roo.bootstrap,
20919                 glyphicon : 'font',
20920                 //html : 'submit'
20921                 menu : {
20922                     xtype: 'Menu',
20923                     xns: Roo.bootstrap,
20924                     items:  []
20925                 }
20926         };
20927         Roo.each(this.formats, function(f) {
20928             style.menu.items.push({
20929                 xtype :'MenuItem',
20930                 xns: Roo.bootstrap,
20931                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20932                 tagname : f,
20933                 listeners : {
20934                     click : function()
20935                     {
20936                         editorcore.insertTag(this.tagname);
20937                         editor.focus();
20938                     }
20939                 }
20940                 
20941             });
20942         });
20943          children.push(style);   
20944             
20945             
20946         btn('bold',false,true);
20947         btn('italic',false,true);
20948         btn('align-left', 'justifyleft',true);
20949         btn('align-center', 'justifycenter',true);
20950         btn('align-right' , 'justifyright',true);
20951         btn('link', false, false, function(btn) {
20952             //Roo.log("create link?");
20953             var url = prompt(this.createLinkText, this.defaultLinkValue);
20954             if(url && url != 'http:/'+'/'){
20955                 this.editorcore.relayCmd('createlink', url);
20956             }
20957         }),
20958         btn('list','insertunorderedlist',true);
20959         btn('pencil', false,true, function(btn){
20960                 Roo.log(this);
20961                 
20962                 this.toggleSourceEdit(btn.pressed);
20963         });
20964         /*
20965         var cog = {
20966                 xtype: 'Button',
20967                 size : 'sm',
20968                 xns: Roo.bootstrap,
20969                 glyphicon : 'cog',
20970                 //html : 'submit'
20971                 menu : {
20972                     xtype: 'Menu',
20973                     xns: Roo.bootstrap,
20974                     items:  []
20975                 }
20976         };
20977         
20978         cog.menu.items.push({
20979             xtype :'MenuItem',
20980             xns: Roo.bootstrap,
20981             html : Clean styles,
20982             tagname : f,
20983             listeners : {
20984                 click : function()
20985                 {
20986                     editorcore.insertTag(this.tagname);
20987                     editor.focus();
20988                 }
20989             }
20990             
20991         });
20992        */
20993         
20994          
20995        this.xtype = 'NavSimplebar';
20996         
20997         for(var i=0;i< children.length;i++) {
20998             
20999             this.buttons.add(this.addxtypeChild(children[i]));
21000             
21001         }
21002         
21003         editor.on('editorevent', this.updateToolbar, this);
21004     },
21005     onBtnClick : function(id)
21006     {
21007        this.editorcore.relayCmd(id);
21008        this.editorcore.focus();
21009     },
21010     
21011     /**
21012      * Protected method that will not generally be called directly. It triggers
21013      * a toolbar update by reading the markup state of the current selection in the editor.
21014      */
21015     updateToolbar: function(){
21016
21017         if(!this.editorcore.activated){
21018             this.editor.onFirstFocus(); // is this neeed?
21019             return;
21020         }
21021
21022         var btns = this.buttons; 
21023         var doc = this.editorcore.doc;
21024         btns.get('bold').setActive(doc.queryCommandState('bold'));
21025         btns.get('italic').setActive(doc.queryCommandState('italic'));
21026         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21027         
21028         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21029         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21030         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21031         
21032         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21033         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21034          /*
21035         
21036         var ans = this.editorcore.getAllAncestors();
21037         if (this.formatCombo) {
21038             
21039             
21040             var store = this.formatCombo.store;
21041             this.formatCombo.setValue("");
21042             for (var i =0; i < ans.length;i++) {
21043                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21044                     // select it..
21045                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21046                     break;
21047                 }
21048             }
21049         }
21050         
21051         
21052         
21053         // hides menus... - so this cant be on a menu...
21054         Roo.bootstrap.MenuMgr.hideAll();
21055         */
21056         Roo.bootstrap.MenuMgr.hideAll();
21057         //this.editorsyncValue();
21058     },
21059     onFirstFocus: function() {
21060         this.buttons.each(function(item){
21061            item.enable();
21062         });
21063     },
21064     toggleSourceEdit : function(sourceEditMode){
21065         
21066           
21067         if(sourceEditMode){
21068             Roo.log("disabling buttons");
21069            this.buttons.each( function(item){
21070                 if(item.cmd != 'pencil'){
21071                     item.disable();
21072                 }
21073             });
21074           
21075         }else{
21076             Roo.log("enabling buttons");
21077             if(this.editorcore.initialized){
21078                 this.buttons.each( function(item){
21079                     item.enable();
21080                 });
21081             }
21082             
21083         }
21084         Roo.log("calling toggole on editor");
21085         // tell the editor that it's been pressed..
21086         this.editor.toggleSourceEdit(sourceEditMode);
21087        
21088     }
21089 });
21090
21091
21092
21093
21094
21095 /**
21096  * @class Roo.bootstrap.Table.AbstractSelectionModel
21097  * @extends Roo.util.Observable
21098  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21099  * implemented by descendant classes.  This class should not be directly instantiated.
21100  * @constructor
21101  */
21102 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21103     this.locked = false;
21104     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21105 };
21106
21107
21108 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21109     /** @ignore Called by the grid automatically. Do not call directly. */
21110     init : function(grid){
21111         this.grid = grid;
21112         this.initEvents();
21113     },
21114
21115     /**
21116      * Locks the selections.
21117      */
21118     lock : function(){
21119         this.locked = true;
21120     },
21121
21122     /**
21123      * Unlocks the selections.
21124      */
21125     unlock : function(){
21126         this.locked = false;
21127     },
21128
21129     /**
21130      * Returns true if the selections are locked.
21131      * @return {Boolean}
21132      */
21133     isLocked : function(){
21134         return this.locked;
21135     }
21136 });
21137 /**
21138  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21139  * @class Roo.bootstrap.Table.RowSelectionModel
21140  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21141  * It supports multiple selections and keyboard selection/navigation. 
21142  * @constructor
21143  * @param {Object} config
21144  */
21145
21146 Roo.bootstrap.Table.RowSelectionModel = function(config){
21147     Roo.apply(this, config);
21148     this.selections = new Roo.util.MixedCollection(false, function(o){
21149         return o.id;
21150     });
21151
21152     this.last = false;
21153     this.lastActive = false;
21154
21155     this.addEvents({
21156         /**
21157              * @event selectionchange
21158              * Fires when the selection changes
21159              * @param {SelectionModel} this
21160              */
21161             "selectionchange" : true,
21162         /**
21163              * @event afterselectionchange
21164              * Fires after the selection changes (eg. by key press or clicking)
21165              * @param {SelectionModel} this
21166              */
21167             "afterselectionchange" : true,
21168         /**
21169              * @event beforerowselect
21170              * Fires when a row is selected being selected, return false to cancel.
21171              * @param {SelectionModel} this
21172              * @param {Number} rowIndex The selected index
21173              * @param {Boolean} keepExisting False if other selections will be cleared
21174              */
21175             "beforerowselect" : true,
21176         /**
21177              * @event rowselect
21178              * Fires when a row is selected.
21179              * @param {SelectionModel} this
21180              * @param {Number} rowIndex The selected index
21181              * @param {Roo.data.Record} r The record
21182              */
21183             "rowselect" : true,
21184         /**
21185              * @event rowdeselect
21186              * Fires when a row is deselected.
21187              * @param {SelectionModel} this
21188              * @param {Number} rowIndex The selected index
21189              */
21190         "rowdeselect" : true
21191     });
21192     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21193     this.locked = false;
21194 };
21195
21196 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21197     /**
21198      * @cfg {Boolean} singleSelect
21199      * True to allow selection of only one row at a time (defaults to false)
21200      */
21201     singleSelect : false,
21202
21203     // private
21204     initEvents : function(){
21205
21206         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21207             this.grid.on("mousedown", this.handleMouseDown, this);
21208         }else{ // allow click to work like normal
21209             this.grid.on("rowclick", this.handleDragableRowClick, this);
21210         }
21211
21212         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21213             "up" : function(e){
21214                 if(!e.shiftKey){
21215                     this.selectPrevious(e.shiftKey);
21216                 }else if(this.last !== false && this.lastActive !== false){
21217                     var last = this.last;
21218                     this.selectRange(this.last,  this.lastActive-1);
21219                     this.grid.getView().focusRow(this.lastActive);
21220                     if(last !== false){
21221                         this.last = last;
21222                     }
21223                 }else{
21224                     this.selectFirstRow();
21225                 }
21226                 this.fireEvent("afterselectionchange", this);
21227             },
21228             "down" : function(e){
21229                 if(!e.shiftKey){
21230                     this.selectNext(e.shiftKey);
21231                 }else if(this.last !== false && this.lastActive !== false){
21232                     var last = this.last;
21233                     this.selectRange(this.last,  this.lastActive+1);
21234                     this.grid.getView().focusRow(this.lastActive);
21235                     if(last !== false){
21236                         this.last = last;
21237                     }
21238                 }else{
21239                     this.selectFirstRow();
21240                 }
21241                 this.fireEvent("afterselectionchange", this);
21242             },
21243             scope: this
21244         });
21245
21246         var view = this.grid.view;
21247         view.on("refresh", this.onRefresh, this);
21248         view.on("rowupdated", this.onRowUpdated, this);
21249         view.on("rowremoved", this.onRemove, this);
21250     },
21251
21252     // private
21253     onRefresh : function(){
21254         var ds = this.grid.dataSource, i, v = this.grid.view;
21255         var s = this.selections;
21256         s.each(function(r){
21257             if((i = ds.indexOfId(r.id)) != -1){
21258                 v.onRowSelect(i);
21259             }else{
21260                 s.remove(r);
21261             }
21262         });
21263     },
21264
21265     // private
21266     onRemove : function(v, index, r){
21267         this.selections.remove(r);
21268     },
21269
21270     // private
21271     onRowUpdated : function(v, index, r){
21272         if(this.isSelected(r)){
21273             v.onRowSelect(index);
21274         }
21275     },
21276
21277     /**
21278      * Select records.
21279      * @param {Array} records The records to select
21280      * @param {Boolean} keepExisting (optional) True to keep existing selections
21281      */
21282     selectRecords : function(records, keepExisting){
21283         if(!keepExisting){
21284             this.clearSelections();
21285         }
21286         var ds = this.grid.dataSource;
21287         for(var i = 0, len = records.length; i < len; i++){
21288             this.selectRow(ds.indexOf(records[i]), true);
21289         }
21290     },
21291
21292     /**
21293      * Gets the number of selected rows.
21294      * @return {Number}
21295      */
21296     getCount : function(){
21297         return this.selections.length;
21298     },
21299
21300     /**
21301      * Selects the first row in the grid.
21302      */
21303     selectFirstRow : function(){
21304         this.selectRow(0);
21305     },
21306
21307     /**
21308      * Select the last row.
21309      * @param {Boolean} keepExisting (optional) True to keep existing selections
21310      */
21311     selectLastRow : function(keepExisting){
21312         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21313     },
21314
21315     /**
21316      * Selects the row immediately following the last selected row.
21317      * @param {Boolean} keepExisting (optional) True to keep existing selections
21318      */
21319     selectNext : function(keepExisting){
21320         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21321             this.selectRow(this.last+1, keepExisting);
21322             this.grid.getView().focusRow(this.last);
21323         }
21324     },
21325
21326     /**
21327      * Selects the row that precedes the last selected row.
21328      * @param {Boolean} keepExisting (optional) True to keep existing selections
21329      */
21330     selectPrevious : function(keepExisting){
21331         if(this.last){
21332             this.selectRow(this.last-1, keepExisting);
21333             this.grid.getView().focusRow(this.last);
21334         }
21335     },
21336
21337     /**
21338      * Returns the selected records
21339      * @return {Array} Array of selected records
21340      */
21341     getSelections : function(){
21342         return [].concat(this.selections.items);
21343     },
21344
21345     /**
21346      * Returns the first selected record.
21347      * @return {Record}
21348      */
21349     getSelected : function(){
21350         return this.selections.itemAt(0);
21351     },
21352
21353
21354     /**
21355      * Clears all selections.
21356      */
21357     clearSelections : function(fast){
21358         if(this.locked) return;
21359         if(fast !== true){
21360             var ds = this.grid.dataSource;
21361             var s = this.selections;
21362             s.each(function(r){
21363                 this.deselectRow(ds.indexOfId(r.id));
21364             }, this);
21365             s.clear();
21366         }else{
21367             this.selections.clear();
21368         }
21369         this.last = false;
21370     },
21371
21372
21373     /**
21374      * Selects all rows.
21375      */
21376     selectAll : function(){
21377         if(this.locked) return;
21378         this.selections.clear();
21379         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21380             this.selectRow(i, true);
21381         }
21382     },
21383
21384     /**
21385      * Returns True if there is a selection.
21386      * @return {Boolean}
21387      */
21388     hasSelection : function(){
21389         return this.selections.length > 0;
21390     },
21391
21392     /**
21393      * Returns True if the specified row is selected.
21394      * @param {Number/Record} record The record or index of the record to check
21395      * @return {Boolean}
21396      */
21397     isSelected : function(index){
21398         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21399         return (r && this.selections.key(r.id) ? true : false);
21400     },
21401
21402     /**
21403      * Returns True if the specified record id is selected.
21404      * @param {String} id The id of record to check
21405      * @return {Boolean}
21406      */
21407     isIdSelected : function(id){
21408         return (this.selections.key(id) ? true : false);
21409     },
21410
21411     // private
21412     handleMouseDown : function(e, t){
21413         var view = this.grid.getView(), rowIndex;
21414         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21415             return;
21416         };
21417         if(e.shiftKey && this.last !== false){
21418             var last = this.last;
21419             this.selectRange(last, rowIndex, e.ctrlKey);
21420             this.last = last; // reset the last
21421             view.focusRow(rowIndex);
21422         }else{
21423             var isSelected = this.isSelected(rowIndex);
21424             if(e.button !== 0 && isSelected){
21425                 view.focusRow(rowIndex);
21426             }else if(e.ctrlKey && isSelected){
21427                 this.deselectRow(rowIndex);
21428             }else if(!isSelected){
21429                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21430                 view.focusRow(rowIndex);
21431             }
21432         }
21433         this.fireEvent("afterselectionchange", this);
21434     },
21435     // private
21436     handleDragableRowClick :  function(grid, rowIndex, e) 
21437     {
21438         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21439             this.selectRow(rowIndex, false);
21440             grid.view.focusRow(rowIndex);
21441              this.fireEvent("afterselectionchange", this);
21442         }
21443     },
21444     
21445     /**
21446      * Selects multiple rows.
21447      * @param {Array} rows Array of the indexes of the row to select
21448      * @param {Boolean} keepExisting (optional) True to keep existing selections
21449      */
21450     selectRows : function(rows, keepExisting){
21451         if(!keepExisting){
21452             this.clearSelections();
21453         }
21454         for(var i = 0, len = rows.length; i < len; i++){
21455             this.selectRow(rows[i], true);
21456         }
21457     },
21458
21459     /**
21460      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21461      * @param {Number} startRow The index of the first row in the range
21462      * @param {Number} endRow The index of the last row in the range
21463      * @param {Boolean} keepExisting (optional) True to retain existing selections
21464      */
21465     selectRange : function(startRow, endRow, keepExisting){
21466         if(this.locked) return;
21467         if(!keepExisting){
21468             this.clearSelections();
21469         }
21470         if(startRow <= endRow){
21471             for(var i = startRow; i <= endRow; i++){
21472                 this.selectRow(i, true);
21473             }
21474         }else{
21475             for(var i = startRow; i >= endRow; i--){
21476                 this.selectRow(i, true);
21477             }
21478         }
21479     },
21480
21481     /**
21482      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21483      * @param {Number} startRow The index of the first row in the range
21484      * @param {Number} endRow The index of the last row in the range
21485      */
21486     deselectRange : function(startRow, endRow, preventViewNotify){
21487         if(this.locked) return;
21488         for(var i = startRow; i <= endRow; i++){
21489             this.deselectRow(i, preventViewNotify);
21490         }
21491     },
21492
21493     /**
21494      * Selects a row.
21495      * @param {Number} row The index of the row to select
21496      * @param {Boolean} keepExisting (optional) True to keep existing selections
21497      */
21498     selectRow : function(index, keepExisting, preventViewNotify){
21499         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21500         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21501             if(!keepExisting || this.singleSelect){
21502                 this.clearSelections();
21503             }
21504             var r = this.grid.dataSource.getAt(index);
21505             this.selections.add(r);
21506             this.last = this.lastActive = index;
21507             if(!preventViewNotify){
21508                 this.grid.getView().onRowSelect(index);
21509             }
21510             this.fireEvent("rowselect", this, index, r);
21511             this.fireEvent("selectionchange", this);
21512         }
21513     },
21514
21515     /**
21516      * Deselects a row.
21517      * @param {Number} row The index of the row to deselect
21518      */
21519     deselectRow : function(index, preventViewNotify){
21520         if(this.locked) return;
21521         if(this.last == index){
21522             this.last = false;
21523         }
21524         if(this.lastActive == index){
21525             this.lastActive = false;
21526         }
21527         var r = this.grid.dataSource.getAt(index);
21528         this.selections.remove(r);
21529         if(!preventViewNotify){
21530             this.grid.getView().onRowDeselect(index);
21531         }
21532         this.fireEvent("rowdeselect", this, index);
21533         this.fireEvent("selectionchange", this);
21534     },
21535
21536     // private
21537     restoreLast : function(){
21538         if(this._last){
21539             this.last = this._last;
21540         }
21541     },
21542
21543     // private
21544     acceptsNav : function(row, col, cm){
21545         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21546     },
21547
21548     // private
21549     onEditorKey : function(field, e){
21550         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21551         if(k == e.TAB){
21552             e.stopEvent();
21553             ed.completeEdit();
21554             if(e.shiftKey){
21555                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21556             }else{
21557                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21558             }
21559         }else if(k == e.ENTER && !e.ctrlKey){
21560             e.stopEvent();
21561             ed.completeEdit();
21562             if(e.shiftKey){
21563                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21564             }else{
21565                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21566             }
21567         }else if(k == e.ESC){
21568             ed.cancelEdit();
21569         }
21570         if(newCell){
21571             g.startEditing(newCell[0], newCell[1]);
21572         }
21573     }
21574 });/*
21575  * Based on:
21576  * Ext JS Library 1.1.1
21577  * Copyright(c) 2006-2007, Ext JS, LLC.
21578  *
21579  * Originally Released Under LGPL - original licence link has changed is not relivant.
21580  *
21581  * Fork - LGPL
21582  * <script type="text/javascript">
21583  */
21584  
21585 /**
21586  * @class Roo.bootstrap.PagingToolbar
21587  * @extends Roo.Row
21588  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21589  * @constructor
21590  * Create a new PagingToolbar
21591  * @param {Object} config The config object
21592  */
21593 Roo.bootstrap.PagingToolbar = function(config)
21594 {
21595     // old args format still supported... - xtype is prefered..
21596         // created from xtype...
21597     var ds = config.dataSource;
21598     this.toolbarItems = [];
21599     if (config.items) {
21600         this.toolbarItems = config.items;
21601 //        config.items = [];
21602     }
21603     
21604     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21605     this.ds = ds;
21606     this.cursor = 0;
21607     if (ds) { 
21608         this.bind(ds);
21609     }
21610     
21611     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21612     
21613 };
21614
21615 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21616     /**
21617      * @cfg {Roo.data.Store} dataSource
21618      * The underlying data store providing the paged data
21619      */
21620     /**
21621      * @cfg {String/HTMLElement/Element} container
21622      * container The id or element that will contain the toolbar
21623      */
21624     /**
21625      * @cfg {Boolean} displayInfo
21626      * True to display the displayMsg (defaults to false)
21627      */
21628     /**
21629      * @cfg {Number} pageSize
21630      * The number of records to display per page (defaults to 20)
21631      */
21632     pageSize: 20,
21633     /**
21634      * @cfg {String} displayMsg
21635      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21636      */
21637     displayMsg : 'Displaying {0} - {1} of {2}',
21638     /**
21639      * @cfg {String} emptyMsg
21640      * The message to display when no records are found (defaults to "No data to display")
21641      */
21642     emptyMsg : 'No data to display',
21643     /**
21644      * Customizable piece of the default paging text (defaults to "Page")
21645      * @type String
21646      */
21647     beforePageText : "Page",
21648     /**
21649      * Customizable piece of the default paging text (defaults to "of %0")
21650      * @type String
21651      */
21652     afterPageText : "of {0}",
21653     /**
21654      * Customizable piece of the default paging text (defaults to "First Page")
21655      * @type String
21656      */
21657     firstText : "First Page",
21658     /**
21659      * Customizable piece of the default paging text (defaults to "Previous Page")
21660      * @type String
21661      */
21662     prevText : "Previous Page",
21663     /**
21664      * Customizable piece of the default paging text (defaults to "Next Page")
21665      * @type String
21666      */
21667     nextText : "Next Page",
21668     /**
21669      * Customizable piece of the default paging text (defaults to "Last Page")
21670      * @type String
21671      */
21672     lastText : "Last Page",
21673     /**
21674      * Customizable piece of the default paging text (defaults to "Refresh")
21675      * @type String
21676      */
21677     refreshText : "Refresh",
21678
21679     buttons : false,
21680     // private
21681     onRender : function(ct, position) 
21682     {
21683         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21684         this.navgroup.parentId = this.id;
21685         this.navgroup.onRender(this.el, null);
21686         // add the buttons to the navgroup
21687         
21688         if(this.displayInfo){
21689             Roo.log(this.el.select('ul.navbar-nav',true).first());
21690             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21691             this.displayEl = this.el.select('.x-paging-info', true).first();
21692 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21693 //            this.displayEl = navel.el.select('span',true).first();
21694         }
21695         
21696         var _this = this;
21697         
21698         if(this.buttons){
21699             Roo.each(_this.buttons, function(e){
21700                Roo.factory(e).onRender(_this.el, null);
21701             });
21702         }
21703             
21704         Roo.each(_this.toolbarItems, function(e) {
21705             _this.navgroup.addItem(e);
21706         });
21707         
21708         
21709         this.first = this.navgroup.addItem({
21710             tooltip: this.firstText,
21711             cls: "prev",
21712             icon : 'fa fa-backward',
21713             disabled: true,
21714             preventDefault: true,
21715             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21716         });
21717         
21718         this.prev =  this.navgroup.addItem({
21719             tooltip: this.prevText,
21720             cls: "prev",
21721             icon : 'fa fa-step-backward',
21722             disabled: true,
21723             preventDefault: true,
21724             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21725         });
21726     //this.addSeparator();
21727         
21728         
21729         var field = this.navgroup.addItem( {
21730             tagtype : 'span',
21731             cls : 'x-paging-position',
21732             
21733             html : this.beforePageText  +
21734                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21735                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21736          } ); //?? escaped?
21737         
21738         this.field = field.el.select('input', true).first();
21739         this.field.on("keydown", this.onPagingKeydown, this);
21740         this.field.on("focus", function(){this.dom.select();});
21741     
21742     
21743         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21744         //this.field.setHeight(18);
21745         //this.addSeparator();
21746         this.next = this.navgroup.addItem({
21747             tooltip: this.nextText,
21748             cls: "next",
21749             html : ' <i class="fa fa-step-forward">',
21750             disabled: true,
21751             preventDefault: true,
21752             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21753         });
21754         this.last = this.navgroup.addItem({
21755             tooltip: this.lastText,
21756             icon : 'fa fa-forward',
21757             cls: "next",
21758             disabled: true,
21759             preventDefault: true,
21760             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21761         });
21762     //this.addSeparator();
21763         this.loading = this.navgroup.addItem({
21764             tooltip: this.refreshText,
21765             icon: 'fa fa-refresh',
21766             preventDefault: true,
21767             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21768         });
21769
21770     },
21771
21772     // private
21773     updateInfo : function(){
21774         if(this.displayEl){
21775             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21776             var msg = count == 0 ?
21777                 this.emptyMsg :
21778                 String.format(
21779                     this.displayMsg,
21780                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21781                 );
21782             this.displayEl.update(msg);
21783         }
21784     },
21785
21786     // private
21787     onLoad : function(ds, r, o){
21788        this.cursor = o.params ? o.params.start : 0;
21789        var d = this.getPageData(),
21790             ap = d.activePage,
21791             ps = d.pages;
21792         
21793        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21794        this.field.dom.value = ap;
21795        this.first.setDisabled(ap == 1);
21796        this.prev.setDisabled(ap == 1);
21797        this.next.setDisabled(ap == ps);
21798        this.last.setDisabled(ap == ps);
21799        this.loading.enable();
21800        this.updateInfo();
21801     },
21802
21803     // private
21804     getPageData : function(){
21805         var total = this.ds.getTotalCount();
21806         return {
21807             total : total,
21808             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21809             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21810         };
21811     },
21812
21813     // private
21814     onLoadError : function(){
21815         this.loading.enable();
21816     },
21817
21818     // private
21819     onPagingKeydown : function(e){
21820         var k = e.getKey();
21821         var d = this.getPageData();
21822         if(k == e.RETURN){
21823             var v = this.field.dom.value, pageNum;
21824             if(!v || isNaN(pageNum = parseInt(v, 10))){
21825                 this.field.dom.value = d.activePage;
21826                 return;
21827             }
21828             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21829             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21830             e.stopEvent();
21831         }
21832         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))
21833         {
21834           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21835           this.field.dom.value = pageNum;
21836           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21837           e.stopEvent();
21838         }
21839         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21840         {
21841           var v = this.field.dom.value, pageNum; 
21842           var increment = (e.shiftKey) ? 10 : 1;
21843           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21844             increment *= -1;
21845           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21846             this.field.dom.value = d.activePage;
21847             return;
21848           }
21849           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21850           {
21851             this.field.dom.value = parseInt(v, 10) + increment;
21852             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21853             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21854           }
21855           e.stopEvent();
21856         }
21857     },
21858
21859     // private
21860     beforeLoad : function(){
21861         if(this.loading){
21862             this.loading.disable();
21863         }
21864     },
21865
21866     // private
21867     onClick : function(which){
21868         
21869         var ds = this.ds;
21870         if (!ds) {
21871             return;
21872         }
21873         
21874         switch(which){
21875             case "first":
21876                 ds.load({params:{start: 0, limit: this.pageSize}});
21877             break;
21878             case "prev":
21879                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21880             break;
21881             case "next":
21882                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21883             break;
21884             case "last":
21885                 var total = ds.getTotalCount();
21886                 var extra = total % this.pageSize;
21887                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21888                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21889             break;
21890             case "refresh":
21891                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21892             break;
21893         }
21894     },
21895
21896     /**
21897      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21898      * @param {Roo.data.Store} store The data store to unbind
21899      */
21900     unbind : function(ds){
21901         ds.un("beforeload", this.beforeLoad, this);
21902         ds.un("load", this.onLoad, this);
21903         ds.un("loadexception", this.onLoadError, this);
21904         ds.un("remove", this.updateInfo, this);
21905         ds.un("add", this.updateInfo, this);
21906         this.ds = undefined;
21907     },
21908
21909     /**
21910      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21911      * @param {Roo.data.Store} store The data store to bind
21912      */
21913     bind : function(ds){
21914         ds.on("beforeload", this.beforeLoad, this);
21915         ds.on("load", this.onLoad, this);
21916         ds.on("loadexception", this.onLoadError, this);
21917         ds.on("remove", this.updateInfo, this);
21918         ds.on("add", this.updateInfo, this);
21919         this.ds = ds;
21920     }
21921 });/*
21922  * - LGPL
21923  *
21924  * element
21925  * 
21926  */
21927
21928 /**
21929  * @class Roo.bootstrap.MessageBar
21930  * @extends Roo.bootstrap.Component
21931  * Bootstrap MessageBar class
21932  * @cfg {String} html contents of the MessageBar
21933  * @cfg {String} weight (info | success | warning | danger) default info
21934  * @cfg {String} beforeClass insert the bar before the given class
21935  * @cfg {Boolean} closable (true | false) default false
21936  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21937  * 
21938  * @constructor
21939  * Create a new Element
21940  * @param {Object} config The config object
21941  */
21942
21943 Roo.bootstrap.MessageBar = function(config){
21944     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21945 };
21946
21947 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21948     
21949     html: '',
21950     weight: 'info',
21951     closable: false,
21952     fixed: false,
21953     beforeClass: 'bootstrap-sticky-wrap',
21954     
21955     getAutoCreate : function(){
21956         
21957         var cfg = {
21958             tag: 'div',
21959             cls: 'alert alert-dismissable alert-' + this.weight,
21960             cn: [
21961                 {
21962                     tag: 'span',
21963                     cls: 'message',
21964                     html: this.html || ''
21965                 }
21966             ]
21967         }
21968         
21969         if(this.fixed){
21970             cfg.cls += ' alert-messages-fixed';
21971         }
21972         
21973         if(this.closable){
21974             cfg.cn.push({
21975                 tag: 'button',
21976                 cls: 'close',
21977                 html: 'x'
21978             });
21979         }
21980         
21981         return cfg;
21982     },
21983     
21984     onRender : function(ct, position)
21985     {
21986         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21987         
21988         if(!this.el){
21989             var cfg = Roo.apply({},  this.getAutoCreate());
21990             cfg.id = Roo.id();
21991             
21992             if (this.cls) {
21993                 cfg.cls += ' ' + this.cls;
21994             }
21995             if (this.style) {
21996                 cfg.style = this.style;
21997             }
21998             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21999             
22000             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22001         }
22002         
22003         this.el.select('>button.close').on('click', this.hide, this);
22004         
22005     },
22006     
22007     show : function()
22008     {
22009         if (!this.rendered) {
22010             this.render();
22011         }
22012         
22013         this.el.show();
22014         
22015         this.fireEvent('show', this);
22016         
22017     },
22018     
22019     hide : function()
22020     {
22021         if (!this.rendered) {
22022             this.render();
22023         }
22024         
22025         this.el.hide();
22026         
22027         this.fireEvent('hide', this);
22028     },
22029     
22030     update : function()
22031     {
22032 //        var e = this.el.dom.firstChild;
22033 //        
22034 //        if(this.closable){
22035 //            e = e.nextSibling;
22036 //        }
22037 //        
22038 //        e.data = this.html || '';
22039
22040         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22041     }
22042    
22043 });
22044
22045  
22046
22047      /*
22048  * - LGPL
22049  *
22050  * Graph
22051  * 
22052  */
22053
22054
22055 /**
22056  * @class Roo.bootstrap.Graph
22057  * @extends Roo.bootstrap.Component
22058  * Bootstrap Graph class
22059 > Prameters
22060  -sm {number} sm 4
22061  -md {number} md 5
22062  @cfg {String} graphtype  bar | vbar | pie
22063  @cfg {number} g_x coodinator | centre x (pie)
22064  @cfg {number} g_y coodinator | centre y (pie)
22065  @cfg {number} g_r radius (pie)
22066  @cfg {number} g_height height of the chart (respected by all elements in the set)
22067  @cfg {number} g_width width of the chart (respected by all elements in the set)
22068  @cfg {Object} title The title of the chart
22069     
22070  -{Array}  values
22071  -opts (object) options for the chart 
22072      o {
22073      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22074      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22075      o vgutter (number)
22076      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.
22077      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22078      o to
22079      o stretch (boolean)
22080      o }
22081  -opts (object) options for the pie
22082      o{
22083      o cut
22084      o startAngle (number)
22085      o endAngle (number)
22086      } 
22087  *
22088  * @constructor
22089  * Create a new Input
22090  * @param {Object} config The config object
22091  */
22092
22093 Roo.bootstrap.Graph = function(config){
22094     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22095     
22096     this.addEvents({
22097         // img events
22098         /**
22099          * @event click
22100          * The img click event for the img.
22101          * @param {Roo.EventObject} e
22102          */
22103         "click" : true
22104     });
22105 };
22106
22107 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22108     
22109     sm: 4,
22110     md: 5,
22111     graphtype: 'bar',
22112     g_height: 250,
22113     g_width: 400,
22114     g_x: 50,
22115     g_y: 50,
22116     g_r: 30,
22117     opts:{
22118         //g_colors: this.colors,
22119         g_type: 'soft',
22120         g_gutter: '20%'
22121
22122     },
22123     title : false,
22124
22125     getAutoCreate : function(){
22126         
22127         var cfg = {
22128             tag: 'div',
22129             html : null
22130         }
22131         
22132         
22133         return  cfg;
22134     },
22135
22136     onRender : function(ct,position){
22137         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22138         this.raphael = Raphael(this.el.dom);
22139         
22140                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22141                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22142                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22143                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22144                 /*
22145                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22146                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22147                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22148                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22149                 
22150                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22151                 r.barchart(330, 10, 300, 220, data1);
22152                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22153                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22154                 */
22155                 
22156                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22157                 // r.barchart(30, 30, 560, 250,  xdata, {
22158                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22159                 //     axis : "0 0 1 1",
22160                 //     axisxlabels :  xdata
22161                 //     //yvalues : cols,
22162                    
22163                 // });
22164 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22165 //        
22166 //        this.load(null,xdata,{
22167 //                axis : "0 0 1 1",
22168 //                axisxlabels :  xdata
22169 //                });
22170
22171     },
22172
22173     load : function(graphtype,xdata,opts){
22174         this.raphael.clear();
22175         if(!graphtype) {
22176             graphtype = this.graphtype;
22177         }
22178         if(!opts){
22179             opts = this.opts;
22180         }
22181         var r = this.raphael,
22182             fin = function () {
22183                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22184             },
22185             fout = function () {
22186                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22187             },
22188             pfin = function() {
22189                 this.sector.stop();
22190                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22191
22192                 if (this.label) {
22193                     this.label[0].stop();
22194                     this.label[0].attr({ r: 7.5 });
22195                     this.label[1].attr({ "font-weight": 800 });
22196                 }
22197             },
22198             pfout = function() {
22199                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22200
22201                 if (this.label) {
22202                     this.label[0].animate({ r: 5 }, 500, "bounce");
22203                     this.label[1].attr({ "font-weight": 400 });
22204                 }
22205             };
22206
22207         switch(graphtype){
22208             case 'bar':
22209                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22210                 break;
22211             case 'hbar':
22212                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22213                 break;
22214             case 'pie':
22215 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22216 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22217 //            
22218                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22219                 
22220                 break;
22221
22222         }
22223         
22224         if(this.title){
22225             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22226         }
22227         
22228     },
22229     
22230     setTitle: function(o)
22231     {
22232         this.title = o;
22233     },
22234     
22235     initEvents: function() {
22236         
22237         if(!this.href){
22238             this.el.on('click', this.onClick, this);
22239         }
22240     },
22241     
22242     onClick : function(e)
22243     {
22244         Roo.log('img onclick');
22245         this.fireEvent('click', this, e);
22246     }
22247    
22248 });
22249
22250  
22251 /*
22252  * - LGPL
22253  *
22254  * numberBox
22255  * 
22256  */
22257 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22258
22259 /**
22260  * @class Roo.bootstrap.dash.NumberBox
22261  * @extends Roo.bootstrap.Component
22262  * Bootstrap NumberBox class
22263  * @cfg {String} headline Box headline
22264  * @cfg {String} content Box content
22265  * @cfg {String} icon Box icon
22266  * @cfg {String} footer Footer text
22267  * @cfg {String} fhref Footer href
22268  * 
22269  * @constructor
22270  * Create a new NumberBox
22271  * @param {Object} config The config object
22272  */
22273
22274
22275 Roo.bootstrap.dash.NumberBox = function(config){
22276     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22277     
22278 };
22279
22280 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22281     
22282     headline : '',
22283     content : '',
22284     icon : '',
22285     footer : '',
22286     fhref : '',
22287     ficon : '',
22288     
22289     getAutoCreate : function(){
22290         
22291         var cfg = {
22292             tag : 'div',
22293             cls : 'small-box ',
22294             cn : [
22295                 {
22296                     tag : 'div',
22297                     cls : 'inner',
22298                     cn :[
22299                         {
22300                             tag : 'h3',
22301                             cls : 'roo-headline',
22302                             html : this.headline
22303                         },
22304                         {
22305                             tag : 'p',
22306                             cls : 'roo-content',
22307                             html : this.content
22308                         }
22309                     ]
22310                 }
22311             ]
22312         }
22313         
22314         if(this.icon){
22315             cfg.cn.push({
22316                 tag : 'div',
22317                 cls : 'icon',
22318                 cn :[
22319                     {
22320                         tag : 'i',
22321                         cls : 'ion ' + this.icon
22322                     }
22323                 ]
22324             });
22325         }
22326         
22327         if(this.footer){
22328             var footer = {
22329                 tag : 'a',
22330                 cls : 'small-box-footer',
22331                 href : this.fhref || '#',
22332                 html : this.footer
22333             };
22334             
22335             cfg.cn.push(footer);
22336             
22337         }
22338         
22339         return  cfg;
22340     },
22341
22342     onRender : function(ct,position){
22343         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22344
22345
22346        
22347                 
22348     },
22349
22350     setHeadline: function (value)
22351     {
22352         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22353     },
22354     
22355     setFooter: function (value, href)
22356     {
22357         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22358         
22359         if(href){
22360             this.el.select('a.small-box-footer',true).first().attr('href', href);
22361         }
22362         
22363     },
22364
22365     setContent: function (value)
22366     {
22367         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22368     },
22369
22370     initEvents: function() 
22371     {   
22372         
22373     }
22374     
22375 });
22376
22377  
22378 /*
22379  * - LGPL
22380  *
22381  * TabBox
22382  * 
22383  */
22384 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22385
22386 /**
22387  * @class Roo.bootstrap.dash.TabBox
22388  * @extends Roo.bootstrap.Component
22389  * Bootstrap TabBox class
22390  * @cfg {String} title Title of the TabBox
22391  * @cfg {String} icon Icon of the TabBox
22392  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22393  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22394  * 
22395  * @constructor
22396  * Create a new TabBox
22397  * @param {Object} config The config object
22398  */
22399
22400
22401 Roo.bootstrap.dash.TabBox = function(config){
22402     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22403     this.addEvents({
22404         // raw events
22405         /**
22406          * @event addpane
22407          * When a pane is added
22408          * @param {Roo.bootstrap.dash.TabPane} pane
22409          */
22410         "addpane" : true,
22411         /**
22412          * @event activatepane
22413          * When a pane is activated
22414          * @param {Roo.bootstrap.dash.TabPane} pane
22415          */
22416         "activatepane" : true
22417         
22418          
22419     });
22420     
22421     this.panes = [];
22422 };
22423
22424 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22425
22426     title : '',
22427     icon : false,
22428     showtabs : true,
22429     tabScrollable : false,
22430     
22431     getChildContainer : function()
22432     {
22433         return this.el.select('.tab-content', true).first();
22434     },
22435     
22436     getAutoCreate : function(){
22437         
22438         var header = {
22439             tag: 'li',
22440             cls: 'pull-left header',
22441             html: this.title,
22442             cn : []
22443         };
22444         
22445         if(this.icon){
22446             header.cn.push({
22447                 tag: 'i',
22448                 cls: 'fa ' + this.icon
22449             });
22450         }
22451         
22452         var h = {
22453             tag: 'ul',
22454             cls: 'nav nav-tabs pull-right',
22455             cn: [
22456                 header
22457             ]
22458         };
22459         
22460         if(this.tabScrollable){
22461             h = {
22462                 tag: 'div',
22463                 cls: 'tab-header',
22464                 cn: [
22465                     {
22466                         tag: 'ul',
22467                         cls: 'nav nav-tabs pull-right',
22468                         cn: [
22469                             header
22470                         ]
22471                     }
22472                 ]
22473             }
22474         }
22475         
22476         var cfg = {
22477             tag: 'div',
22478             cls: 'nav-tabs-custom',
22479             cn: [
22480                 h,
22481                 {
22482                     tag: 'div',
22483                     cls: 'tab-content no-padding',
22484                     cn: []
22485                 }
22486             ]
22487         }
22488
22489         return  cfg;
22490     },
22491     initEvents : function()
22492     {
22493         //Roo.log('add add pane handler');
22494         this.on('addpane', this.onAddPane, this);
22495     },
22496      /**
22497      * Updates the box title
22498      * @param {String} html to set the title to.
22499      */
22500     setTitle : function(value)
22501     {
22502         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22503     },
22504     onAddPane : function(pane)
22505     {
22506         this.panes.push(pane);
22507         //Roo.log('addpane');
22508         //Roo.log(pane);
22509         // tabs are rendere left to right..
22510         if(!this.showtabs){
22511             return;
22512         }
22513         
22514         var ctr = this.el.select('.nav-tabs', true).first();
22515          
22516          
22517         var existing = ctr.select('.nav-tab',true);
22518         var qty = existing.getCount();;
22519         
22520         
22521         var tab = ctr.createChild({
22522             tag : 'li',
22523             cls : 'nav-tab' + (qty ? '' : ' active'),
22524             cn : [
22525                 {
22526                     tag : 'a',
22527                     href:'#',
22528                     html : pane.title
22529                 }
22530             ]
22531         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22532         pane.tab = tab;
22533         
22534         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22535         if (!qty) {
22536             pane.el.addClass('active');
22537         }
22538         
22539                 
22540     },
22541     onTabClick : function(ev,un,ob,pane)
22542     {
22543         //Roo.log('tab - prev default');
22544         ev.preventDefault();
22545         
22546         
22547         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22548         pane.tab.addClass('active');
22549         //Roo.log(pane.title);
22550         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22551         // technically we should have a deactivate event.. but maybe add later.
22552         // and it should not de-activate the selected tab...
22553         this.fireEvent('activatepane', pane);
22554         pane.el.addClass('active');
22555         pane.fireEvent('activate');
22556         
22557         
22558     },
22559     
22560     getActivePane : function()
22561     {
22562         var r = false;
22563         Roo.each(this.panes, function(p) {
22564             if(p.el.hasClass('active')){
22565                 r = p;
22566                 return false;
22567             }
22568             
22569             return;
22570         });
22571         
22572         return r;
22573     }
22574     
22575     
22576 });
22577
22578  
22579 /*
22580  * - LGPL
22581  *
22582  * Tab pane
22583  * 
22584  */
22585 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22586 /**
22587  * @class Roo.bootstrap.TabPane
22588  * @extends Roo.bootstrap.Component
22589  * Bootstrap TabPane class
22590  * @cfg {Boolean} active (false | true) Default false
22591  * @cfg {String} title title of panel
22592
22593  * 
22594  * @constructor
22595  * Create a new TabPane
22596  * @param {Object} config The config object
22597  */
22598
22599 Roo.bootstrap.dash.TabPane = function(config){
22600     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22601     
22602     this.addEvents({
22603         // raw events
22604         /**
22605          * @event activate
22606          * When a pane is activated
22607          * @param {Roo.bootstrap.dash.TabPane} pane
22608          */
22609         "activate" : true
22610          
22611     });
22612 };
22613
22614 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22615     
22616     active : false,
22617     title : '',
22618     
22619     // the tabBox that this is attached to.
22620     tab : false,
22621      
22622     getAutoCreate : function() 
22623     {
22624         var cfg = {
22625             tag: 'div',
22626             cls: 'tab-pane'
22627         }
22628         
22629         if(this.active){
22630             cfg.cls += ' active';
22631         }
22632         
22633         return cfg;
22634     },
22635     initEvents  : function()
22636     {
22637         //Roo.log('trigger add pane handler');
22638         this.parent().fireEvent('addpane', this)
22639     },
22640     
22641      /**
22642      * Updates the tab title 
22643      * @param {String} html to set the title to.
22644      */
22645     setTitle: function(str)
22646     {
22647         if (!this.tab) {
22648             return;
22649         }
22650         this.title = str;
22651         this.tab.select('a', true).first().dom.innerHTML = str;
22652         
22653     }
22654     
22655     
22656     
22657 });
22658
22659  
22660
22661
22662  /*
22663  * - LGPL
22664  *
22665  * menu
22666  * 
22667  */
22668 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22669
22670 /**
22671  * @class Roo.bootstrap.menu.Menu
22672  * @extends Roo.bootstrap.Component
22673  * Bootstrap Menu class - container for Menu
22674  * @cfg {String} html Text of the menu
22675  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22676  * @cfg {String} icon Font awesome icon
22677  * @cfg {String} pos Menu align to (top | bottom) default bottom
22678  * 
22679  * 
22680  * @constructor
22681  * Create a new Menu
22682  * @param {Object} config The config object
22683  */
22684
22685
22686 Roo.bootstrap.menu.Menu = function(config){
22687     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22688     
22689     this.addEvents({
22690         /**
22691          * @event beforeshow
22692          * Fires before this menu is displayed
22693          * @param {Roo.bootstrap.menu.Menu} this
22694          */
22695         beforeshow : true,
22696         /**
22697          * @event beforehide
22698          * Fires before this menu is hidden
22699          * @param {Roo.bootstrap.menu.Menu} this
22700          */
22701         beforehide : true,
22702         /**
22703          * @event show
22704          * Fires after this menu is displayed
22705          * @param {Roo.bootstrap.menu.Menu} this
22706          */
22707         show : true,
22708         /**
22709          * @event hide
22710          * Fires after this menu is hidden
22711          * @param {Roo.bootstrap.menu.Menu} this
22712          */
22713         hide : true,
22714         /**
22715          * @event click
22716          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22717          * @param {Roo.bootstrap.menu.Menu} this
22718          * @param {Roo.EventObject} e
22719          */
22720         click : true
22721     });
22722     
22723 };
22724
22725 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22726     
22727     submenu : false,
22728     html : '',
22729     weight : 'default',
22730     icon : false,
22731     pos : 'bottom',
22732     
22733     
22734     getChildContainer : function() {
22735         if(this.isSubMenu){
22736             return this.el;
22737         }
22738         
22739         return this.el.select('ul.dropdown-menu', true).first();  
22740     },
22741     
22742     getAutoCreate : function()
22743     {
22744         var text = [
22745             {
22746                 tag : 'span',
22747                 cls : 'roo-menu-text',
22748                 html : this.html
22749             }
22750         ];
22751         
22752         if(this.icon){
22753             text.unshift({
22754                 tag : 'i',
22755                 cls : 'fa ' + this.icon
22756             })
22757         }
22758         
22759         
22760         var cfg = {
22761             tag : 'div',
22762             cls : 'btn-group',
22763             cn : [
22764                 {
22765                     tag : 'button',
22766                     cls : 'dropdown-button btn btn-' + this.weight,
22767                     cn : text
22768                 },
22769                 {
22770                     tag : 'button',
22771                     cls : 'dropdown-toggle btn btn-' + this.weight,
22772                     cn : [
22773                         {
22774                             tag : 'span',
22775                             cls : 'caret'
22776                         }
22777                     ]
22778                 },
22779                 {
22780                     tag : 'ul',
22781                     cls : 'dropdown-menu'
22782                 }
22783             ]
22784             
22785         };
22786         
22787         if(this.pos == 'top'){
22788             cfg.cls += ' dropup';
22789         }
22790         
22791         if(this.isSubMenu){
22792             cfg = {
22793                 tag : 'ul',
22794                 cls : 'dropdown-menu'
22795             }
22796         }
22797         
22798         return cfg;
22799     },
22800     
22801     onRender : function(ct, position)
22802     {
22803         this.isSubMenu = ct.hasClass('dropdown-submenu');
22804         
22805         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22806     },
22807     
22808     initEvents : function() 
22809     {
22810         if(this.isSubMenu){
22811             return;
22812         }
22813         
22814         this.hidden = true;
22815         
22816         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22817         this.triggerEl.on('click', this.onTriggerPress, this);
22818         
22819         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22820         this.buttonEl.on('click', this.onClick, this);
22821         
22822     },
22823     
22824     list : function()
22825     {
22826         if(this.isSubMenu){
22827             return this.el;
22828         }
22829         
22830         return this.el.select('ul.dropdown-menu', true).first();
22831     },
22832     
22833     onClick : function(e)
22834     {
22835         this.fireEvent("click", this, e);
22836     },
22837     
22838     onTriggerPress  : function(e)
22839     {   
22840         if (this.isVisible()) {
22841             this.hide();
22842         } else {
22843             this.show();
22844         }
22845     },
22846     
22847     isVisible : function(){
22848         return !this.hidden;
22849     },
22850     
22851     show : function()
22852     {
22853         this.fireEvent("beforeshow", this);
22854         
22855         this.hidden = false;
22856         this.el.addClass('open');
22857         
22858         Roo.get(document).on("mouseup", this.onMouseUp, this);
22859         
22860         this.fireEvent("show", this);
22861         
22862         
22863     },
22864     
22865     hide : function()
22866     {
22867         this.fireEvent("beforehide", this);
22868         
22869         this.hidden = true;
22870         this.el.removeClass('open');
22871         
22872         Roo.get(document).un("mouseup", this.onMouseUp);
22873         
22874         this.fireEvent("hide", this);
22875     },
22876     
22877     onMouseUp : function()
22878     {
22879         this.hide();
22880     }
22881     
22882 });
22883
22884  
22885  /*
22886  * - LGPL
22887  *
22888  * menu item
22889  * 
22890  */
22891 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22892
22893 /**
22894  * @class Roo.bootstrap.menu.Item
22895  * @extends Roo.bootstrap.Component
22896  * Bootstrap MenuItem class
22897  * @cfg {Boolean} submenu (true | false) default false
22898  * @cfg {String} html text of the item
22899  * @cfg {String} href the link
22900  * @cfg {Boolean} disable (true | false) default false
22901  * @cfg {Boolean} preventDefault (true | false) default true
22902  * @cfg {String} icon Font awesome icon
22903  * @cfg {String} pos Submenu align to (left | right) default right 
22904  * 
22905  * 
22906  * @constructor
22907  * Create a new Item
22908  * @param {Object} config The config object
22909  */
22910
22911
22912 Roo.bootstrap.menu.Item = function(config){
22913     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22914     this.addEvents({
22915         /**
22916          * @event mouseover
22917          * Fires when the mouse is hovering over this menu
22918          * @param {Roo.bootstrap.menu.Item} this
22919          * @param {Roo.EventObject} e
22920          */
22921         mouseover : true,
22922         /**
22923          * @event mouseout
22924          * Fires when the mouse exits this menu
22925          * @param {Roo.bootstrap.menu.Item} this
22926          * @param {Roo.EventObject} e
22927          */
22928         mouseout : true,
22929         // raw events
22930         /**
22931          * @event click
22932          * The raw click event for the entire grid.
22933          * @param {Roo.EventObject} e
22934          */
22935         click : true
22936     });
22937 };
22938
22939 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22940     
22941     submenu : false,
22942     href : '',
22943     html : '',
22944     preventDefault: true,
22945     disable : false,
22946     icon : false,
22947     pos : 'right',
22948     
22949     getAutoCreate : function()
22950     {
22951         var text = [
22952             {
22953                 tag : 'span',
22954                 cls : 'roo-menu-item-text',
22955                 html : this.html
22956             }
22957         ];
22958         
22959         if(this.icon){
22960             text.unshift({
22961                 tag : 'i',
22962                 cls : 'fa ' + this.icon
22963             })
22964         }
22965         
22966         var cfg = {
22967             tag : 'li',
22968             cn : [
22969                 {
22970                     tag : 'a',
22971                     href : this.href || '#',
22972                     cn : text
22973                 }
22974             ]
22975         };
22976         
22977         if(this.disable){
22978             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22979         }
22980         
22981         if(this.submenu){
22982             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22983             
22984             if(this.pos == 'left'){
22985                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22986             }
22987         }
22988         
22989         return cfg;
22990     },
22991     
22992     initEvents : function() 
22993     {
22994         this.el.on('mouseover', this.onMouseOver, this);
22995         this.el.on('mouseout', this.onMouseOut, this);
22996         
22997         this.el.select('a', true).first().on('click', this.onClick, this);
22998         
22999     },
23000     
23001     onClick : function(e)
23002     {
23003         if(this.preventDefault){
23004             e.preventDefault();
23005         }
23006         
23007         this.fireEvent("click", this, e);
23008     },
23009     
23010     onMouseOver : function(e)
23011     {
23012         if(this.submenu && this.pos == 'left'){
23013             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23014         }
23015         
23016         this.fireEvent("mouseover", this, e);
23017     },
23018     
23019     onMouseOut : function(e)
23020     {
23021         this.fireEvent("mouseout", this, e);
23022     }
23023 });
23024
23025  
23026
23027  /*
23028  * - LGPL
23029  *
23030  * menu separator
23031  * 
23032  */
23033 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23034
23035 /**
23036  * @class Roo.bootstrap.menu.Separator
23037  * @extends Roo.bootstrap.Component
23038  * Bootstrap Separator class
23039  * 
23040  * @constructor
23041  * Create a new Separator
23042  * @param {Object} config The config object
23043  */
23044
23045
23046 Roo.bootstrap.menu.Separator = function(config){
23047     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23048 };
23049
23050 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23051     
23052     getAutoCreate : function(){
23053         var cfg = {
23054             tag : 'li',
23055             cls: 'divider'
23056         };
23057         
23058         return cfg;
23059     }
23060    
23061 });
23062
23063  
23064
23065  /*
23066  * - LGPL
23067  *
23068  * Tooltip
23069  * 
23070  */
23071
23072 /**
23073  * @class Roo.bootstrap.Tooltip
23074  * Bootstrap Tooltip class
23075  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23076  * to determine which dom element triggers the tooltip.
23077  * 
23078  * It needs to add support for additional attributes like tooltip-position
23079  * 
23080  * @constructor
23081  * Create a new Toolti
23082  * @param {Object} config The config object
23083  */
23084
23085 Roo.bootstrap.Tooltip = function(config){
23086     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23087 };
23088
23089 Roo.apply(Roo.bootstrap.Tooltip, {
23090     /**
23091      * @function init initialize tooltip monitoring.
23092      * @static
23093      */
23094     currentEl : false,
23095     currentTip : false,
23096     currentRegion : false,
23097     
23098     //  init : delay?
23099     
23100     init : function()
23101     {
23102         Roo.get(document).on('mouseover', this.enter ,this);
23103         Roo.get(document).on('mouseout', this.leave, this);
23104          
23105         
23106         this.currentTip = new Roo.bootstrap.Tooltip();
23107     },
23108     
23109     enter : function(ev)
23110     {
23111         var dom = ev.getTarget();
23112         
23113         //Roo.log(['enter',dom]);
23114         var el = Roo.fly(dom);
23115         if (this.currentEl) {
23116             //Roo.log(dom);
23117             //Roo.log(this.currentEl);
23118             //Roo.log(this.currentEl.contains(dom));
23119             if (this.currentEl == el) {
23120                 return;
23121             }
23122             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23123                 return;
23124             }
23125
23126         }
23127         
23128         
23129         
23130         if (this.currentTip.el) {
23131             this.currentTip.el.hide(); // force hiding...
23132         }    
23133         //Roo.log(ev);
23134         var bindEl = el;
23135         
23136         // you can not look for children, as if el is the body.. then everythign is the child..
23137         if (!el.attr('tooltip')) { //
23138             if (!el.select("[tooltip]").elements.length) {
23139                 return;
23140             }
23141             // is the mouse over this child...?
23142             bindEl = el.select("[tooltip]").first();
23143             var xy = ev.getXY();
23144             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23145                 //Roo.log("not in region.");
23146                 return;
23147             }
23148             //Roo.log("child element over..");
23149             
23150         }
23151         this.currentEl = bindEl;
23152         this.currentTip.bind(bindEl);
23153         this.currentRegion = Roo.lib.Region.getRegion(dom);
23154         this.currentTip.enter();
23155         
23156     },
23157     leave : function(ev)
23158     {
23159         var dom = ev.getTarget();
23160         //Roo.log(['leave',dom]);
23161         if (!this.currentEl) {
23162             return;
23163         }
23164         
23165         
23166         if (dom != this.currentEl.dom) {
23167             return;
23168         }
23169         var xy = ev.getXY();
23170         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23171             return;
23172         }
23173         // only activate leave if mouse cursor is outside... bounding box..
23174         
23175         
23176         
23177         
23178         if (this.currentTip) {
23179             this.currentTip.leave();
23180         }
23181         //Roo.log('clear currentEl');
23182         this.currentEl = false;
23183         
23184         
23185     },
23186     alignment : {
23187         'left' : ['r-l', [-2,0], 'right'],
23188         'right' : ['l-r', [2,0], 'left'],
23189         'bottom' : ['t-b', [0,2], 'top'],
23190         'top' : [ 'b-t', [0,-2], 'bottom']
23191     }
23192     
23193 });
23194
23195
23196 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23197     
23198     
23199     bindEl : false,
23200     
23201     delay : null, // can be { show : 300 , hide: 500}
23202     
23203     timeout : null,
23204     
23205     hoverState : null, //???
23206     
23207     placement : 'bottom', 
23208     
23209     getAutoCreate : function(){
23210     
23211         var cfg = {
23212            cls : 'tooltip',
23213            role : 'tooltip',
23214            cn : [
23215                 {
23216                     cls : 'tooltip-arrow'
23217                 },
23218                 {
23219                     cls : 'tooltip-inner'
23220                 }
23221            ]
23222         };
23223         
23224         return cfg;
23225     },
23226     bind : function(el)
23227     {
23228         this.bindEl = el;
23229     },
23230       
23231     
23232     enter : function () {
23233        
23234         if (this.timeout != null) {
23235             clearTimeout(this.timeout);
23236         }
23237         
23238         this.hoverState = 'in';
23239          //Roo.log("enter - show");
23240         if (!this.delay || !this.delay.show) {
23241             this.show();
23242             return;
23243         }
23244         var _t = this;
23245         this.timeout = setTimeout(function () {
23246             if (_t.hoverState == 'in') {
23247                 _t.show();
23248             }
23249         }, this.delay.show);
23250     },
23251     leave : function()
23252     {
23253         clearTimeout(this.timeout);
23254     
23255         this.hoverState = 'out';
23256          if (!this.delay || !this.delay.hide) {
23257             this.hide();
23258             return;
23259         }
23260        
23261         var _t = this;
23262         this.timeout = setTimeout(function () {
23263             //Roo.log("leave - timeout");
23264             
23265             if (_t.hoverState == 'out') {
23266                 _t.hide();
23267                 Roo.bootstrap.Tooltip.currentEl = false;
23268             }
23269         }, delay);
23270     },
23271     
23272     show : function ()
23273     {
23274         if (!this.el) {
23275             this.render(document.body);
23276         }
23277         // set content.
23278         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23279         
23280         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23281         
23282         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23283         
23284         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23285         
23286         var placement = typeof this.placement == 'function' ?
23287             this.placement.call(this, this.el, on_el) :
23288             this.placement;
23289             
23290         var autoToken = /\s?auto?\s?/i;
23291         var autoPlace = autoToken.test(placement);
23292         if (autoPlace) {
23293             placement = placement.replace(autoToken, '') || 'top';
23294         }
23295         
23296         //this.el.detach()
23297         //this.el.setXY([0,0]);
23298         this.el.show();
23299         //this.el.dom.style.display='block';
23300         this.el.addClass(placement);
23301         
23302         //this.el.appendTo(on_el);
23303         
23304         var p = this.getPosition();
23305         var box = this.el.getBox();
23306         
23307         if (autoPlace) {
23308             // fixme..
23309         }
23310         var align = Roo.bootstrap.Tooltip.alignment[placement];
23311         this.el.alignTo(this.bindEl, align[0],align[1]);
23312         //var arrow = this.el.select('.arrow',true).first();
23313         //arrow.set(align[2], 
23314         
23315         this.el.addClass('in fade');
23316         this.hoverState = null;
23317         
23318         if (this.el.hasClass('fade')) {
23319             // fade it?
23320         }
23321         
23322     },
23323     hide : function()
23324     {
23325          
23326         if (!this.el) {
23327             return;
23328         }
23329         //this.el.setXY([0,0]);
23330         this.el.removeClass('in');
23331         //this.el.hide();
23332         
23333     }
23334     
23335 });
23336  
23337
23338  /*
23339  * - LGPL
23340  *
23341  * Location Picker
23342  * 
23343  */
23344
23345 /**
23346  * @class Roo.bootstrap.LocationPicker
23347  * @extends Roo.bootstrap.Component
23348  * Bootstrap LocationPicker class
23349  * @cfg {Number} latitude Position when init default 0
23350  * @cfg {Number} longitude Position when init default 0
23351  * @cfg {Number} zoom default 15
23352  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23353  * @cfg {Boolean} mapTypeControl default false
23354  * @cfg {Boolean} disableDoubleClickZoom default false
23355  * @cfg {Boolean} scrollwheel default true
23356  * @cfg {Boolean} streetViewControl default false
23357  * @cfg {Number} radius default 0
23358  * @cfg {String} locationName
23359  * @cfg {Boolean} draggable default true
23360  * @cfg {Boolean} enableAutocomplete default false
23361  * @cfg {Boolean} enableReverseGeocode default true
23362  * @cfg {String} markerTitle
23363  * 
23364  * @constructor
23365  * Create a new LocationPicker
23366  * @param {Object} config The config object
23367  */
23368
23369
23370 Roo.bootstrap.LocationPicker = function(config){
23371     
23372     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23373     
23374     this.addEvents({
23375         /**
23376          * @event initial
23377          * Fires when the picker initialized.
23378          * @param {Roo.bootstrap.LocationPicker} this
23379          * @param {Google Location} location
23380          */
23381         initial : true,
23382         /**
23383          * @event positionchanged
23384          * Fires when the picker position changed.
23385          * @param {Roo.bootstrap.LocationPicker} this
23386          * @param {Google Location} location
23387          */
23388         positionchanged : true,
23389         /**
23390          * @event resize
23391          * Fires when the map resize.
23392          * @param {Roo.bootstrap.LocationPicker} this
23393          */
23394         resize : true,
23395         /**
23396          * @event show
23397          * Fires when the map show.
23398          * @param {Roo.bootstrap.LocationPicker} this
23399          */
23400         show : true,
23401         /**
23402          * @event hide
23403          * Fires when the map hide.
23404          * @param {Roo.bootstrap.LocationPicker} this
23405          */
23406         hide : true,
23407         /**
23408          * @event mapClick
23409          * Fires when click the map.
23410          * @param {Roo.bootstrap.LocationPicker} this
23411          * @param {Map event} e
23412          */
23413         mapClick : true,
23414         /**
23415          * @event mapRightClick
23416          * Fires when right click the map.
23417          * @param {Roo.bootstrap.LocationPicker} this
23418          * @param {Map event} e
23419          */
23420         mapRightClick : true,
23421         /**
23422          * @event markerClick
23423          * Fires when click the marker.
23424          * @param {Roo.bootstrap.LocationPicker} this
23425          * @param {Map event} e
23426          */
23427         markerClick : true,
23428         /**
23429          * @event markerRightClick
23430          * Fires when right click the marker.
23431          * @param {Roo.bootstrap.LocationPicker} this
23432          * @param {Map event} e
23433          */
23434         markerRightClick : true,
23435         /**
23436          * @event OverlayViewDraw
23437          * Fires when OverlayView Draw
23438          * @param {Roo.bootstrap.LocationPicker} this
23439          */
23440         OverlayViewDraw : true,
23441         /**
23442          * @event OverlayViewOnAdd
23443          * Fires when OverlayView Draw
23444          * @param {Roo.bootstrap.LocationPicker} this
23445          */
23446         OverlayViewOnAdd : true,
23447         /**
23448          * @event OverlayViewOnRemove
23449          * Fires when OverlayView Draw
23450          * @param {Roo.bootstrap.LocationPicker} this
23451          */
23452         OverlayViewOnRemove : true,
23453         /**
23454          * @event OverlayViewShow
23455          * Fires when OverlayView Draw
23456          * @param {Roo.bootstrap.LocationPicker} this
23457          * @param {Pixel} cpx
23458          */
23459         OverlayViewShow : true,
23460         /**
23461          * @event OverlayViewHide
23462          * Fires when OverlayView Draw
23463          * @param {Roo.bootstrap.LocationPicker} this
23464          */
23465         OverlayViewHide : true
23466     });
23467         
23468 };
23469
23470 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23471     
23472     gMapContext: false,
23473     
23474     latitude: 0,
23475     longitude: 0,
23476     zoom: 15,
23477     mapTypeId: false,
23478     mapTypeControl: false,
23479     disableDoubleClickZoom: false,
23480     scrollwheel: true,
23481     streetViewControl: false,
23482     radius: 0,
23483     locationName: '',
23484     draggable: true,
23485     enableAutocomplete: false,
23486     enableReverseGeocode: true,
23487     markerTitle: '',
23488     
23489     getAutoCreate: function()
23490     {
23491
23492         var cfg = {
23493             tag: 'div',
23494             cls: 'roo-location-picker'
23495         };
23496         
23497         return cfg
23498     },
23499     
23500     initEvents: function(ct, position)
23501     {       
23502         if(!this.el.getWidth() || this.isApplied()){
23503             return;
23504         }
23505         
23506         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23507         
23508         this.initial();
23509     },
23510     
23511     initial: function()
23512     {
23513         if(!this.mapTypeId){
23514             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23515         }
23516         
23517         this.gMapContext = this.GMapContext();
23518         
23519         this.initOverlayView();
23520         
23521         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23522         
23523         var _this = this;
23524                 
23525         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23526             _this.setPosition(_this.gMapContext.marker.position);
23527         });
23528         
23529         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23530             _this.fireEvent('mapClick', this, event);
23531             
23532         });
23533
23534         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23535             _this.fireEvent('mapRightClick', this, event);
23536             
23537         });
23538         
23539         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23540             _this.fireEvent('markerClick', this, event);
23541             
23542         });
23543
23544         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23545             _this.fireEvent('markerRightClick', this, event);
23546             
23547         });
23548         
23549         this.setPosition(this.gMapContext.location);
23550         
23551         this.fireEvent('initial', this, this.gMapContext.location);
23552     },
23553     
23554     initOverlayView: function()
23555     {
23556         var _this = this;
23557         
23558         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23559             
23560             draw: function()
23561             {
23562                 _this.fireEvent('OverlayViewDraw', _this);
23563             },
23564             
23565             onAdd: function()
23566             {
23567                 _this.fireEvent('OverlayViewOnAdd', _this);
23568             },
23569             
23570             onRemove: function()
23571             {
23572                 _this.fireEvent('OverlayViewOnRemove', _this);
23573             },
23574             
23575             show: function(cpx)
23576             {
23577                 _this.fireEvent('OverlayViewShow', _this, cpx);
23578             },
23579             
23580             hide: function()
23581             {
23582                 _this.fireEvent('OverlayViewHide', _this);
23583             }
23584             
23585         });
23586     },
23587     
23588     fromLatLngToContainerPixel: function(event)
23589     {
23590         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23591     },
23592     
23593     isApplied: function() 
23594     {
23595         return this.getGmapContext() == false ? false : true;
23596     },
23597     
23598     getGmapContext: function() 
23599     {
23600         return this.gMapContext
23601     },
23602     
23603     GMapContext: function() 
23604     {
23605         var position = new google.maps.LatLng(this.latitude, this.longitude);
23606         
23607         var _map = new google.maps.Map(this.el.dom, {
23608             center: position,
23609             zoom: this.zoom,
23610             mapTypeId: this.mapTypeId,
23611             mapTypeControl: this.mapTypeControl,
23612             disableDoubleClickZoom: this.disableDoubleClickZoom,
23613             scrollwheel: this.scrollwheel,
23614             streetViewControl: this.streetViewControl,
23615             locationName: this.locationName,
23616             draggable: this.draggable,
23617             enableAutocomplete: this.enableAutocomplete,
23618             enableReverseGeocode: this.enableReverseGeocode
23619         });
23620         
23621         var _marker = new google.maps.Marker({
23622             position: position,
23623             map: _map,
23624             title: this.markerTitle,
23625             draggable: this.draggable
23626         });
23627         
23628         return {
23629             map: _map,
23630             marker: _marker,
23631             circle: null,
23632             location: position,
23633             radius: this.radius,
23634             locationName: this.locationName,
23635             addressComponents: {
23636                 formatted_address: null,
23637                 addressLine1: null,
23638                 addressLine2: null,
23639                 streetName: null,
23640                 streetNumber: null,
23641                 city: null,
23642                 district: null,
23643                 state: null,
23644                 stateOrProvince: null
23645             },
23646             settings: this,
23647             domContainer: this.el.dom,
23648             geodecoder: new google.maps.Geocoder()
23649         };
23650     },
23651     
23652     drawCircle: function(center, radius, options) 
23653     {
23654         if (this.gMapContext.circle != null) {
23655             this.gMapContext.circle.setMap(null);
23656         }
23657         if (radius > 0) {
23658             radius *= 1;
23659             options = Roo.apply({}, options, {
23660                 strokeColor: "#0000FF",
23661                 strokeOpacity: .35,
23662                 strokeWeight: 2,
23663                 fillColor: "#0000FF",
23664                 fillOpacity: .2
23665             });
23666             
23667             options.map = this.gMapContext.map;
23668             options.radius = radius;
23669             options.center = center;
23670             this.gMapContext.circle = new google.maps.Circle(options);
23671             return this.gMapContext.circle;
23672         }
23673         
23674         return null;
23675     },
23676     
23677     setPosition: function(location) 
23678     {
23679         this.gMapContext.location = location;
23680         this.gMapContext.marker.setPosition(location);
23681         this.gMapContext.map.panTo(location);
23682         this.drawCircle(location, this.gMapContext.radius, {});
23683         
23684         var _this = this;
23685         
23686         if (this.gMapContext.settings.enableReverseGeocode) {
23687             this.gMapContext.geodecoder.geocode({
23688                 latLng: this.gMapContext.location
23689             }, function(results, status) {
23690                 
23691                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23692                     _this.gMapContext.locationName = results[0].formatted_address;
23693                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23694                     
23695                     _this.fireEvent('positionchanged', this, location);
23696                 }
23697             });
23698             
23699             return;
23700         }
23701         
23702         this.fireEvent('positionchanged', this, location);
23703     },
23704     
23705     resize: function()
23706     {
23707         google.maps.event.trigger(this.gMapContext.map, "resize");
23708         
23709         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23710         
23711         this.fireEvent('resize', this);
23712     },
23713     
23714     setPositionByLatLng: function(latitude, longitude)
23715     {
23716         this.setPosition(new google.maps.LatLng(latitude, longitude));
23717     },
23718     
23719     getCurrentPosition: function() 
23720     {
23721         return {
23722             latitude: this.gMapContext.location.lat(),
23723             longitude: this.gMapContext.location.lng()
23724         };
23725     },
23726     
23727     getAddressName: function() 
23728     {
23729         return this.gMapContext.locationName;
23730     },
23731     
23732     getAddressComponents: function() 
23733     {
23734         return this.gMapContext.addressComponents;
23735     },
23736     
23737     address_component_from_google_geocode: function(address_components) 
23738     {
23739         var result = {};
23740         
23741         for (var i = 0; i < address_components.length; i++) {
23742             var component = address_components[i];
23743             if (component.types.indexOf("postal_code") >= 0) {
23744                 result.postalCode = component.short_name;
23745             } else if (component.types.indexOf("street_number") >= 0) {
23746                 result.streetNumber = component.short_name;
23747             } else if (component.types.indexOf("route") >= 0) {
23748                 result.streetName = component.short_name;
23749             } else if (component.types.indexOf("neighborhood") >= 0) {
23750                 result.city = component.short_name;
23751             } else if (component.types.indexOf("locality") >= 0) {
23752                 result.city = component.short_name;
23753             } else if (component.types.indexOf("sublocality") >= 0) {
23754                 result.district = component.short_name;
23755             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23756                 result.stateOrProvince = component.short_name;
23757             } else if (component.types.indexOf("country") >= 0) {
23758                 result.country = component.short_name;
23759             }
23760         }
23761         
23762         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23763         result.addressLine2 = "";
23764         return result;
23765     },
23766     
23767     setZoomLevel: function(zoom)
23768     {
23769         this.gMapContext.map.setZoom(zoom);
23770     },
23771     
23772     show: function()
23773     {
23774         if(!this.el){
23775             return;
23776         }
23777         
23778         this.el.show();
23779         
23780         this.resize();
23781         
23782         this.fireEvent('show', this);
23783     },
23784     
23785     hide: function()
23786     {
23787         if(!this.el){
23788             return;
23789         }
23790         
23791         this.el.hide();
23792         
23793         this.fireEvent('hide', this);
23794     }
23795     
23796 });
23797
23798 Roo.apply(Roo.bootstrap.LocationPicker, {
23799     
23800     OverlayView : function(map, options)
23801     {
23802         options = options || {};
23803         
23804         this.setMap(map);
23805     }
23806     
23807     
23808 });/*
23809  * - LGPL
23810  *
23811  * Alert
23812  * 
23813  */
23814
23815 /**
23816  * @class Roo.bootstrap.Alert
23817  * @extends Roo.bootstrap.Component
23818  * Bootstrap Alert class
23819  * @cfg {String} title The title of alert
23820  * @cfg {String} html The content of alert
23821  * @cfg {String} weight (  success | info | warning | danger )
23822  * @cfg {String} faicon font-awesomeicon
23823  * 
23824  * @constructor
23825  * Create a new alert
23826  * @param {Object} config The config object
23827  */
23828
23829
23830 Roo.bootstrap.Alert = function(config){
23831     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23832     
23833 };
23834
23835 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23836     
23837     title: '',
23838     html: '',
23839     weight: false,
23840     faicon: false,
23841     
23842     getAutoCreate : function()
23843     {
23844         
23845         var cfg = {
23846             tag : 'div',
23847             cls : 'alert',
23848             cn : [
23849                 {
23850                     tag : 'i',
23851                     cls : 'roo-alert-icon'
23852                     
23853                 },
23854                 {
23855                     tag : 'b',
23856                     cls : 'roo-alert-title',
23857                     html : this.title
23858                 },
23859                 {
23860                     tag : 'span',
23861                     cls : 'roo-alert-text',
23862                     html : this.html
23863                 }
23864             ]
23865         };
23866         
23867         if(this.faicon){
23868             cfg.cn[0].cls += ' fa ' + this.faicon;
23869         }
23870         
23871         if(this.weight){
23872             cfg.cls += ' alert-' + this.weight;
23873         }
23874         
23875         return cfg;
23876     },
23877     
23878     initEvents: function() 
23879     {
23880         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23881     },
23882     
23883     setTitle : function(str)
23884     {
23885         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23886     },
23887     
23888     setText : function(str)
23889     {
23890         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23891     },
23892     
23893     setWeight : function(weight)
23894     {
23895         if(this.weight){
23896             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23897         }
23898         
23899         this.weight = weight;
23900         
23901         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23902     },
23903     
23904     setIcon : function(icon)
23905     {
23906         if(this.faicon){
23907             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23908         }
23909         
23910         this.faicon = icon
23911         
23912         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23913     },
23914     
23915     hide: function() 
23916     {
23917         this.el.hide();   
23918     },
23919     
23920     show: function() 
23921     {  
23922         this.el.show();   
23923     }
23924     
23925 });
23926
23927  
23928 /*
23929 * Licence: LGPL
23930 */
23931
23932 /**
23933  * @class Roo.bootstrap.UploadCropbox
23934  * @extends Roo.bootstrap.Component
23935  * Bootstrap UploadCropbox class
23936  * @cfg {String} emptyText show when image has been loaded
23937  * @cfg {Number} minWidth default 300
23938  * @cfg {Number} minHeight default 300
23939  * 
23940  * @constructor
23941  * Create a new UploadCropbox
23942  * @param {Object} config The config object
23943  */
23944
23945 Roo.bootstrap.UploadCropbox = function(config){
23946     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23947     
23948     this.addEvents({
23949         /**
23950          * @event beforeSelectFile
23951          * Fire before select file
23952          * @param {Roo.bootstrap.UploadCropbox} this
23953          */
23954         "beforeSelectFile" : true,
23955         /**
23956          * @event initial
23957          * Fire after initEvent
23958          * @param {Roo.bootstrap.UploadCropbox} this
23959          */
23960         "initial" : true,
23961         /**
23962          * @event crop
23963          * Fire after initEvent
23964          * @param {Roo.bootstrap.UploadCropbox} this
23965          * @param {String} imageData
23966          */
23967         "crop" : true
23968         
23969     });
23970 };
23971
23972 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
23973     
23974     emptyText : 'Click to upload image',
23975     scale : 0,
23976     baseScale : 1,
23977     rotate : 0,
23978     dragable : false,
23979     mouseX : 0,
23980     mouseY : 0,
23981     cropImageData : false,
23982     cropType : 'image/png',
23983     minWidth : 300,
23984     minHeight : 300,
23985     
23986     getAutoCreate : function()
23987     {
23988         var cfg = {
23989             tag : 'div',
23990             cls : 'roo-upload-cropbox',
23991             cn : [
23992                 {
23993                     tag : 'div',
23994                     cls : 'roo-upload-cropbox-image-section',
23995                     cn : [
23996                         {
23997                             tag : 'div',
23998                             cls : 'roo-upload-cropbox-canvas',
23999                             cn : [
24000                                 {
24001                                     tag : 'img',
24002                                     cls : 'roo-upload-cropbox-image'
24003                                 }
24004                             ]
24005                         },
24006                         {
24007                             tag : 'div',
24008                             cls : 'roo-upload-cropbox-thumb'
24009                         }
24010                     ]
24011                 },
24012                 {
24013                     tag : 'div',
24014                     cls : 'roo-upload-cropbox-footer-section',
24015                     cn : {
24016                         tag : 'div',
24017                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24018                         cn : [
24019                             {
24020                                 tag : 'div',
24021                                 cls : 'btn-group',
24022                                 cn : [
24023                                     {
24024                                         tag : 'button',
24025                                         cls : 'btn btn-default roo-upload-cropbox-rotate-left',
24026                                         html : '<i class="fa fa-undo"></i>'
24027                                     }
24028                                 ]
24029                             },
24030                             {
24031                                 tag : 'div',
24032                                 cls : 'btn-group',
24033                                 cn : [
24034                                     {
24035                                         tag : 'button',
24036                                         cls : 'btn btn-default roo-upload-cropbox-picture',
24037                                         html : '<i class="fa fa-picture-o"></i>'
24038                                     }
24039                                 ]
24040                             },
24041                             {
24042                                 tag : 'div',
24043                                 cls : 'btn-group',
24044                                 cn : [
24045                                     {
24046                                         tag : 'button',
24047                                         cls : 'btn btn-default roo-upload-cropbox-rotate-right',
24048                                         html : '<i class="fa fa-repeat"></i>'
24049                                     }
24050                                 ]
24051                             }
24052                         ]
24053                     }
24054                 }
24055             ]
24056         };
24057         
24058         return cfg;
24059     },
24060     
24061     initEvents : function()
24062     {
24063         this.imageSection = this.el.select('.roo-upload-cropbox-image-section', true).first();
24064         this.imageSection.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24065         
24066         this.imageCanvas = this.el.select('.roo-upload-cropbox-canvas', true).first();
24067         this.imageCanvas.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24068         
24069         this.image = this.el.select('.roo-upload-cropbox-image', true).first();
24070         this.image.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24071         
24072         this.thumb = this.el.select('.roo-upload-cropbox-thumb', true).first();
24073         this.thumb.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24074         
24075         this.footerSection = this.el.select('.roo-upload-cropbox-footer-section', true).first();
24076         this.footerSection.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24077         this.footerSection.hide();
24078         
24079         this.rotateLeft = this.el.select('.roo-upload-cropbox-rotate-left', true).first();
24080         this.rotateLeft.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24081         
24082         this.pictureBtn = this.el.select('.roo-upload-cropbox-picture', true).first();
24083         this.pictureBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24084         
24085         this.rotateRight = this.el.select('.roo-upload-cropbox-rotate-right', true).first();
24086         this.rotateRight.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24087         
24088         this.calcThumbBoxSize();
24089         
24090         this.bind();
24091         
24092         this.fireEvent('initial', this);
24093     },
24094
24095     bind : function()
24096     {
24097         this.image.on('load', this.onLoadCanvasImage, this);
24098         
24099         if(!this.imageSectionHasOnClickEvent){
24100             this.imageSection.on('click', this.beforeSelectFile, this);
24101             this.imageSectionHasOnClickEvent = true;
24102         }
24103         
24104         this.imageSection.on('mousedown', this.onMouseDown, this);
24105         
24106         this.imageSection.on('mousemove', this.onMouseMove, this);
24107         
24108         var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24109         
24110         this.imageSection.on(mousewheel, this.onMouseWheel, this);
24111
24112         Roo.get(document).on('mouseup', this.onMouseUp, this);
24113         
24114         this.pictureBtn.on('click', this.beforeSelectFile, this);
24115         
24116         this.rotateLeft.on('click', this.onRotateLeft, this);
24117         
24118         this.rotateRight.on('click', this.onRotateRight, this);
24119         
24120     },
24121     
24122     unbind : function()
24123     {
24124         this.image.un('load', this.onLoadCanvasImage, this);
24125         
24126         if(this.imageSectionHasOnClickEvent){
24127             this.imageSection.un('click', this.beforeSelectFile, this);
24128             this.imageSectionHasOnClickEvent = false;
24129         }
24130         
24131         this.imageSection.un('mousedown', this.onMouseDown, this);
24132         
24133         this.imageSection.un('mousemove', this.onMouseMove, this);
24134         
24135         var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24136         
24137         this.imageSection.un(mousewheel, this.onMouseWheel, this);
24138
24139         Roo.get(document).un('mouseup', this.onMouseUp, this);
24140         
24141         this.pictureBtn.un('click', this.beforeSelectFile, this);
24142         
24143         this.rotateLeft.un('click', this.onRotateLeft, this);
24144         
24145         this.rotateRight.un('click', this.onRotateRight, this);
24146     },
24147     
24148     reset : function()
24149     {    
24150         this.scale = 0;
24151         this.baseScale = 1;
24152         this.rotate = 0;
24153         this.dragable = false;
24154         this.mouseX = 0;
24155         this.mouseY = 0;
24156         this.cropImageData = false;
24157         
24158         this.imageCanvas.dom.removeAttribute('style');
24159         this.image.dom.removeAttribute('style');
24160         this.image.attr('src', '');
24161         
24162         if(!this.imageSectionHasOnClickEvent){
24163             this.imageSection.on('click', this.beforeSelectFile, this);
24164             this.imageSectionHasOnClickEvent = true;
24165         }
24166         
24167     },
24168     
24169     beforeSelectFile : function(e)
24170     {
24171         e.preventDefault();
24172         this.fireEvent('beforeSelectFile', this);
24173     },
24174     
24175     loadCanvasImage : function(src)
24176     {   
24177         this.reset();
24178         
24179         this.image.attr('src', src);
24180     },
24181     
24182     onLoadCanvasImage : function(src)
24183     {   
24184         if(this.imageSectionHasOnClickEvent){
24185             this.imageSection.un('click', this.beforeSelectFile, this);
24186             this.imageSectionHasOnClickEvent = false;
24187         }
24188         
24189         this.image.OriginWidth = this.image.getWidth();
24190         this.image.OriginHeight = this.image.getHeight();
24191         
24192         this.fitThumbBox();
24193         
24194         this.image.setWidth(this.image.OriginWidth * this.getScaleLevel(false));
24195         this.image.setHeight(this.image.OriginHeight * this.getScaleLevel(false));
24196                 
24197         this.footerSection.show();
24198         
24199         this.setCanvasPosition();
24200     },
24201     
24202     setCanvasPosition : function()
24203     {   
24204         var pw = (this.imageSection.getWidth(true) - this.image.getWidth()) / 2;
24205         var ph = (this.imageSection.getHeight(true) - this.image.getHeight()) / 2;
24206         
24207         this.imageCanvas.setLeft(pw);
24208         this.imageCanvas.setTop(ph);
24209     },
24210     
24211     onMouseDown : function(e)
24212     {   
24213         e.stopEvent();
24214         
24215         this.dragable = true;
24216         this.mouseX = e.getPageX();
24217         this.mouseY = e.getPageY();
24218         
24219     },
24220     
24221     onMouseMove : function(e)
24222     {   
24223         e.stopEvent();
24224         
24225         if (!this.dragable){
24226             return;
24227         }
24228         
24229         var transform = new WebKitCSSMatrix(window.getComputedStyle(this.thumb.dom).webkitTransform);
24230         
24231         var minX = this.thumb.getLeft(true) + transform.m41;
24232         var minY = this.thumb.getTop(true) + transform.m42;
24233         
24234         var maxX = minX + this.thumb.getWidth() - this.image.getWidth();
24235         var maxY = minY + this.thumb.getHeight() - this.image.getHeight();
24236         
24237         if(this.rotate == 90 || this.rotate == 270){
24238             minX = this.thumb.getLeft(true) + transform.m41 - (this.image.getWidth() - this.image.getHeight()) / 2;
24239             minY = this.thumb.getTop(true) + transform.m42 + (this.image.getWidth() - this.image.getHeight()) / 2;
24240             
24241             maxX = minX + this.thumb.getWidth() - this.image.getHeight();
24242             maxY = minY + this.thumb.getHeight() - this.image.getWidth();
24243         }
24244         
24245         var x = e.getPageX() - this.mouseX;
24246         var y = e.getPageY() - this.mouseY;
24247         
24248         var bgX = x + this.imageCanvas.getLeft(true);
24249         var bgY = y + this.imageCanvas.getTop(true);
24250         
24251         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24252         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24253         
24254         this.imageCanvas.setLeft(bgX);
24255         this.imageCanvas.setTop(bgY);
24256         
24257         this.mouseX = e.getPageX();
24258         this.mouseY = e.getPageY();
24259     },
24260     
24261     onMouseUp : function(e)
24262     {   
24263         e.stopEvent();
24264         
24265         this.dragable = false;
24266     },
24267     
24268     onMouseWheel : function(e)
24269     {   
24270         e.stopEvent();
24271         
24272         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24273         
24274         var width = this.image.OriginWidth * this.getScaleLevel(false);
24275         var height = this.image.OriginHeight * this.getScaleLevel(false);
24276         
24277         if(
24278                 e.getWheelDelta() == -1 &&
24279                 (
24280                     (
24281                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumb.getWidth() || height < this.thumb.getHeight())
24282                     )
24283                     ||
24284                     (
24285                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumb.getWidth() || width < this.thumb.getHeight())
24286                     )
24287                 )
24288         ){
24289             this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
24290             return;
24291         }
24292         
24293         this.image.setWidth(width);
24294         this.image.setHeight(height);
24295         
24296         this.setCanvasPosition();
24297         
24298     },
24299     
24300     onRotateLeft : function(e)
24301     {
24302         e.stopEvent();
24303         
24304         if(
24305                 (
24306                     (this.rotate == 0 || this.rotate == 180) 
24307                     &&
24308                     (this.image.getHeight() < this.thumb.getWidth() || this.image.getWidth() < this.thumb.getHeight())
24309                 )
24310                 ||
24311                 (
24312                     (this.rotate == 90 || this.rotate == 270) 
24313                     &&
24314                     (this.image.getWidth() < this.thumb.getWidth() || this.image.getHeight() < this.thumb.getHeight())
24315                 )
24316                 
24317         ){
24318             return;
24319         }
24320         
24321         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24322
24323         this.imageCanvas.setStyle({
24324             '-ms-transform' : 'rotate(' + this.rotate + 'deg)',
24325             '-webkit-transform' : 'rotate(' + this.rotate + 'deg)',
24326             'transform' : 'rotate(' + this.rotate + 'deg)'
24327         });
24328
24329         this.setCanvasPosition();
24330         
24331     },
24332     
24333     onRotateRight : function(e)
24334     {
24335         e.stopEvent();
24336         
24337         if(
24338                 (
24339                     (this.rotate == 0 || this.rotate == 180) 
24340                     &&
24341                     (this.image.getHeight() < this.thumb.getWidth() || this.image.getWidth() < this.thumb.getHeight())
24342                 )
24343                 ||
24344                 (
24345                     (this.rotate == 90 || this.rotate == 270) 
24346                     &&
24347                     (this.image.getWidth() < this.thumb.getWidth() || this.image.getHeight() < this.thumb.getHeight())
24348                 )
24349                 
24350         ){
24351             return false;
24352         }
24353         
24354         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24355
24356         this.imageCanvas.setStyle({
24357             '-ms-transform' : 'rotate(' + this.rotate + 'deg)',
24358             '-webkit-transform' : 'rotate(' + this.rotate + 'deg)',
24359             'transform' : 'rotate(' + this.rotate + 'deg)'
24360         });
24361
24362         this.setCanvasPosition();
24363         
24364         
24365     },
24366     
24367     crop : function()
24368     {
24369         var canvas = document.createElement("canvas");
24370         
24371         var context = canvas.getContext("2d");
24372         
24373         canvas.width = this.minWidth;
24374         canvas.height = this.minHeight;
24375         
24376         var centerX = this.minWidth / 2;
24377         var centerY = this.minHeight / 2;
24378         
24379         var cropWidth = this.thumb.getWidth() * this.getScaleLevel(true);
24380         var cropHeight = this.thumb.getHeight() * this.getScaleLevel(true);
24381         
24382         var transform = new WebKitCSSMatrix(window.getComputedStyle(this.thumb.dom).webkitTransform);
24383         var thumbX = this.thumb.getLeft(true) + transform.m41;
24384         var thumbY = this.thumb.getTop(true) + transform.m42;
24385         
24386         var x = (thumbX - this.imageCanvas.getLeft(true)) * this.getScaleLevel(true);
24387         var y = (thumbY - this.imageCanvas.getTop(true)) * this.getScaleLevel(true);
24388         
24389         if(this.rotate == 90){
24390             
24391             x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24392             y = this.image.getHeight() - this.thumb.getWidth() - (thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true));
24393             
24394             x = x * this.getScaleLevel(true);
24395             y = y * this.getScaleLevel(true);
24396             
24397             x = x < 0 ? 0 : x;
24398             y = y < 0 ? 0 : y;
24399             
24400             cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24401             cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24402             
24403             canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24404             canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24405
24406             centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24407             centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24408             
24409             context.translate(centerX, centerY);
24410             context.rotate(this.rotate * Math.PI / 180);
24411             
24412             context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24413         
24414             var canvas2 = document.createElement("canvas");
24415             var context2 = canvas2.getContext("2d");
24416             
24417             canvas2.width = this.minWidth;
24418             canvas2.height = this.minHeight;
24419             
24420             context2.drawImage(canvas, Math.abs(this.minWidth - this.minHeight), 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24421     
24422             this.cropImageData = canvas2.toDataURL(this.cropType);
24423
24424             this.fireEvent('crop', this, this.cropImageData);
24425             
24426             return;
24427             
24428         }
24429         
24430         if(this.rotate == 270){
24431             
24432             x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24433             y = thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true);
24434             
24435             x = (this.image.getWidth() - this.thumb.getHeight() - x) * this.getScaleLevel(true);
24436             y = y * this.getScaleLevel(true);
24437             
24438             x = x < 0 ? 0 : x;
24439             y = y < 0 ? 0 : y;
24440             
24441             cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24442             cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24443             
24444             canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24445             canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24446
24447             centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24448             centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24449             
24450             context.translate(centerX, centerY);
24451             context.rotate(this.rotate * Math.PI / 180);
24452             
24453             context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24454         
24455             var canvas2 = document.createElement("canvas");
24456             var context2 = canvas2.getContext("2d");
24457             
24458             canvas2.width = this.minWidth;
24459             canvas2.height = this.minHeight;
24460             
24461             context2.drawImage(canvas, 0, 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24462     
24463             this.cropImageData = canvas2.toDataURL(this.cropType);
24464             
24465             this.fireEvent('crop', this, this.cropImageData);
24466             
24467             return;
24468             
24469         }
24470         
24471         if(this.rotate == 180){
24472             x = this.image.OriginWidth - this.thumb.getWidth() * this.getScaleLevel(true) - x;
24473             y = this.image.OriginHeight - this.thumb.getHeight() * this.getScaleLevel(true) - y;
24474         }
24475         
24476         x = x < 0 ? 0 : x;
24477         y = y < 0 ? 0 : y;
24478             
24479         context.translate(centerX, centerY);
24480
24481         context.rotate(this.rotate * Math.PI / 180);
24482         
24483         context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, canvas.width, canvas.height);
24484         
24485         this.cropImageData = canvas.toDataURL(this.cropType);
24486         
24487         this.fireEvent('crop', this, this.cropImageData);
24488     },
24489     
24490     calcThumbBoxSize : function()
24491     {
24492         var width, height;
24493         
24494         height = 300;
24495         width = this.minWidth * height / this.minHeight;
24496         
24497         if(this.minWidth > this.minHeight){
24498             width = 300;
24499             height = this.minHeight * width / this.minWidth;
24500         }
24501         
24502         this.thumb.setStyle({
24503             width : width + 'px',
24504             height : height + 'px'
24505         });
24506
24507         return;
24508             
24509     },
24510     
24511     fitThumbBox : function()
24512     {
24513         var width = this.thumb.getWidth();
24514         var height = this.image.OriginHeight * width / this.image.OriginWidth;
24515         
24516         this.baseScale = width / this.image.OriginWidth;
24517         
24518         if(this.image.OriginWidth > this.image.OriginHeight){
24519             height = this.thumb.getHeight();
24520             width = this.image.OriginWidth * height / this.image.OriginHeight;
24521             
24522             this.baseScale = height / this.image.OriginHeight;
24523         }
24524         
24525         return;
24526     },
24527     
24528     getScaleLevel : function(reverse)
24529     {
24530         if(reverse){
24531             return Math.pow(1.1, this.scale * -1) / this.baseScale;
24532         }
24533         
24534         return this.baseScale * Math.pow(1.1, this.scale);
24535     }
24536     
24537 });