Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state && this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * @constructor
4313  * Create a new Navbar Button
4314  * @param {Object} config The config object
4315  */
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4318     this.addEvents({
4319         // raw events
4320         /**
4321          * @event click
4322          * The raw click event for the entire grid.
4323          * @param {Roo.EventObject} e
4324          */
4325         "click" : true,
4326          /**
4327             * @event changed
4328             * Fires when the active item active state changes
4329             * @param {Roo.bootstrap.NavSidebarItem} this
4330             * @param {boolean} state the new state
4331              
4332          */
4333         'changed': true
4334     });
4335    
4336 };
4337
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4339     
4340     
4341     getAutoCreate : function(){
4342         
4343         
4344         var a = {
4345                 tag: 'a',
4346                 href : this.href || '#',
4347                 cls: '',
4348                 html : '',
4349                 cn : []
4350         };
4351         var cfg = {
4352             tag: 'li',
4353             cls: '',
4354             cn: [ a ]
4355         }
4356         var span = {
4357             tag: 'span',
4358             html : this.html || ''
4359         }
4360         
4361         
4362         if (this.active) {
4363             cfg.cls += ' active';
4364         }
4365         
4366         // left icon..
4367         if (this.glyphicon || this.icon) {
4368             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4369             a.cn.push({ tag : 'i', cls : c }) ;
4370         }
4371         // html..
4372         a.cn.push(span);
4373         // then badge..
4374         if (this.badge !== '') {
4375             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4376         }
4377         // fi
4378         if (this.menu) {
4379             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380             a.cls += 'dropdown-toggle treeview' ;
4381             
4382         }
4383         
4384         
4385         
4386         return cfg;
4387          
4388            
4389     }
4390    
4391      
4392  
4393 });
4394  
4395
4396  /*
4397  * - LGPL
4398  *
4399  * row
4400  * 
4401  */
4402
4403 /**
4404  * @class Roo.bootstrap.Row
4405  * @extends Roo.bootstrap.Component
4406  * Bootstrap Row class (contains columns...)
4407  * 
4408  * @constructor
4409  * Create a new Row
4410  * @param {Object} config The config object
4411  */
4412
4413 Roo.bootstrap.Row = function(config){
4414     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4415 };
4416
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4418     
4419     getAutoCreate : function(){
4420        return {
4421             cls: 'row clearfix'
4422        };
4423     }
4424     
4425     
4426 });
4427
4428  
4429
4430  /*
4431  * - LGPL
4432  *
4433  * element
4434  * 
4435  */
4436
4437 /**
4438  * @class Roo.bootstrap.Element
4439  * @extends Roo.bootstrap.Component
4440  * Bootstrap Element class
4441  * @cfg {String} html contents of the element
4442  * @cfg {String} tag tag of the element
4443  * @cfg {String} cls class of the element
4444  * @cfg {Boolean} preventDefault (true|false) default false
4445  * @cfg {Boolean} clickable (true|false) default false
4446  * 
4447  * @constructor
4448  * Create a new Element
4449  * @param {Object} config The config object
4450  */
4451
4452 Roo.bootstrap.Element = function(config){
4453     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4454     
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * When a element is chick
4460          * @param {Roo.bootstrap.Element} this
4461          * @param {Roo.EventObject} e
4462          */
4463         "click" : true
4464     });
4465 };
4466
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4468     
4469     tag: 'div',
4470     cls: '',
4471     html: '',
4472     preventDefault: false, 
4473     clickable: false,
4474     
4475     getAutoCreate : function(){
4476         
4477         var cfg = {
4478             tag: this.tag,
4479             cls: this.cls,
4480             html: this.html
4481         }
4482         
4483         return cfg;
4484     },
4485     
4486     initEvents: function() 
4487     {
4488         Roo.bootstrap.Element.superclass.initEvents.call(this);
4489         
4490         if(this.clickable){
4491             this.el.on('click', this.onClick, this);
4492         }
4493         
4494     },
4495     
4496     onClick : function(e)
4497     {
4498         if(this.preventDefault){
4499             e.preventDefault();
4500         }
4501         
4502         this.fireEvent('click', this, e);
4503     },
4504     
4505     getValue : function()
4506     {
4507         return this.el.dom.innerHTML;
4508     },
4509     
4510     setValue : function(value)
4511     {
4512         this.el.dom.innerHTML = value;
4513     }
4514    
4515 });
4516
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * pagination
4523  * 
4524  */
4525
4526 /**
4527  * @class Roo.bootstrap.Pagination
4528  * @extends Roo.bootstrap.Component
4529  * Bootstrap Pagination class
4530  * @cfg {String} size xs | sm | md | lg
4531  * @cfg {Boolean} inverse false | true
4532  * 
4533  * @constructor
4534  * Create a new Pagination
4535  * @param {Object} config The config object
4536  */
4537
4538 Roo.bootstrap.Pagination = function(config){
4539     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4540 };
4541
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4543     
4544     cls: false,
4545     size: false,
4546     inverse: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = {
4550             tag: 'ul',
4551                 cls: 'pagination'
4552         };
4553         if (this.inverse) {
4554             cfg.cls += ' inverse';
4555         }
4556         if (this.html) {
4557             cfg.html=this.html;
4558         }
4559         if (this.cls) {
4560             cfg.cls += " " + this.cls;
4561         }
4562         return cfg;
4563     }
4564    
4565 });
4566
4567  
4568
4569  /*
4570  * - LGPL
4571  *
4572  * Pagination item
4573  * 
4574  */
4575
4576
4577 /**
4578  * @class Roo.bootstrap.PaginationItem
4579  * @extends Roo.bootstrap.Component
4580  * Bootstrap PaginationItem class
4581  * @cfg {String} html text
4582  * @cfg {String} href the link
4583  * @cfg {Boolean} preventDefault (true | false) default true
4584  * @cfg {Boolean} active (true | false) default false
4585  * @cfg {Boolean} disabled default false
4586  * 
4587  * 
4588  * @constructor
4589  * Create a new PaginationItem
4590  * @param {Object} config The config object
4591  */
4592
4593
4594 Roo.bootstrap.PaginationItem = function(config){
4595     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4596     this.addEvents({
4597         // raw events
4598         /**
4599          * @event click
4600          * The raw click event for the entire grid.
4601          * @param {Roo.EventObject} e
4602          */
4603         "click" : true
4604     });
4605 };
4606
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4608     
4609     href : false,
4610     html : false,
4611     preventDefault: true,
4612     active : false,
4613     cls : false,
4614     disabled: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg= {
4618             tag: 'li',
4619             cn: [
4620                 {
4621                     tag : 'a',
4622                     href : this.href ? this.href : '#',
4623                     html : this.html ? this.html : ''
4624                 }
4625             ]
4626         };
4627         
4628         if(this.cls){
4629             cfg.cls = this.cls;
4630         }
4631         
4632         if(this.disabled){
4633             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4634         }
4635         
4636         if(this.active){
4637             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4638         }
4639         
4640         return cfg;
4641     },
4642     
4643     initEvents: function() {
4644         
4645         this.el.on('click', this.onClick, this);
4646         
4647     },
4648     onClick : function(e)
4649     {
4650         Roo.log('PaginationItem on click ');
4651         if(this.preventDefault){
4652             e.preventDefault();
4653         }
4654         
4655         if(this.disabled){
4656             return;
4657         }
4658         
4659         this.fireEvent('click', this, e);
4660     }
4661    
4662 });
4663
4664  
4665
4666  /*
4667  * - LGPL
4668  *
4669  * slider
4670  * 
4671  */
4672
4673
4674 /**
4675  * @class Roo.bootstrap.Slider
4676  * @extends Roo.bootstrap.Component
4677  * Bootstrap Slider class
4678  *    
4679  * @constructor
4680  * Create a new Slider
4681  * @param {Object} config The config object
4682  */
4683
4684 Roo.bootstrap.Slider = function(config){
4685     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4686 };
4687
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4689     
4690     getAutoCreate : function(){
4691         
4692         var cfg = {
4693             tag: 'div',
4694             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4695             cn: [
4696                 {
4697                     tag: 'a',
4698                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4699                 }
4700             ]
4701         }
4702         
4703         return cfg;
4704     }
4705    
4706 });
4707
4708  /*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718  
4719
4720 /**
4721  * @class Roo.grid.ColumnModel
4722  * @extends Roo.util.Observable
4723  * This is the default implementation of a ColumnModel used by the Grid. It defines
4724  * the columns in the grid.
4725  * <br>Usage:<br>
4726  <pre><code>
4727  var colModel = new Roo.grid.ColumnModel([
4728         {header: "Ticker", width: 60, sortable: true, locked: true},
4729         {header: "Company Name", width: 150, sortable: true},
4730         {header: "Market Cap.", width: 100, sortable: true},
4731         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732         {header: "Employees", width: 100, sortable: true, resizable: false}
4733  ]);
4734  </code></pre>
4735  * <p>
4736  
4737  * The config options listed for this class are options which may appear in each
4738  * individual column definition.
4739  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4740  * @constructor
4741  * @param {Object} config An Array of column config objects. See this class's
4742  * config objects for details.
4743 */
4744 Roo.grid.ColumnModel = function(config){
4745         /**
4746      * The config passed into the constructor
4747      */
4748     this.config = config;
4749     this.lookup = {};
4750
4751     // if no id, create one
4752     // if the column does not have a dataIndex mapping,
4753     // map it to the order it is in the config
4754     for(var i = 0, len = config.length; i < len; i++){
4755         var c = config[i];
4756         if(typeof c.dataIndex == "undefined"){
4757             c.dataIndex = i;
4758         }
4759         if(typeof c.renderer == "string"){
4760             c.renderer = Roo.util.Format[c.renderer];
4761         }
4762         if(typeof c.id == "undefined"){
4763             c.id = Roo.id();
4764         }
4765         if(c.editor && c.editor.xtype){
4766             c.editor  = Roo.factory(c.editor, Roo.grid);
4767         }
4768         if(c.editor && c.editor.isFormField){
4769             c.editor = new Roo.grid.GridEditor(c.editor);
4770         }
4771         this.lookup[c.id] = c;
4772     }
4773
4774     /**
4775      * The width of columns which have no width specified (defaults to 100)
4776      * @type Number
4777      */
4778     this.defaultWidth = 100;
4779
4780     /**
4781      * Default sortable of columns which have no sortable specified (defaults to false)
4782      * @type Boolean
4783      */
4784     this.defaultSortable = false;
4785
4786     this.addEvents({
4787         /**
4788              * @event widthchange
4789              * Fires when the width of a column changes.
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Number} newWidth The new width
4793              */
4794             "widthchange": true,
4795         /**
4796              * @event headerchange
4797              * Fires when the text of a header changes.
4798              * @param {ColumnModel} this
4799              * @param {Number} columnIndex The column index
4800              * @param {Number} newText The new header text
4801              */
4802             "headerchange": true,
4803         /**
4804              * @event hiddenchange
4805              * Fires when a column is hidden or "unhidden".
4806              * @param {ColumnModel} this
4807              * @param {Number} columnIndex The column index
4808              * @param {Boolean} hidden true if hidden, false otherwise
4809              */
4810             "hiddenchange": true,
4811             /**
4812          * @event columnmoved
4813          * Fires when a column is moved.
4814          * @param {ColumnModel} this
4815          * @param {Number} oldIndex
4816          * @param {Number} newIndex
4817          */
4818         "columnmoved" : true,
4819         /**
4820          * @event columlockchange
4821          * Fires when a column's locked state is changed
4822          * @param {ColumnModel} this
4823          * @param {Number} colIndex
4824          * @param {Boolean} locked true if locked
4825          */
4826         "columnlockchange" : true
4827     });
4828     Roo.grid.ColumnModel.superclass.constructor.call(this);
4829 };
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4831     /**
4832      * @cfg {String} header The header text to display in the Grid view.
4833      */
4834     /**
4835      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837      * specified, the column's index is used as an index into the Record's data Array.
4838      */
4839     /**
4840      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4842      */
4843     /**
4844      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845      * Defaults to the value of the {@link #defaultSortable} property.
4846      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4847      */
4848     /**
4849      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4850      */
4851     /**
4852      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4853      */
4854     /**
4855      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4856      */
4857     /**
4858      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4859      */
4860     /**
4861      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4865      */
4866        /**
4867      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4868      */
4869     /**
4870      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4871      */
4872     /**
4873      * @cfg {String} cursor (Optional)
4874      */
4875     /**
4876      * @cfg {String} tooltip (Optional)
4877      */
4878     /**
4879      * Returns the id of the column at the specified index.
4880      * @param {Number} index The column index
4881      * @return {String} the id
4882      */
4883     getColumnId : function(index){
4884         return this.config[index].id;
4885     },
4886
4887     /**
4888      * Returns the column for a specified id.
4889      * @param {String} id The column id
4890      * @return {Object} the column
4891      */
4892     getColumnById : function(id){
4893         return this.lookup[id];
4894     },
4895
4896     
4897     /**
4898      * Returns the column for a specified dataIndex.
4899      * @param {String} dataIndex The column dataIndex
4900      * @return {Object|Boolean} the column or false if not found
4901      */
4902     getColumnByDataIndex: function(dataIndex){
4903         var index = this.findColumnIndex(dataIndex);
4904         return index > -1 ? this.config[index] : false;
4905     },
4906     
4907     /**
4908      * Returns the index for a specified column id.
4909      * @param {String} id The column id
4910      * @return {Number} the index, or -1 if not found
4911      */
4912     getIndexById : function(id){
4913         for(var i = 0, len = this.config.length; i < len; i++){
4914             if(this.config[i].id == id){
4915                 return i;
4916             }
4917         }
4918         return -1;
4919     },
4920     
4921     /**
4922      * Returns the index for a specified column dataIndex.
4923      * @param {String} dataIndex The column dataIndex
4924      * @return {Number} the index, or -1 if not found
4925      */
4926     
4927     findColumnIndex : function(dataIndex){
4928         for(var i = 0, len = this.config.length; i < len; i++){
4929             if(this.config[i].dataIndex == dataIndex){
4930                 return i;
4931             }
4932         }
4933         return -1;
4934     },
4935     
4936     
4937     moveColumn : function(oldIndex, newIndex){
4938         var c = this.config[oldIndex];
4939         this.config.splice(oldIndex, 1);
4940         this.config.splice(newIndex, 0, c);
4941         this.dataMap = null;
4942         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4943     },
4944
4945     isLocked : function(colIndex){
4946         return this.config[colIndex].locked === true;
4947     },
4948
4949     setLocked : function(colIndex, value, suppressEvent){
4950         if(this.isLocked(colIndex) == value){
4951             return;
4952         }
4953         this.config[colIndex].locked = value;
4954         if(!suppressEvent){
4955             this.fireEvent("columnlockchange", this, colIndex, value);
4956         }
4957     },
4958
4959     getTotalLockedWidth : function(){
4960         var totalWidth = 0;
4961         for(var i = 0; i < this.config.length; i++){
4962             if(this.isLocked(i) && !this.isHidden(i)){
4963                 this.totalWidth += this.getColumnWidth(i);
4964             }
4965         }
4966         return totalWidth;
4967     },
4968
4969     getLockedCount : function(){
4970         for(var i = 0, len = this.config.length; i < len; i++){
4971             if(!this.isLocked(i)){
4972                 return i;
4973             }
4974         }
4975     },
4976
4977     /**
4978      * Returns the number of columns.
4979      * @return {Number}
4980      */
4981     getColumnCount : function(visibleOnly){
4982         if(visibleOnly === true){
4983             var c = 0;
4984             for(var i = 0, len = this.config.length; i < len; i++){
4985                 if(!this.isHidden(i)){
4986                     c++;
4987                 }
4988             }
4989             return c;
4990         }
4991         return this.config.length;
4992     },
4993
4994     /**
4995      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996      * @param {Function} fn
4997      * @param {Object} scope (optional)
4998      * @return {Array} result
4999      */
5000     getColumnsBy : function(fn, scope){
5001         var r = [];
5002         for(var i = 0, len = this.config.length; i < len; i++){
5003             var c = this.config[i];
5004             if(fn.call(scope||this, c, i) === true){
5005                 r[r.length] = c;
5006             }
5007         }
5008         return r;
5009     },
5010
5011     /**
5012      * Returns true if the specified column is sortable.
5013      * @param {Number} col The column index
5014      * @return {Boolean}
5015      */
5016     isSortable : function(col){
5017         if(typeof this.config[col].sortable == "undefined"){
5018             return this.defaultSortable;
5019         }
5020         return this.config[col].sortable;
5021     },
5022
5023     /**
5024      * Returns the rendering (formatting) function defined for the column.
5025      * @param {Number} col The column index.
5026      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5027      */
5028     getRenderer : function(col){
5029         if(!this.config[col].renderer){
5030             return Roo.grid.ColumnModel.defaultRenderer;
5031         }
5032         return this.config[col].renderer;
5033     },
5034
5035     /**
5036      * Sets the rendering (formatting) function for a column.
5037      * @param {Number} col The column index
5038      * @param {Function} fn The function to use to process the cell's raw data
5039      * to return HTML markup for the grid view. The render function is called with
5040      * the following parameters:<ul>
5041      * <li>Data value.</li>
5042      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043      * <li>css A CSS style string to apply to the table cell.</li>
5044      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046      * <li>Row index</li>
5047      * <li>Column index</li>
5048      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5049      */
5050     setRenderer : function(col, fn){
5051         this.config[col].renderer = fn;
5052     },
5053
5054     /**
5055      * Returns the width for the specified column.
5056      * @param {Number} col The column index
5057      * @return {Number}
5058      */
5059     getColumnWidth : function(col){
5060         return this.config[col].width * 1 || this.defaultWidth;
5061     },
5062
5063     /**
5064      * Sets the width for a column.
5065      * @param {Number} col The column index
5066      * @param {Number} width The new width
5067      */
5068     setColumnWidth : function(col, width, suppressEvent){
5069         this.config[col].width = width;
5070         this.totalWidth = null;
5071         if(!suppressEvent){
5072              this.fireEvent("widthchange", this, col, width);
5073         }
5074     },
5075
5076     /**
5077      * Returns the total width of all columns.
5078      * @param {Boolean} includeHidden True to include hidden column widths
5079      * @return {Number}
5080      */
5081     getTotalWidth : function(includeHidden){
5082         if(!this.totalWidth){
5083             this.totalWidth = 0;
5084             for(var i = 0, len = this.config.length; i < len; i++){
5085                 if(includeHidden || !this.isHidden(i)){
5086                     this.totalWidth += this.getColumnWidth(i);
5087                 }
5088             }
5089         }
5090         return this.totalWidth;
5091     },
5092
5093     /**
5094      * Returns the header for the specified column.
5095      * @param {Number} col The column index
5096      * @return {String}
5097      */
5098     getColumnHeader : function(col){
5099         return this.config[col].header;
5100     },
5101
5102     /**
5103      * Sets the header for a column.
5104      * @param {Number} col The column index
5105      * @param {String} header The new header
5106      */
5107     setColumnHeader : function(col, header){
5108         this.config[col].header = header;
5109         this.fireEvent("headerchange", this, col, header);
5110     },
5111
5112     /**
5113      * Returns the tooltip for the specified column.
5114      * @param {Number} col The column index
5115      * @return {String}
5116      */
5117     getColumnTooltip : function(col){
5118             return this.config[col].tooltip;
5119     },
5120     /**
5121      * Sets the tooltip for a column.
5122      * @param {Number} col The column index
5123      * @param {String} tooltip The new tooltip
5124      */
5125     setColumnTooltip : function(col, tooltip){
5126             this.config[col].tooltip = tooltip;
5127     },
5128
5129     /**
5130      * Returns the dataIndex for the specified column.
5131      * @param {Number} col The column index
5132      * @return {Number}
5133      */
5134     getDataIndex : function(col){
5135         return this.config[col].dataIndex;
5136     },
5137
5138     /**
5139      * Sets the dataIndex for a column.
5140      * @param {Number} col The column index
5141      * @param {Number} dataIndex The new dataIndex
5142      */
5143     setDataIndex : function(col, dataIndex){
5144         this.config[col].dataIndex = dataIndex;
5145     },
5146
5147     
5148     
5149     /**
5150      * Returns true if the cell is editable.
5151      * @param {Number} colIndex The column index
5152      * @param {Number} rowIndex The row index
5153      * @return {Boolean}
5154      */
5155     isCellEditable : function(colIndex, rowIndex){
5156         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5157     },
5158
5159     /**
5160      * Returns the editor defined for the cell/column.
5161      * return false or null to disable editing.
5162      * @param {Number} colIndex The column index
5163      * @param {Number} rowIndex The row index
5164      * @return {Object}
5165      */
5166     getCellEditor : function(colIndex, rowIndex){
5167         return this.config[colIndex].editor;
5168     },
5169
5170     /**
5171      * Sets if a column is editable.
5172      * @param {Number} col The column index
5173      * @param {Boolean} editable True if the column is editable
5174      */
5175     setEditable : function(col, editable){
5176         this.config[col].editable = editable;
5177     },
5178
5179
5180     /**
5181      * Returns true if the column is hidden.
5182      * @param {Number} colIndex The column index
5183      * @return {Boolean}
5184      */
5185     isHidden : function(colIndex){
5186         return this.config[colIndex].hidden;
5187     },
5188
5189
5190     /**
5191      * Returns true if the column width cannot be changed
5192      */
5193     isFixed : function(colIndex){
5194         return this.config[colIndex].fixed;
5195     },
5196
5197     /**
5198      * Returns true if the column can be resized
5199      * @return {Boolean}
5200      */
5201     isResizable : function(colIndex){
5202         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5203     },
5204     /**
5205      * Sets if a column is hidden.
5206      * @param {Number} colIndex The column index
5207      * @param {Boolean} hidden True if the column is hidden
5208      */
5209     setHidden : function(colIndex, hidden){
5210         this.config[colIndex].hidden = hidden;
5211         this.totalWidth = null;
5212         this.fireEvent("hiddenchange", this, colIndex, hidden);
5213     },
5214
5215     /**
5216      * Sets the editor for a column.
5217      * @param {Number} col The column index
5218      * @param {Object} editor The editor object
5219      */
5220     setEditor : function(col, editor){
5221         this.config[col].editor = editor;
5222     }
5223 });
5224
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226         if(typeof value == "string" && value.length < 1){
5227             return "&#160;";
5228         }
5229         return value;
5230 };
5231
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5234 /*
5235  * Based on:
5236  * Ext JS Library 1.1.1
5237  * Copyright(c) 2006-2007, Ext JS, LLC.
5238  *
5239  * Originally Released Under LGPL - original licence link has changed is not relivant.
5240  *
5241  * Fork - LGPL
5242  * <script type="text/javascript">
5243  */
5244  
5245 /**
5246  * @class Roo.LoadMask
5247  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5248  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5250  * element's UpdateManager load indicator and will be destroyed after the initial load.
5251  * @constructor
5252  * Create a new LoadMask
5253  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254  * @param {Object} config The config object
5255  */
5256 Roo.LoadMask = function(el, config){
5257     this.el = Roo.get(el);
5258     Roo.apply(this, config);
5259     if(this.store){
5260         this.store.on('beforeload', this.onBeforeLoad, this);
5261         this.store.on('load', this.onLoad, this);
5262         this.store.on('loadexception', this.onLoadException, this);
5263         this.removeMask = false;
5264     }else{
5265         var um = this.el.getUpdateManager();
5266         um.showLoadIndicator = false; // disable the default indicator
5267         um.on('beforeupdate', this.onBeforeLoad, this);
5268         um.on('update', this.onLoad, this);
5269         um.on('failure', this.onLoad, this);
5270         this.removeMask = true;
5271     }
5272 };
5273
5274 Roo.LoadMask.prototype = {
5275     /**
5276      * @cfg {Boolean} removeMask
5277      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5279      */
5280     /**
5281      * @cfg {String} msg
5282      * The text to display in a centered loading message box (defaults to 'Loading...')
5283      */
5284     msg : 'Loading...',
5285     /**
5286      * @cfg {String} msgCls
5287      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5288      */
5289     msgCls : 'x-mask-loading',
5290
5291     /**
5292      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5293      * @type Boolean
5294      */
5295     disabled: false,
5296
5297     /**
5298      * Disables the mask to prevent it from being displayed
5299      */
5300     disable : function(){
5301        this.disabled = true;
5302     },
5303
5304     /**
5305      * Enables the mask so that it can be displayed
5306      */
5307     enable : function(){
5308         this.disabled = false;
5309     },
5310     
5311     onLoadException : function()
5312     {
5313         Roo.log(arguments);
5314         
5315         if (typeof(arguments[3]) != 'undefined') {
5316             Roo.MessageBox.alert("Error loading",arguments[3]);
5317         } 
5318         /*
5319         try {
5320             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5322             }   
5323         } catch(e) {
5324             
5325         }
5326         */
5327     
5328         
5329         
5330         this.el.unmask(this.removeMask);
5331     },
5332     // private
5333     onLoad : function()
5334     {
5335         this.el.unmask(this.removeMask);
5336     },
5337
5338     // private
5339     onBeforeLoad : function(){
5340         if(!this.disabled){
5341             this.el.mask(this.msg, this.msgCls);
5342         }
5343     },
5344
5345     // private
5346     destroy : function(){
5347         if(this.store){
5348             this.store.un('beforeload', this.onBeforeLoad, this);
5349             this.store.un('load', this.onLoad, this);
5350             this.store.un('loadexception', this.onLoadException, this);
5351         }else{
5352             var um = this.el.getUpdateManager();
5353             um.un('beforeupdate', this.onBeforeLoad, this);
5354             um.un('update', this.onLoad, this);
5355             um.un('failure', this.onLoad, this);
5356         }
5357     }
5358 };/*
5359  * - LGPL
5360  *
5361  * table
5362  * 
5363  */
5364
5365 /**
5366  * @class Roo.bootstrap.Table
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Table class
5369  * @cfg {String} cls table class
5370  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371  * @cfg {String} bgcolor Specifies the background color for a table
5372  * @cfg {Number} border Specifies whether the table cells should have borders or not
5373  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374  * @cfg {Number} cellspacing Specifies the space between cells
5375  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377  * @cfg {String} sortable Specifies that the table should be sortable
5378  * @cfg {String} summary Specifies a summary of the content of a table
5379  * @cfg {Number} width Specifies the width of a table
5380  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5381  * 
5382  * @cfg {boolean} striped Should the rows be alternative striped
5383  * @cfg {boolean} bordered Add borders to the table
5384  * @cfg {boolean} hover Add hover highlighting
5385  * @cfg {boolean} condensed Format condensed
5386  * @cfg {boolean} responsive Format condensed
5387  * @cfg {Boolean} loadMask (true|false) default false
5388  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5389  * @cfg {Boolean} thead (true|false) generate thead, default true
5390  * @cfg {Boolean} RowSelection (true|false) default false
5391  * @cfg {Boolean} CellSelection (true|false) default false
5392  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5393  
5394  * 
5395  * @constructor
5396  * Create a new Table
5397  * @param {Object} config The config object
5398  */
5399
5400 Roo.bootstrap.Table = function(config){
5401     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5402     
5403     if (this.sm) {
5404         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5405         this.sm = this.selModel;
5406         this.sm.xmodule = this.xmodule || false;
5407     }
5408     if (this.cm && typeof(this.cm.config) == 'undefined') {
5409         this.colModel = new Roo.grid.ColumnModel(this.cm);
5410         this.cm = this.colModel;
5411         this.cm.xmodule = this.xmodule || false;
5412     }
5413     if (this.store) {
5414         this.store= Roo.factory(this.store, Roo.data);
5415         this.ds = this.store;
5416         this.ds.xmodule = this.xmodule || false;
5417          
5418     }
5419     if (this.footer && this.store) {
5420         this.footer.dataSource = this.ds;
5421         this.footer = Roo.factory(this.footer);
5422     }
5423     
5424     /** @private */
5425     this.addEvents({
5426         /**
5427          * @event cellclick
5428          * Fires when a cell is clicked
5429          * @param {Roo.bootstrap.Table} this
5430          * @param {Roo.Element} el
5431          * @param {Number} rowIndex
5432          * @param {Number} columnIndex
5433          * @param {Roo.EventObject} e
5434          */
5435         "cellclick" : true,
5436         /**
5437          * @event celldblclick
5438          * Fires when a cell is double clicked
5439          * @param {Roo.bootstrap.Table} this
5440          * @param {Roo.Element} el
5441          * @param {Number} rowIndex
5442          * @param {Number} columnIndex
5443          * @param {Roo.EventObject} e
5444          */
5445         "celldblclick" : true,
5446         /**
5447          * @event rowclick
5448          * Fires when a row is clicked
5449          * @param {Roo.bootstrap.Table} this
5450          * @param {Roo.Element} el
5451          * @param {Number} rowIndex
5452          * @param {Roo.EventObject} e
5453          */
5454         "rowclick" : true,
5455         /**
5456          * @event rowdblclick
5457          * Fires when a row is double clicked
5458          * @param {Roo.bootstrap.Table} this
5459          * @param {Roo.Element} el
5460          * @param {Number} rowIndex
5461          * @param {Roo.EventObject} e
5462          */
5463         "rowdblclick" : true,
5464         /**
5465          * @event mouseover
5466          * Fires when a mouseover occur
5467          * @param {Roo.bootstrap.Table} this
5468          * @param {Roo.Element} el
5469          * @param {Number} rowIndex
5470          * @param {Number} columnIndex
5471          * @param {Roo.EventObject} e
5472          */
5473         "mouseover" : true,
5474         /**
5475          * @event mouseout
5476          * Fires when a mouseout occur
5477          * @param {Roo.bootstrap.Table} this
5478          * @param {Roo.Element} el
5479          * @param {Number} rowIndex
5480          * @param {Number} columnIndex
5481          * @param {Roo.EventObject} e
5482          */
5483         "mouseout" : true,
5484         /**
5485          * @event rowclass
5486          * Fires when a row is rendered, so you can change add a style to it.
5487          * @param {Roo.bootstrap.Table} this
5488          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5489          */
5490         'rowclass' : true,
5491           /**
5492          * @event rowsrendered
5493          * Fires when all the  rows have been rendered
5494          * @param {Roo.bootstrap.Table} this
5495          */
5496         'rowsrendered' : true
5497         
5498     });
5499 };
5500
5501 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5502     
5503     cls: false,
5504     align: false,
5505     bgcolor: false,
5506     border: false,
5507     cellpadding: false,
5508     cellspacing: false,
5509     frame: false,
5510     rules: false,
5511     sortable: false,
5512     summary: false,
5513     width: false,
5514     striped : false,
5515     bordered: false,
5516     hover:  false,
5517     condensed : false,
5518     responsive : false,
5519     sm : false,
5520     cm : false,
5521     store : false,
5522     loadMask : false,
5523     tfoot : true,
5524     thead : true,
5525     RowSelection : false,
5526     CellSelection : false,
5527     layout : false,
5528     
5529     // Roo.Element - the tbody
5530     mainBody: false, 
5531     
5532     getAutoCreate : function(){
5533         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5534         
5535         cfg = {
5536             tag: 'table',
5537             cls : 'table',
5538             cn : []
5539         }
5540             
5541         if (this.striped) {
5542             cfg.cls += ' table-striped';
5543         }
5544         
5545         if (this.hover) {
5546             cfg.cls += ' table-hover';
5547         }
5548         if (this.bordered) {
5549             cfg.cls += ' table-bordered';
5550         }
5551         if (this.condensed) {
5552             cfg.cls += ' table-condensed';
5553         }
5554         if (this.responsive) {
5555             cfg.cls += ' table-responsive';
5556         }
5557         
5558         if (this.cls) {
5559             cfg.cls+=  ' ' +this.cls;
5560         }
5561         
5562         // this lot should be simplifed...
5563         
5564         if (this.align) {
5565             cfg.align=this.align;
5566         }
5567         if (this.bgcolor) {
5568             cfg.bgcolor=this.bgcolor;
5569         }
5570         if (this.border) {
5571             cfg.border=this.border;
5572         }
5573         if (this.cellpadding) {
5574             cfg.cellpadding=this.cellpadding;
5575         }
5576         if (this.cellspacing) {
5577             cfg.cellspacing=this.cellspacing;
5578         }
5579         if (this.frame) {
5580             cfg.frame=this.frame;
5581         }
5582         if (this.rules) {
5583             cfg.rules=this.rules;
5584         }
5585         if (this.sortable) {
5586             cfg.sortable=this.sortable;
5587         }
5588         if (this.summary) {
5589             cfg.summary=this.summary;
5590         }
5591         if (this.width) {
5592             cfg.width=this.width;
5593         }
5594         if (this.layout) {
5595             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5596         }
5597         
5598         if(this.store || this.cm){
5599             if(this.thead){
5600                 cfg.cn.push(this.renderHeader());
5601             }
5602             
5603             cfg.cn.push(this.renderBody());
5604             
5605             if(this.tfoot){
5606                 cfg.cn.push(this.renderFooter());
5607             }
5608             
5609             cfg.cls+=  ' TableGrid';
5610         }
5611         
5612         return { cn : [ cfg ] };
5613     },
5614     
5615     initEvents : function()
5616     {   
5617         if(!this.store || !this.cm){
5618             return;
5619         }
5620         
5621         //Roo.log('initEvents with ds!!!!');
5622         
5623         this.mainBody = this.el.select('tbody', true).first();
5624         
5625         
5626         var _this = this;
5627         
5628         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5629             e.on('click', _this.sort, _this);
5630         });
5631         
5632         this.el.on("click", this.onClick, this);
5633         this.el.on("dblclick", this.onDblClick, this);
5634         
5635         // why is this done????? = it breaks dialogs??
5636         //this.parent().el.setStyle('position', 'relative');
5637         
5638         
5639         if (this.footer) {
5640             this.footer.parentId = this.id;
5641             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5642         }
5643         
5644         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5645         
5646         this.store.on('load', this.onLoad, this);
5647         this.store.on('beforeload', this.onBeforeLoad, this);
5648         this.store.on('update', this.onUpdate, this);
5649         this.store.on('add', this.onAdd, this);
5650         
5651     },
5652     
5653     onMouseover : function(e, el)
5654     {
5655         var cell = Roo.get(el);
5656         
5657         if(!cell){
5658             return;
5659         }
5660         
5661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5662             cell = cell.findParent('td', false, true);
5663         }
5664         
5665         var row = cell.findParent('tr', false, true);
5666         var cellIndex = cell.dom.cellIndex;
5667         var rowIndex = row.dom.rowIndex - 1; // start from 0
5668         
5669         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5670         
5671     },
5672     
5673     onMouseout : function(e, el)
5674     {
5675         var cell = Roo.get(el);
5676         
5677         if(!cell){
5678             return;
5679         }
5680         
5681         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5682             cell = cell.findParent('td', false, true);
5683         }
5684         
5685         var row = cell.findParent('tr', false, true);
5686         var cellIndex = cell.dom.cellIndex;
5687         var rowIndex = row.dom.rowIndex - 1; // start from 0
5688         
5689         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5690         
5691     },
5692     
5693     onClick : function(e, el)
5694     {
5695         var cell = Roo.get(el);
5696         
5697         if(!cell || (!this.CellSelection && !this.RowSelection)){
5698             return;
5699         }
5700         
5701         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5702             cell = cell.findParent('td', false, true);
5703         }
5704         
5705         if(!cell || typeof(cell) == 'undefined'){
5706             return;
5707         }
5708         
5709         var row = cell.findParent('tr', false, true);
5710         
5711         if(!row || typeof(row) == 'undefined'){
5712             return;
5713         }
5714         
5715         var cellIndex = cell.dom.cellIndex;
5716         var rowIndex = this.getRowIndex(row);
5717         
5718         if(this.CellSelection){
5719             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5720         }
5721         
5722         if(this.RowSelection){
5723             this.fireEvent('rowclick', this, row, rowIndex, e);
5724         }
5725         
5726         
5727     },
5728     
5729     onDblClick : function(e,el)
5730     {
5731         var cell = Roo.get(el);
5732         
5733         if(!cell || (!this.CellSelection && !this.RowSelection)){
5734             return;
5735         }
5736         
5737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5738             cell = cell.findParent('td', false, true);
5739         }
5740         
5741         if(!cell || typeof(cell) == 'undefined'){
5742             return;
5743         }
5744         
5745         var row = cell.findParent('tr', false, true);
5746         
5747         if(!row || typeof(row) == 'undefined'){
5748             return;
5749         }
5750         
5751         var cellIndex = cell.dom.cellIndex;
5752         var rowIndex = this.getRowIndex(row);
5753         
5754         if(this.CellSelection){
5755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5756         }
5757         
5758         if(this.RowSelection){
5759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5760         }
5761     },
5762     
5763     sort : function(e,el)
5764     {
5765         var col = Roo.get(el);
5766         
5767         if(!col.hasClass('sortable')){
5768             return;
5769         }
5770         
5771         var sort = col.attr('sort');
5772         var dir = 'ASC';
5773         
5774         if(col.hasClass('glyphicon-arrow-up')){
5775             dir = 'DESC';
5776         }
5777         
5778         this.store.sortInfo = {field : sort, direction : dir};
5779         
5780         if (this.footer) {
5781             Roo.log("calling footer first");
5782             this.footer.onClick('first');
5783         } else {
5784         
5785             this.store.load({ params : { start : 0 } });
5786         }
5787     },
5788     
5789     renderHeader : function()
5790     {
5791         var header = {
5792             tag: 'thead',
5793             cn : []
5794         };
5795         
5796         var cm = this.cm;
5797         
5798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5799             
5800             var config = cm.config[i];
5801                     
5802             var c = {
5803                 tag: 'th',
5804                 style : '',
5805                 html: cm.getColumnHeader(i)
5806             };
5807             
5808             if(typeof(config.tooltip) != 'undefined'){
5809                 c.tooltip = config.tooltip;
5810             }
5811             
5812             if(typeof(config.colspan) != 'undefined'){
5813                 c.colspan = config.colspan;
5814             }
5815             
5816             if(typeof(config.hidden) != 'undefined' && config.hidden){
5817                 c.style += ' display:none;';
5818             }
5819             
5820             if(typeof(config.dataIndex) != 'undefined'){
5821                 c.sort = config.dataIndex;
5822             }
5823             
5824             if(typeof(config.sortable) != 'undefined' && config.sortable){
5825                 c.cls = 'sortable';
5826             }
5827             
5828             if(typeof(config.align) != 'undefined' && config.align.length){
5829                 c.style += ' text-align:' + config.align + ';';
5830             }
5831             
5832             if(typeof(config.width) != 'undefined'){
5833                 c.style += ' width:' + config.width + 'px;';
5834             }
5835             
5836             if(typeof(config.cls) != 'undefined'){
5837                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5838             }
5839             
5840             header.cn.push(c)
5841         }
5842         
5843         return header;
5844     },
5845     
5846     renderBody : function()
5847     {
5848         var body = {
5849             tag: 'tbody',
5850             cn : [
5851                 {
5852                     tag: 'tr',
5853                     cn : [
5854                         {
5855                             tag : 'td',
5856                             colspan :  this.cm.getColumnCount()
5857                         }
5858                     ]
5859                 }
5860             ]
5861         };
5862         
5863         return body;
5864     },
5865     
5866     renderFooter : function()
5867     {
5868         var footer = {
5869             tag: 'tfoot',
5870             cn : [
5871                 {
5872                     tag: 'tr',
5873                     cn : [
5874                         {
5875                             tag : 'td',
5876                             colspan :  this.cm.getColumnCount()
5877                         }
5878                     ]
5879                 }
5880             ]
5881         };
5882         
5883         return footer;
5884     },
5885     
5886     
5887     
5888     onLoad : function()
5889     {
5890         Roo.log('ds onload');
5891         this.clear();
5892         
5893         var _this = this;
5894         var cm = this.cm;
5895         var ds = this.store;
5896         
5897         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5898             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5899             
5900             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5901                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5902             }
5903             
5904             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5905                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5906             }
5907         });
5908         
5909         var tbody =  this.mainBody;
5910               
5911         if(ds.getCount() > 0){
5912             ds.data.each(function(d,rowIndex){
5913                 var row =  this.renderRow(cm, ds, rowIndex);
5914                 
5915                 tbody.createChild(row);
5916                 
5917                 var _this = this;
5918                 
5919                 if(row.cellObjects.length){
5920                     Roo.each(row.cellObjects, function(r){
5921                         _this.renderCellObject(r);
5922                     })
5923                 }
5924                 
5925             }, this);
5926         }
5927         
5928         Roo.each(this.el.select('tbody td', true).elements, function(e){
5929             e.on('mouseover', _this.onMouseover, _this);
5930         });
5931         
5932         Roo.each(this.el.select('tbody td', true).elements, function(e){
5933             e.on('mouseout', _this.onMouseout, _this);
5934         });
5935         this.fireEvent('rowsrendered', this);
5936         //if(this.loadMask){
5937         //    this.maskEl.hide();
5938         //}
5939     },
5940     
5941     
5942     onUpdate : function(ds,record)
5943     {
5944         this.refreshRow(record);
5945     },
5946     
5947     onRemove : function(ds, record, index, isUpdate){
5948         if(isUpdate !== true){
5949             this.fireEvent("beforerowremoved", this, index, record);
5950         }
5951         var bt = this.mainBody.dom;
5952         
5953         var rows = this.el.select('tbody > tr', true).elements;
5954         
5955         if(typeof(rows[index]) != 'undefined'){
5956             bt.removeChild(rows[index].dom);
5957         }
5958         
5959 //        if(bt.rows[index]){
5960 //            bt.removeChild(bt.rows[index]);
5961 //        }
5962         
5963         if(isUpdate !== true){
5964             //this.stripeRows(index);
5965             //this.syncRowHeights(index, index);
5966             //this.layout();
5967             this.fireEvent("rowremoved", this, index, record);
5968         }
5969     },
5970     
5971     onAdd : function(ds, records, rowIndex)
5972     {
5973         //Roo.log('on Add called');
5974         // - note this does not handle multiple adding very well..
5975         var bt = this.mainBody.dom;
5976         for (var i =0 ; i < records.length;i++) {
5977             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5978             //Roo.log(records[i]);
5979             //Roo.log(this.store.getAt(rowIndex+i));
5980             this.insertRow(this.store, rowIndex + i, false);
5981             return;
5982         }
5983         
5984     },
5985     
5986     
5987     refreshRow : function(record){
5988         var ds = this.store, index;
5989         if(typeof record == 'number'){
5990             index = record;
5991             record = ds.getAt(index);
5992         }else{
5993             index = ds.indexOf(record);
5994         }
5995         this.insertRow(ds, index, true);
5996         this.onRemove(ds, record, index+1, true);
5997         //this.syncRowHeights(index, index);
5998         //this.layout();
5999         this.fireEvent("rowupdated", this, index, record);
6000     },
6001     
6002     insertRow : function(dm, rowIndex, isUpdate){
6003         
6004         if(!isUpdate){
6005             this.fireEvent("beforerowsinserted", this, rowIndex);
6006         }
6007             //var s = this.getScrollState();
6008         var row = this.renderRow(this.cm, this.store, rowIndex);
6009         // insert before rowIndex..
6010         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6011         
6012         var _this = this;
6013                 
6014         if(row.cellObjects.length){
6015             Roo.each(row.cellObjects, function(r){
6016                 _this.renderCellObject(r);
6017             })
6018         }
6019             
6020         if(!isUpdate){
6021             this.fireEvent("rowsinserted", this, rowIndex);
6022             //this.syncRowHeights(firstRow, lastRow);
6023             //this.stripeRows(firstRow);
6024             //this.layout();
6025         }
6026         
6027     },
6028     
6029     
6030     getRowDom : function(rowIndex)
6031     {
6032         var rows = this.el.select('tbody > tr', true).elements;
6033         
6034         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6035         
6036     },
6037     // returns the object tree for a tr..
6038   
6039     
6040     renderRow : function(cm, ds, rowIndex) 
6041     {
6042         
6043         var d = ds.getAt(rowIndex);
6044         
6045         var row = {
6046             tag : 'tr',
6047             cn : []
6048         };
6049             
6050         var cellObjects = [];
6051         
6052         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6053             var config = cm.config[i];
6054             
6055             var renderer = cm.getRenderer(i);
6056             var value = '';
6057             var id = false;
6058             
6059             if(typeof(renderer) !== 'undefined'){
6060                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6061             }
6062             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6063             // and are rendered into the cells after the row is rendered - using the id for the element.
6064             
6065             if(typeof(value) === 'object'){
6066                 id = Roo.id();
6067                 cellObjects.push({
6068                     container : id,
6069                     cfg : value 
6070                 })
6071             }
6072             
6073             var rowcfg = {
6074                 record: d,
6075                 rowIndex : rowIndex,
6076                 colIndex : i,
6077                 rowClass : ''
6078             }
6079
6080             this.fireEvent('rowclass', this, rowcfg);
6081             
6082             var td = {
6083                 tag: 'td',
6084                 cls : rowcfg.rowClass,
6085                 style: '',
6086                 html: (typeof(value) === 'object') ? '' : value
6087             };
6088             
6089             if (id) {
6090                 td.id = id;
6091             }
6092             
6093             if(typeof(config.colspan) != 'undefined'){
6094                 td.colspan = config.colspan;
6095             }
6096             
6097             if(typeof(config.hidden) != 'undefined' && config.hidden){
6098                 td.style += ' display:none;';
6099             }
6100             
6101             if(typeof(config.align) != 'undefined' && config.align.length){
6102                 td.style += ' text-align:' + config.align + ';';
6103             }
6104             
6105             if(typeof(config.width) != 'undefined'){
6106                 td.style += ' width:' +  config.width + 'px;';
6107             }
6108             
6109             if(typeof(config.cursor) != 'undefined'){
6110                 td.style += ' cursor:' +  config.cursor + ';';
6111             }
6112             
6113             if(typeof(config.cls) != 'undefined'){
6114                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6115             }
6116              
6117             row.cn.push(td);
6118            
6119         }
6120         
6121         row.cellObjects = cellObjects;
6122         
6123         return row;
6124           
6125     },
6126     
6127     
6128     
6129     onBeforeLoad : function()
6130     {
6131         //Roo.log('ds onBeforeLoad');
6132         
6133         //this.clear();
6134         
6135         //if(this.loadMask){
6136         //    this.maskEl.show();
6137         //}
6138     },
6139      /**
6140      * Remove all rows
6141      */
6142     clear : function()
6143     {
6144         this.el.select('tbody', true).first().dom.innerHTML = '';
6145     },
6146     /**
6147      * Show or hide a row.
6148      * @param {Number} rowIndex to show or hide
6149      * @param {Boolean} state hide
6150      */
6151     setRowVisibility : function(rowIndex, state)
6152     {
6153         var bt = this.mainBody.dom;
6154         
6155         var rows = this.el.select('tbody > tr', true).elements;
6156         
6157         if(typeof(rows[rowIndex]) == 'undefined'){
6158             return;
6159         }
6160         rows[rowIndex].dom.style.display = state ? '' : 'none';
6161     },
6162     
6163     
6164     getSelectionModel : function(){
6165         if(!this.selModel){
6166             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6167         }
6168         return this.selModel;
6169     },
6170     /*
6171      * Render the Roo.bootstrap object from renderder
6172      */
6173     renderCellObject : function(r)
6174     {
6175         var _this = this;
6176         
6177         var t = r.cfg.render(r.container);
6178         
6179         if(r.cfg.cn){
6180             Roo.each(r.cfg.cn, function(c){
6181                 var child = {
6182                     container: t.getChildContainer(),
6183                     cfg: c
6184                 }
6185                 _this.renderCellObject(child);
6186             })
6187         }
6188     },
6189     
6190     getRowIndex : function(row)
6191     {
6192         var rowIndex = -1;
6193         
6194         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6195             if(el != row){
6196                 return;
6197             }
6198             
6199             rowIndex = index;
6200         });
6201         
6202         return rowIndex;
6203     }
6204    
6205 });
6206
6207  
6208
6209  /*
6210  * - LGPL
6211  *
6212  * table cell
6213  * 
6214  */
6215
6216 /**
6217  * @class Roo.bootstrap.TableCell
6218  * @extends Roo.bootstrap.Component
6219  * Bootstrap TableCell class
6220  * @cfg {String} html cell contain text
6221  * @cfg {String} cls cell class
6222  * @cfg {String} tag cell tag (td|th) default td
6223  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6224  * @cfg {String} align Aligns the content in a cell
6225  * @cfg {String} axis Categorizes cells
6226  * @cfg {String} bgcolor Specifies the background color of a cell
6227  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6228  * @cfg {Number} colspan Specifies the number of columns a cell should span
6229  * @cfg {String} headers Specifies one or more header cells a cell is related to
6230  * @cfg {Number} height Sets the height of a cell
6231  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6232  * @cfg {Number} rowspan Sets the number of rows a cell should span
6233  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6234  * @cfg {String} valign Vertical aligns the content in a cell
6235  * @cfg {Number} width Specifies the width of a cell
6236  * 
6237  * @constructor
6238  * Create a new TableCell
6239  * @param {Object} config The config object
6240  */
6241
6242 Roo.bootstrap.TableCell = function(config){
6243     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6244 };
6245
6246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6247     
6248     html: false,
6249     cls: false,
6250     tag: false,
6251     abbr: false,
6252     align: false,
6253     axis: false,
6254     bgcolor: false,
6255     charoff: false,
6256     colspan: false,
6257     headers: false,
6258     height: false,
6259     nowrap: false,
6260     rowspan: false,
6261     scope: false,
6262     valign: false,
6263     width: false,
6264     
6265     
6266     getAutoCreate : function(){
6267         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6268         
6269         cfg = {
6270             tag: 'td'
6271         }
6272         
6273         if(this.tag){
6274             cfg.tag = this.tag;
6275         }
6276         
6277         if (this.html) {
6278             cfg.html=this.html
6279         }
6280         if (this.cls) {
6281             cfg.cls=this.cls
6282         }
6283         if (this.abbr) {
6284             cfg.abbr=this.abbr
6285         }
6286         if (this.align) {
6287             cfg.align=this.align
6288         }
6289         if (this.axis) {
6290             cfg.axis=this.axis
6291         }
6292         if (this.bgcolor) {
6293             cfg.bgcolor=this.bgcolor
6294         }
6295         if (this.charoff) {
6296             cfg.charoff=this.charoff
6297         }
6298         if (this.colspan) {
6299             cfg.colspan=this.colspan
6300         }
6301         if (this.headers) {
6302             cfg.headers=this.headers
6303         }
6304         if (this.height) {
6305             cfg.height=this.height
6306         }
6307         if (this.nowrap) {
6308             cfg.nowrap=this.nowrap
6309         }
6310         if (this.rowspan) {
6311             cfg.rowspan=this.rowspan
6312         }
6313         if (this.scope) {
6314             cfg.scope=this.scope
6315         }
6316         if (this.valign) {
6317             cfg.valign=this.valign
6318         }
6319         if (this.width) {
6320             cfg.width=this.width
6321         }
6322         
6323         
6324         return cfg;
6325     }
6326    
6327 });
6328
6329  
6330
6331  /*
6332  * - LGPL
6333  *
6334  * table row
6335  * 
6336  */
6337
6338 /**
6339  * @class Roo.bootstrap.TableRow
6340  * @extends Roo.bootstrap.Component
6341  * Bootstrap TableRow class
6342  * @cfg {String} cls row class
6343  * @cfg {String} align Aligns the content in a table row
6344  * @cfg {String} bgcolor Specifies a background color for a table row
6345  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6346  * @cfg {String} valign Vertical aligns the content in a table row
6347  * 
6348  * @constructor
6349  * Create a new TableRow
6350  * @param {Object} config The config object
6351  */
6352
6353 Roo.bootstrap.TableRow = function(config){
6354     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6355 };
6356
6357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6358     
6359     cls: false,
6360     align: false,
6361     bgcolor: false,
6362     charoff: false,
6363     valign: false,
6364     
6365     getAutoCreate : function(){
6366         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6367         
6368         cfg = {
6369             tag: 'tr'
6370         }
6371             
6372         if(this.cls){
6373             cfg.cls = this.cls;
6374         }
6375         if(this.align){
6376             cfg.align = this.align;
6377         }
6378         if(this.bgcolor){
6379             cfg.bgcolor = this.bgcolor;
6380         }
6381         if(this.charoff){
6382             cfg.charoff = this.charoff;
6383         }
6384         if(this.valign){
6385             cfg.valign = this.valign;
6386         }
6387         
6388         return cfg;
6389     }
6390    
6391 });
6392
6393  
6394
6395  /*
6396  * - LGPL
6397  *
6398  * table body
6399  * 
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.TableBody
6404  * @extends Roo.bootstrap.Component
6405  * Bootstrap TableBody class
6406  * @cfg {String} cls element class
6407  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6408  * @cfg {String} align Aligns the content inside the element
6409  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6410  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6411  * 
6412  * @constructor
6413  * Create a new TableBody
6414  * @param {Object} config The config object
6415  */
6416
6417 Roo.bootstrap.TableBody = function(config){
6418     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6419 };
6420
6421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6422     
6423     cls: false,
6424     tag: false,
6425     align: false,
6426     charoff: false,
6427     valign: false,
6428     
6429     getAutoCreate : function(){
6430         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6431         
6432         cfg = {
6433             tag: 'tbody'
6434         }
6435             
6436         if (this.cls) {
6437             cfg.cls=this.cls
6438         }
6439         if(this.tag){
6440             cfg.tag = this.tag;
6441         }
6442         
6443         if(this.align){
6444             cfg.align = this.align;
6445         }
6446         if(this.charoff){
6447             cfg.charoff = this.charoff;
6448         }
6449         if(this.valign){
6450             cfg.valign = this.valign;
6451         }
6452         
6453         return cfg;
6454     }
6455     
6456     
6457 //    initEvents : function()
6458 //    {
6459 //        
6460 //        if(!this.store){
6461 //            return;
6462 //        }
6463 //        
6464 //        this.store = Roo.factory(this.store, Roo.data);
6465 //        this.store.on('load', this.onLoad, this);
6466 //        
6467 //        this.store.load();
6468 //        
6469 //    },
6470 //    
6471 //    onLoad: function () 
6472 //    {   
6473 //        this.fireEvent('load', this);
6474 //    }
6475 //    
6476 //   
6477 });
6478
6479  
6480
6481  /*
6482  * Based on:
6483  * Ext JS Library 1.1.1
6484  * Copyright(c) 2006-2007, Ext JS, LLC.
6485  *
6486  * Originally Released Under LGPL - original licence link has changed is not relivant.
6487  *
6488  * Fork - LGPL
6489  * <script type="text/javascript">
6490  */
6491
6492 // as we use this in bootstrap.
6493 Roo.namespace('Roo.form');
6494  /**
6495  * @class Roo.form.Action
6496  * Internal Class used to handle form actions
6497  * @constructor
6498  * @param {Roo.form.BasicForm} el The form element or its id
6499  * @param {Object} config Configuration options
6500  */
6501
6502  
6503  
6504 // define the action interface
6505 Roo.form.Action = function(form, options){
6506     this.form = form;
6507     this.options = options || {};
6508 };
6509 /**
6510  * Client Validation Failed
6511  * @const 
6512  */
6513 Roo.form.Action.CLIENT_INVALID = 'client';
6514 /**
6515  * Server Validation Failed
6516  * @const 
6517  */
6518 Roo.form.Action.SERVER_INVALID = 'server';
6519  /**
6520  * Connect to Server Failed
6521  * @const 
6522  */
6523 Roo.form.Action.CONNECT_FAILURE = 'connect';
6524 /**
6525  * Reading Data from Server Failed
6526  * @const 
6527  */
6528 Roo.form.Action.LOAD_FAILURE = 'load';
6529
6530 Roo.form.Action.prototype = {
6531     type : 'default',
6532     failureType : undefined,
6533     response : undefined,
6534     result : undefined,
6535
6536     // interface method
6537     run : function(options){
6538
6539     },
6540
6541     // interface method
6542     success : function(response){
6543
6544     },
6545
6546     // interface method
6547     handleResponse : function(response){
6548
6549     },
6550
6551     // default connection failure
6552     failure : function(response){
6553         
6554         this.response = response;
6555         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6556         this.form.afterAction(this, false);
6557     },
6558
6559     processResponse : function(response){
6560         this.response = response;
6561         if(!response.responseText){
6562             return true;
6563         }
6564         this.result = this.handleResponse(response);
6565         return this.result;
6566     },
6567
6568     // utility functions used internally
6569     getUrl : function(appendParams){
6570         var url = this.options.url || this.form.url || this.form.el.dom.action;
6571         if(appendParams){
6572             var p = this.getParams();
6573             if(p){
6574                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6575             }
6576         }
6577         return url;
6578     },
6579
6580     getMethod : function(){
6581         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6582     },
6583
6584     getParams : function(){
6585         var bp = this.form.baseParams;
6586         var p = this.options.params;
6587         if(p){
6588             if(typeof p == "object"){
6589                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6590             }else if(typeof p == 'string' && bp){
6591                 p += '&' + Roo.urlEncode(bp);
6592             }
6593         }else if(bp){
6594             p = Roo.urlEncode(bp);
6595         }
6596         return p;
6597     },
6598
6599     createCallback : function(){
6600         return {
6601             success: this.success,
6602             failure: this.failure,
6603             scope: this,
6604             timeout: (this.form.timeout*1000),
6605             upload: this.form.fileUpload ? this.success : undefined
6606         };
6607     }
6608 };
6609
6610 Roo.form.Action.Submit = function(form, options){
6611     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6612 };
6613
6614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6615     type : 'submit',
6616
6617     haveProgress : false,
6618     uploadComplete : false,
6619     
6620     // uploadProgress indicator.
6621     uploadProgress : function()
6622     {
6623         if (!this.form.progressUrl) {
6624             return;
6625         }
6626         
6627         if (!this.haveProgress) {
6628             Roo.MessageBox.progress("Uploading", "Uploading");
6629         }
6630         if (this.uploadComplete) {
6631            Roo.MessageBox.hide();
6632            return;
6633         }
6634         
6635         this.haveProgress = true;
6636    
6637         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6638         
6639         var c = new Roo.data.Connection();
6640         c.request({
6641             url : this.form.progressUrl,
6642             params: {
6643                 id : uid
6644             },
6645             method: 'GET',
6646             success : function(req){
6647                //console.log(data);
6648                 var rdata = false;
6649                 var edata;
6650                 try  {
6651                    rdata = Roo.decode(req.responseText)
6652                 } catch (e) {
6653                     Roo.log("Invalid data from server..");
6654                     Roo.log(edata);
6655                     return;
6656                 }
6657                 if (!rdata || !rdata.success) {
6658                     Roo.log(rdata);
6659                     Roo.MessageBox.alert(Roo.encode(rdata));
6660                     return;
6661                 }
6662                 var data = rdata.data;
6663                 
6664                 if (this.uploadComplete) {
6665                    Roo.MessageBox.hide();
6666                    return;
6667                 }
6668                    
6669                 if (data){
6670                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6671                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6672                     );
6673                 }
6674                 this.uploadProgress.defer(2000,this);
6675             },
6676        
6677             failure: function(data) {
6678                 Roo.log('progress url failed ');
6679                 Roo.log(data);
6680             },
6681             scope : this
6682         });
6683            
6684     },
6685     
6686     
6687     run : function()
6688     {
6689         // run get Values on the form, so it syncs any secondary forms.
6690         this.form.getValues();
6691         
6692         var o = this.options;
6693         var method = this.getMethod();
6694         var isPost = method == 'POST';
6695         if(o.clientValidation === false || this.form.isValid()){
6696             
6697             if (this.form.progressUrl) {
6698                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6699                     (new Date() * 1) + '' + Math.random());
6700                     
6701             } 
6702             
6703             
6704             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6705                 form:this.form.el.dom,
6706                 url:this.getUrl(!isPost),
6707                 method: method,
6708                 params:isPost ? this.getParams() : null,
6709                 isUpload: this.form.fileUpload
6710             }));
6711             
6712             this.uploadProgress();
6713
6714         }else if (o.clientValidation !== false){ // client validation failed
6715             this.failureType = Roo.form.Action.CLIENT_INVALID;
6716             this.form.afterAction(this, false);
6717         }
6718     },
6719
6720     success : function(response)
6721     {
6722         this.uploadComplete= true;
6723         if (this.haveProgress) {
6724             Roo.MessageBox.hide();
6725         }
6726         
6727         
6728         var result = this.processResponse(response);
6729         if(result === true || result.success){
6730             this.form.afterAction(this, true);
6731             return;
6732         }
6733         if(result.errors){
6734             this.form.markInvalid(result.errors);
6735             this.failureType = Roo.form.Action.SERVER_INVALID;
6736         }
6737         this.form.afterAction(this, false);
6738     },
6739     failure : function(response)
6740     {
6741         this.uploadComplete= true;
6742         if (this.haveProgress) {
6743             Roo.MessageBox.hide();
6744         }
6745         
6746         this.response = response;
6747         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6748         this.form.afterAction(this, false);
6749     },
6750     
6751     handleResponse : function(response){
6752         if(this.form.errorReader){
6753             var rs = this.form.errorReader.read(response);
6754             var errors = [];
6755             if(rs.records){
6756                 for(var i = 0, len = rs.records.length; i < len; i++) {
6757                     var r = rs.records[i];
6758                     errors[i] = r.data;
6759                 }
6760             }
6761             if(errors.length < 1){
6762                 errors = null;
6763             }
6764             return {
6765                 success : rs.success,
6766                 errors : errors
6767             };
6768         }
6769         var ret = false;
6770         try {
6771             ret = Roo.decode(response.responseText);
6772         } catch (e) {
6773             ret = {
6774                 success: false,
6775                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6776                 errors : []
6777             };
6778         }
6779         return ret;
6780         
6781     }
6782 });
6783
6784
6785 Roo.form.Action.Load = function(form, options){
6786     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6787     this.reader = this.form.reader;
6788 };
6789
6790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6791     type : 'load',
6792
6793     run : function(){
6794         
6795         Roo.Ajax.request(Roo.apply(
6796                 this.createCallback(), {
6797                     method:this.getMethod(),
6798                     url:this.getUrl(false),
6799                     params:this.getParams()
6800         }));
6801     },
6802
6803     success : function(response){
6804         
6805         var result = this.processResponse(response);
6806         if(result === true || !result.success || !result.data){
6807             this.failureType = Roo.form.Action.LOAD_FAILURE;
6808             this.form.afterAction(this, false);
6809             return;
6810         }
6811         this.form.clearInvalid();
6812         this.form.setValues(result.data);
6813         this.form.afterAction(this, true);
6814     },
6815
6816     handleResponse : function(response){
6817         if(this.form.reader){
6818             var rs = this.form.reader.read(response);
6819             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6820             return {
6821                 success : rs.success,
6822                 data : data
6823             };
6824         }
6825         return Roo.decode(response.responseText);
6826     }
6827 });
6828
6829 Roo.form.Action.ACTION_TYPES = {
6830     'load' : Roo.form.Action.Load,
6831     'submit' : Roo.form.Action.Submit
6832 };/*
6833  * - LGPL
6834  *
6835  * form
6836  * 
6837  */
6838
6839 /**
6840  * @class Roo.bootstrap.Form
6841  * @extends Roo.bootstrap.Component
6842  * Bootstrap Form class
6843  * @cfg {String} method  GET | POST (default POST)
6844  * @cfg {String} labelAlign top | left (default top)
6845  * @cfg {String} align left  | right - for navbars
6846  * @cfg {Boolean} loadMask load mask when submit (default true)
6847
6848  * 
6849  * @constructor
6850  * Create a new Form
6851  * @param {Object} config The config object
6852  */
6853
6854
6855 Roo.bootstrap.Form = function(config){
6856     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6857     this.addEvents({
6858         /**
6859          * @event clientvalidation
6860          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6861          * @param {Form} this
6862          * @param {Boolean} valid true if the form has passed client-side validation
6863          */
6864         clientvalidation: true,
6865         /**
6866          * @event beforeaction
6867          * Fires before any action is performed. Return false to cancel the action.
6868          * @param {Form} this
6869          * @param {Action} action The action to be performed
6870          */
6871         beforeaction: true,
6872         /**
6873          * @event actionfailed
6874          * Fires when an action fails.
6875          * @param {Form} this
6876          * @param {Action} action The action that failed
6877          */
6878         actionfailed : true,
6879         /**
6880          * @event actioncomplete
6881          * Fires when an action is completed.
6882          * @param {Form} this
6883          * @param {Action} action The action that completed
6884          */
6885         actioncomplete : true
6886     });
6887     
6888 };
6889
6890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6891       
6892      /**
6893      * @cfg {String} method
6894      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6895      */
6896     method : 'POST',
6897     /**
6898      * @cfg {String} url
6899      * The URL to use for form actions if one isn't supplied in the action options.
6900      */
6901     /**
6902      * @cfg {Boolean} fileUpload
6903      * Set to true if this form is a file upload.
6904      */
6905      
6906     /**
6907      * @cfg {Object} baseParams
6908      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6909      */
6910       
6911     /**
6912      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6913      */
6914     timeout: 30,
6915     /**
6916      * @cfg {Sting} align (left|right) for navbar forms
6917      */
6918     align : 'left',
6919
6920     // private
6921     activeAction : null,
6922  
6923     /**
6924      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6925      * element by passing it or its id or mask the form itself by passing in true.
6926      * @type Mixed
6927      */
6928     waitMsgTarget : false,
6929     
6930     loadMask : true,
6931     
6932     getAutoCreate : function(){
6933         
6934         var cfg = {
6935             tag: 'form',
6936             method : this.method || 'POST',
6937             id : this.id || Roo.id(),
6938             cls : ''
6939         }
6940         if (this.parent().xtype.match(/^Nav/)) {
6941             cfg.cls = 'navbar-form navbar-' + this.align;
6942             
6943         }
6944         
6945         if (this.labelAlign == 'left' ) {
6946             cfg.cls += ' form-horizontal';
6947         }
6948         
6949         
6950         return cfg;
6951     },
6952     initEvents : function()
6953     {
6954         this.el.on('submit', this.onSubmit, this);
6955         // this was added as random key presses on the form where triggering form submit.
6956         this.el.on('keypress', function(e) {
6957             if (e.getCharCode() != 13) {
6958                 return true;
6959             }
6960             // we might need to allow it for textareas.. and some other items.
6961             // check e.getTarget().
6962             
6963             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6964                 return true;
6965             }
6966         
6967             Roo.log("keypress blocked");
6968             
6969             e.preventDefault();
6970             return false;
6971         });
6972         
6973     },
6974     // private
6975     onSubmit : function(e){
6976         e.stopEvent();
6977     },
6978     
6979      /**
6980      * Returns true if client-side validation on the form is successful.
6981      * @return Boolean
6982      */
6983     isValid : function(){
6984         var items = this.getItems();
6985         var valid = true;
6986         items.each(function(f){
6987            if(!f.validate()){
6988                valid = false;
6989                
6990            }
6991         });
6992         return valid;
6993     },
6994     /**
6995      * Returns true if any fields in this form have changed since their original load.
6996      * @return Boolean
6997      */
6998     isDirty : function(){
6999         var dirty = false;
7000         var items = this.getItems();
7001         items.each(function(f){
7002            if(f.isDirty()){
7003                dirty = true;
7004                return false;
7005            }
7006            return true;
7007         });
7008         return dirty;
7009     },
7010      /**
7011      * Performs a predefined action (submit or load) or custom actions you define on this form.
7012      * @param {String} actionName The name of the action type
7013      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7014      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7015      * accept other config options):
7016      * <pre>
7017 Property          Type             Description
7018 ----------------  ---------------  ----------------------------------------------------------------------------------
7019 url               String           The url for the action (defaults to the form's url)
7020 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7021 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7022 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7023                                    validate the form on the client (defaults to false)
7024      * </pre>
7025      * @return {BasicForm} this
7026      */
7027     doAction : function(action, options){
7028         if(typeof action == 'string'){
7029             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7030         }
7031         if(this.fireEvent('beforeaction', this, action) !== false){
7032             this.beforeAction(action);
7033             action.run.defer(100, action);
7034         }
7035         return this;
7036     },
7037     
7038     // private
7039     beforeAction : function(action){
7040         var o = action.options;
7041         
7042         if(this.loadMask){
7043             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7044         }
7045         // not really supported yet.. ??
7046         
7047         //if(this.waitMsgTarget === true){
7048         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7049         //}else if(this.waitMsgTarget){
7050         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7051         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7052         //}else {
7053         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7054        // }
7055          
7056     },
7057
7058     // private
7059     afterAction : function(action, success){
7060         this.activeAction = null;
7061         var o = action.options;
7062         
7063         //if(this.waitMsgTarget === true){
7064             this.el.unmask();
7065         //}else if(this.waitMsgTarget){
7066         //    this.waitMsgTarget.unmask();
7067         //}else{
7068         //    Roo.MessageBox.updateProgress(1);
7069         //    Roo.MessageBox.hide();
7070        // }
7071         // 
7072         if(success){
7073             if(o.reset){
7074                 this.reset();
7075             }
7076             Roo.callback(o.success, o.scope, [this, action]);
7077             this.fireEvent('actioncomplete', this, action);
7078             
7079         }else{
7080             
7081             // failure condition..
7082             // we have a scenario where updates need confirming.
7083             // eg. if a locking scenario exists..
7084             // we look for { errors : { needs_confirm : true }} in the response.
7085             if (
7086                 (typeof(action.result) != 'undefined')  &&
7087                 (typeof(action.result.errors) != 'undefined')  &&
7088                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7089            ){
7090                 var _t = this;
7091                 Roo.log("not supported yet");
7092                  /*
7093                 
7094                 Roo.MessageBox.confirm(
7095                     "Change requires confirmation",
7096                     action.result.errorMsg,
7097                     function(r) {
7098                         if (r != 'yes') {
7099                             return;
7100                         }
7101                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7102                     }
7103                     
7104                 );
7105                 */
7106                 
7107                 
7108                 return;
7109             }
7110             
7111             Roo.callback(o.failure, o.scope, [this, action]);
7112             // show an error message if no failed handler is set..
7113             if (!this.hasListener('actionfailed')) {
7114                 Roo.log("need to add dialog support");
7115                 /*
7116                 Roo.MessageBox.alert("Error",
7117                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7118                         action.result.errorMsg :
7119                         "Saving Failed, please check your entries or try again"
7120                 );
7121                 */
7122             }
7123             
7124             this.fireEvent('actionfailed', this, action);
7125         }
7126         
7127     },
7128     /**
7129      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7130      * @param {String} id The value to search for
7131      * @return Field
7132      */
7133     findField : function(id){
7134         var items = this.getItems();
7135         var field = items.get(id);
7136         if(!field){
7137              items.each(function(f){
7138                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7139                     field = f;
7140                     return false;
7141                 }
7142                 return true;
7143             });
7144         }
7145         return field || null;
7146     },
7147      /**
7148      * Mark fields in this form invalid in bulk.
7149      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7150      * @return {BasicForm} this
7151      */
7152     markInvalid : function(errors){
7153         if(errors instanceof Array){
7154             for(var i = 0, len = errors.length; i < len; i++){
7155                 var fieldError = errors[i];
7156                 var f = this.findField(fieldError.id);
7157                 if(f){
7158                     f.markInvalid(fieldError.msg);
7159                 }
7160             }
7161         }else{
7162             var field, id;
7163             for(id in errors){
7164                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7165                     field.markInvalid(errors[id]);
7166                 }
7167             }
7168         }
7169         //Roo.each(this.childForms || [], function (f) {
7170         //    f.markInvalid(errors);
7171         //});
7172         
7173         return this;
7174     },
7175
7176     /**
7177      * Set values for fields in this form in bulk.
7178      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7179      * @return {BasicForm} this
7180      */
7181     setValues : function(values){
7182         if(values instanceof Array){ // array of objects
7183             for(var i = 0, len = values.length; i < len; i++){
7184                 var v = values[i];
7185                 var f = this.findField(v.id);
7186                 if(f){
7187                     f.setValue(v.value);
7188                     if(this.trackResetOnLoad){
7189                         f.originalValue = f.getValue();
7190                     }
7191                 }
7192             }
7193         }else{ // object hash
7194             var field, id;
7195             for(id in values){
7196                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7197                     
7198                     if (field.setFromData && 
7199                         field.valueField && 
7200                         field.displayField &&
7201                         // combos' with local stores can 
7202                         // be queried via setValue()
7203                         // to set their value..
7204                         (field.store && !field.store.isLocal)
7205                         ) {
7206                         // it's a combo
7207                         var sd = { };
7208                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7209                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7210                         field.setFromData(sd);
7211                         
7212                     } else {
7213                         field.setValue(values[id]);
7214                     }
7215                     
7216                     
7217                     if(this.trackResetOnLoad){
7218                         field.originalValue = field.getValue();
7219                     }
7220                 }
7221             }
7222         }
7223          
7224         //Roo.each(this.childForms || [], function (f) {
7225         //    f.setValues(values);
7226         //});
7227                 
7228         return this;
7229     },
7230
7231     /**
7232      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7233      * they are returned as an array.
7234      * @param {Boolean} asString
7235      * @return {Object}
7236      */
7237     getValues : function(asString){
7238         //if (this.childForms) {
7239             // copy values from the child forms
7240         //    Roo.each(this.childForms, function (f) {
7241         //        this.setValues(f.getValues());
7242         //    }, this);
7243         //}
7244         
7245         
7246         
7247         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7248         if(asString === true){
7249             return fs;
7250         }
7251         return Roo.urlDecode(fs);
7252     },
7253     
7254     /**
7255      * Returns the fields in this form as an object with key/value pairs. 
7256      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7257      * @return {Object}
7258      */
7259     getFieldValues : function(with_hidden)
7260     {
7261         var items = this.getItems();
7262         var ret = {};
7263         items.each(function(f){
7264             if (!f.getName()) {
7265                 return;
7266             }
7267             var v = f.getValue();
7268             if (f.inputType =='radio') {
7269                 if (typeof(ret[f.getName()]) == 'undefined') {
7270                     ret[f.getName()] = ''; // empty..
7271                 }
7272                 
7273                 if (!f.el.dom.checked) {
7274                     return;
7275                     
7276                 }
7277                 v = f.el.dom.value;
7278                 
7279             }
7280             
7281             // not sure if this supported any more..
7282             if ((typeof(v) == 'object') && f.getRawValue) {
7283                 v = f.getRawValue() ; // dates..
7284             }
7285             // combo boxes where name != hiddenName...
7286             if (f.name != f.getName()) {
7287                 ret[f.name] = f.getRawValue();
7288             }
7289             ret[f.getName()] = v;
7290         });
7291         
7292         return ret;
7293     },
7294
7295     /**
7296      * Clears all invalid messages in this form.
7297      * @return {BasicForm} this
7298      */
7299     clearInvalid : function(){
7300         var items = this.getItems();
7301         
7302         items.each(function(f){
7303            f.clearInvalid();
7304         });
7305         
7306         
7307         
7308         return this;
7309     },
7310
7311     /**
7312      * Resets this form.
7313      * @return {BasicForm} this
7314      */
7315     reset : function(){
7316         var items = this.getItems();
7317         items.each(function(f){
7318             f.reset();
7319         });
7320         
7321         Roo.each(this.childForms || [], function (f) {
7322             f.reset();
7323         });
7324        
7325         
7326         return this;
7327     },
7328     getItems : function()
7329     {
7330         var r=new Roo.util.MixedCollection(false, function(o){
7331             return o.id || (o.id = Roo.id());
7332         });
7333         var iter = function(el) {
7334             if (el.inputEl) {
7335                 r.add(el);
7336             }
7337             if (!el.items) {
7338                 return;
7339             }
7340             Roo.each(el.items,function(e) {
7341                 iter(e);
7342             });
7343             
7344             
7345         };
7346         
7347         iter(this);
7348         return r;
7349         
7350         
7351         
7352         
7353     }
7354     
7355 });
7356
7357  
7358 /*
7359  * Based on:
7360  * Ext JS Library 1.1.1
7361  * Copyright(c) 2006-2007, Ext JS, LLC.
7362  *
7363  * Originally Released Under LGPL - original licence link has changed is not relivant.
7364  *
7365  * Fork - LGPL
7366  * <script type="text/javascript">
7367  */
7368 /**
7369  * @class Roo.form.VTypes
7370  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7371  * @singleton
7372  */
7373 Roo.form.VTypes = function(){
7374     // closure these in so they are only created once.
7375     var alpha = /^[a-zA-Z_]+$/;
7376     var alphanum = /^[a-zA-Z0-9_]+$/;
7377     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7378     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7379
7380     // All these messages and functions are configurable
7381     return {
7382         /**
7383          * The function used to validate email addresses
7384          * @param {String} value The email address
7385          */
7386         'email' : function(v){
7387             return email.test(v);
7388         },
7389         /**
7390          * The error text to display when the email validation function returns false
7391          * @type String
7392          */
7393         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7394         /**
7395          * The keystroke filter mask to be applied on email input
7396          * @type RegExp
7397          */
7398         'emailMask' : /[a-z0-9_\.\-@]/i,
7399
7400         /**
7401          * The function used to validate URLs
7402          * @param {String} value The URL
7403          */
7404         'url' : function(v){
7405             return url.test(v);
7406         },
7407         /**
7408          * The error text to display when the url validation function returns false
7409          * @type String
7410          */
7411         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7412         
7413         /**
7414          * The function used to validate alpha values
7415          * @param {String} value The value
7416          */
7417         'alpha' : function(v){
7418             return alpha.test(v);
7419         },
7420         /**
7421          * The error text to display when the alpha validation function returns false
7422          * @type String
7423          */
7424         'alphaText' : 'This field should only contain letters and _',
7425         /**
7426          * The keystroke filter mask to be applied on alpha input
7427          * @type RegExp
7428          */
7429         'alphaMask' : /[a-z_]/i,
7430
7431         /**
7432          * The function used to validate alphanumeric values
7433          * @param {String} value The value
7434          */
7435         'alphanum' : function(v){
7436             return alphanum.test(v);
7437         },
7438         /**
7439          * The error text to display when the alphanumeric validation function returns false
7440          * @type String
7441          */
7442         'alphanumText' : 'This field should only contain letters, numbers and _',
7443         /**
7444          * The keystroke filter mask to be applied on alphanumeric input
7445          * @type RegExp
7446          */
7447         'alphanumMask' : /[a-z0-9_]/i
7448     };
7449 }();/*
7450  * - LGPL
7451  *
7452  * Input
7453  * 
7454  */
7455
7456 /**
7457  * @class Roo.bootstrap.Input
7458  * @extends Roo.bootstrap.Component
7459  * Bootstrap Input class
7460  * @cfg {Boolean} disabled is it disabled
7461  * @cfg {String} fieldLabel - the label associated
7462  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7463  * @cfg {String} name name of the input
7464  * @cfg {string} fieldLabel - the label associated
7465  * @cfg {string}  inputType - input / file submit ...
7466  * @cfg {string} placeholder - placeholder to put in text.
7467  * @cfg {string}  before - input group add on before
7468  * @cfg {string} after - input group add on after
7469  * @cfg {string} size - (lg|sm) or leave empty..
7470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7472  * @cfg {Number} md colspan out of 12 for computer-sized screens
7473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7474  * @cfg {string} value default value of the input
7475  * @cfg {Number} labelWidth set the width of label (0-12)
7476  * @cfg {String} labelAlign (top|left)
7477  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7478  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7479
7480  * @cfg {String} align (left|center|right) Default left
7481  * @cfg {Boolean} forceFeedback (true|false) Default false
7482  * 
7483  * 
7484  * 
7485  * 
7486  * @constructor
7487  * Create a new Input
7488  * @param {Object} config The config object
7489  */
7490
7491 Roo.bootstrap.Input = function(config){
7492     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7493    
7494         this.addEvents({
7495             /**
7496              * @event focus
7497              * Fires when this field receives input focus.
7498              * @param {Roo.form.Field} this
7499              */
7500             focus : true,
7501             /**
7502              * @event blur
7503              * Fires when this field loses input focus.
7504              * @param {Roo.form.Field} this
7505              */
7506             blur : true,
7507             /**
7508              * @event specialkey
7509              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7510              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7511              * @param {Roo.form.Field} this
7512              * @param {Roo.EventObject} e The event object
7513              */
7514             specialkey : true,
7515             /**
7516              * @event change
7517              * Fires just before the field blurs if the field value has changed.
7518              * @param {Roo.form.Field} this
7519              * @param {Mixed} newValue The new value
7520              * @param {Mixed} oldValue The original value
7521              */
7522             change : true,
7523             /**
7524              * @event invalid
7525              * Fires after the field has been marked as invalid.
7526              * @param {Roo.form.Field} this
7527              * @param {String} msg The validation message
7528              */
7529             invalid : true,
7530             /**
7531              * @event valid
7532              * Fires after the field has been validated with no errors.
7533              * @param {Roo.form.Field} this
7534              */
7535             valid : true,
7536              /**
7537              * @event keyup
7538              * Fires after the key up
7539              * @param {Roo.form.Field} this
7540              * @param {Roo.EventObject}  e The event Object
7541              */
7542             keyup : true
7543         });
7544 };
7545
7546 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7547      /**
7548      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7549       automatic validation (defaults to "keyup").
7550      */
7551     validationEvent : "keyup",
7552      /**
7553      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7554      */
7555     validateOnBlur : true,
7556     /**
7557      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7558      */
7559     validationDelay : 250,
7560      /**
7561      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7562      */
7563     focusClass : "x-form-focus",  // not needed???
7564     
7565        
7566     /**
7567      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7568      */
7569     invalidClass : "has-warning",
7570     
7571     /**
7572      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7573      */
7574     validClass : "has-success",
7575     
7576     /**
7577      * @cfg {Boolean} hasFeedback (true|false) default true
7578      */
7579     hasFeedback : true,
7580     
7581     /**
7582      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7583      */
7584     invalidFeedbackClass : "glyphicon-warning-sign",
7585     
7586     /**
7587      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7588      */
7589     validFeedbackClass : "glyphicon-ok",
7590     
7591     /**
7592      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7593      */
7594     selectOnFocus : false,
7595     
7596      /**
7597      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7598      */
7599     maskRe : null,
7600        /**
7601      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7602      */
7603     vtype : null,
7604     
7605       /**
7606      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7607      */
7608     disableKeyFilter : false,
7609     
7610        /**
7611      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7612      */
7613     disabled : false,
7614      /**
7615      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7616      */
7617     allowBlank : true,
7618     /**
7619      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7620      */
7621     blankText : "This field is required",
7622     
7623      /**
7624      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7625      */
7626     minLength : 0,
7627     /**
7628      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7629      */
7630     maxLength : Number.MAX_VALUE,
7631     /**
7632      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7633      */
7634     minLengthText : "The minimum length for this field is {0}",
7635     /**
7636      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7637      */
7638     maxLengthText : "The maximum length for this field is {0}",
7639   
7640     
7641     /**
7642      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7643      * If available, this function will be called only after the basic validators all return true, and will be passed the
7644      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7645      */
7646     validator : null,
7647     /**
7648      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7649      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7650      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7651      */
7652     regex : null,
7653     /**
7654      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7655      */
7656     regexText : "",
7657     
7658     autocomplete: false,
7659     
7660     
7661     fieldLabel : '',
7662     inputType : 'text',
7663     
7664     name : false,
7665     placeholder: false,
7666     before : false,
7667     after : false,
7668     size : false,
7669     hasFocus : false,
7670     preventMark: false,
7671     isFormField : true,
7672     value : '',
7673     labelWidth : 2,
7674     labelAlign : false,
7675     readOnly : false,
7676     align : false,
7677     formatedValue : false,
7678     forceFeedback : false,
7679     
7680     parentLabelAlign : function()
7681     {
7682         var parent = this;
7683         while (parent.parent()) {
7684             parent = parent.parent();
7685             if (typeof(parent.labelAlign) !='undefined') {
7686                 return parent.labelAlign;
7687             }
7688         }
7689         return 'left';
7690         
7691     },
7692     
7693     getAutoCreate : function(){
7694         
7695         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7696         
7697         var id = Roo.id();
7698         
7699         var cfg = {};
7700         
7701         if(this.inputType != 'hidden'){
7702             cfg.cls = 'form-group' //input-group
7703         }
7704         
7705         var input =  {
7706             tag: 'input',
7707             id : id,
7708             type : this.inputType,
7709             value : this.value,
7710             cls : 'form-control',
7711             placeholder : this.placeholder || '',
7712             autocomplete : this.autocomplete || 'new-password'
7713         };
7714         
7715         
7716         if(this.align){
7717             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7718         }
7719         
7720         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7721             input.maxLength = this.maxLength;
7722         }
7723         
7724         if (this.disabled) {
7725             input.disabled=true;
7726         }
7727         
7728         if (this.readOnly) {
7729             input.readonly=true;
7730         }
7731         
7732         if (this.name) {
7733             input.name = this.name;
7734         }
7735         if (this.size) {
7736             input.cls += ' input-' + this.size;
7737         }
7738         var settings=this;
7739         ['xs','sm','md','lg'].map(function(size){
7740             if (settings[size]) {
7741                 cfg.cls += ' col-' + size + '-' + settings[size];
7742             }
7743         });
7744         
7745         var inputblock = input;
7746         
7747         var feedback = {
7748             tag: 'span',
7749             cls: 'glyphicon form-control-feedback'
7750         };
7751             
7752         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7753             
7754             inputblock = {
7755                 cls : 'has-feedback',
7756                 cn :  [
7757                     input,
7758                     feedback
7759                 ] 
7760             };  
7761         }
7762         
7763         if (this.before || this.after) {
7764             
7765             inputblock = {
7766                 cls : 'input-group',
7767                 cn :  [] 
7768             };
7769             
7770             if (this.before && typeof(this.before) == 'string') {
7771                 
7772                 inputblock.cn.push({
7773                     tag :'span',
7774                     cls : 'roo-input-before input-group-addon',
7775                     html : this.before
7776                 });
7777             }
7778             if (this.before && typeof(this.before) == 'object') {
7779                 this.before = Roo.factory(this.before);
7780                 Roo.log(this.before);
7781                 inputblock.cn.push({
7782                     tag :'span',
7783                     cls : 'roo-input-before input-group-' +
7784                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7785                 });
7786             }
7787             
7788             inputblock.cn.push(input);
7789             
7790             if (this.after && typeof(this.after) == 'string') {
7791                 inputblock.cn.push({
7792                     tag :'span',
7793                     cls : 'roo-input-after input-group-addon',
7794                     html : this.after
7795                 });
7796             }
7797             if (this.after && typeof(this.after) == 'object') {
7798                 this.after = Roo.factory(this.after);
7799                 Roo.log(this.after);
7800                 inputblock.cn.push({
7801                     tag :'span',
7802                     cls : 'roo-input-after input-group-' +
7803                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7804                 });
7805             }
7806             
7807             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7808                 inputblock.cls += ' has-feedback';
7809                 inputblock.cn.push(feedback);
7810             }
7811         };
7812         
7813         if (align ==='left' && this.fieldLabel.length) {
7814                 Roo.log("left and has label");
7815                 cfg.cn = [
7816                     
7817                     {
7818                         tag: 'label',
7819                         'for' :  id,
7820                         cls : 'control-label col-sm-' + this.labelWidth,
7821                         html : this.fieldLabel
7822                         
7823                     },
7824                     {
7825                         cls : "col-sm-" + (12 - this.labelWidth), 
7826                         cn: [
7827                             inputblock
7828                         ]
7829                     }
7830                     
7831                 ];
7832         } else if ( this.fieldLabel.length) {
7833                 Roo.log(" label");
7834                  cfg.cn = [
7835                    
7836                     {
7837                         tag: 'label',
7838                         //cls : 'input-group-addon',
7839                         html : this.fieldLabel
7840                         
7841                     },
7842                     
7843                     inputblock
7844                     
7845                 ];
7846
7847         } else {
7848             
7849                 Roo.log(" no label && no align");
7850                 cfg.cn = [
7851                     
7852                         inputblock
7853                     
7854                 ];
7855                 
7856                 
7857         };
7858         Roo.log('input-parentType: ' + this.parentType);
7859         
7860         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7861            cfg.cls += ' navbar-form';
7862            Roo.log(cfg);
7863         }
7864         
7865         return cfg;
7866         
7867     },
7868     /**
7869      * return the real input element.
7870      */
7871     inputEl: function ()
7872     {
7873         return this.el.select('input.form-control',true).first();
7874     },
7875     
7876     tooltipEl : function()
7877     {
7878         return this.inputEl();
7879     },
7880     
7881     setDisabled : function(v)
7882     {
7883         var i  = this.inputEl().dom;
7884         if (!v) {
7885             i.removeAttribute('disabled');
7886             return;
7887             
7888         }
7889         i.setAttribute('disabled','true');
7890     },
7891     initEvents : function()
7892     {
7893           
7894         this.inputEl().on("keydown" , this.fireKey,  this);
7895         this.inputEl().on("focus", this.onFocus,  this);
7896         this.inputEl().on("blur", this.onBlur,  this);
7897         
7898         this.inputEl().relayEvent('keyup', this);
7899  
7900         // reference to original value for reset
7901         this.originalValue = this.getValue();
7902         //Roo.form.TextField.superclass.initEvents.call(this);
7903         if(this.validationEvent == 'keyup'){
7904             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7905             this.inputEl().on('keyup', this.filterValidation, this);
7906         }
7907         else if(this.validationEvent !== false){
7908             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7909         }
7910         
7911         if(this.selectOnFocus){
7912             this.on("focus", this.preFocus, this);
7913             
7914         }
7915         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7916             this.inputEl().on("keypress", this.filterKeys, this);
7917         }
7918        /* if(this.grow){
7919             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7920             this.el.on("click", this.autoSize,  this);
7921         }
7922         */
7923         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7924             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7925         }
7926         
7927         if (typeof(this.before) == 'object') {
7928             this.before.render(this.el.select('.roo-input-before',true).first());
7929         }
7930         if (typeof(this.after) == 'object') {
7931             this.after.render(this.el.select('.roo-input-after',true).first());
7932         }
7933         
7934         
7935     },
7936     filterValidation : function(e){
7937         if(!e.isNavKeyPress()){
7938             this.validationTask.delay(this.validationDelay);
7939         }
7940     },
7941      /**
7942      * Validates the field value
7943      * @return {Boolean} True if the value is valid, else false
7944      */
7945     validate : function(){
7946         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7947         if(this.disabled || this.validateValue(this.getRawValue())){
7948             this.markValid();
7949             return true;
7950         }
7951         
7952         this.markInvalid();
7953         return false;
7954     },
7955     
7956     
7957     /**
7958      * Validates a value according to the field's validation rules and marks the field as invalid
7959      * if the validation fails
7960      * @param {Mixed} value The value to validate
7961      * @return {Boolean} True if the value is valid, else false
7962      */
7963     validateValue : function(value){
7964         if(value.length < 1)  { // if it's blank
7965             if(this.allowBlank){
7966                 return true;
7967             }
7968             return false;
7969         }
7970         
7971         if(value.length < this.minLength){
7972             return false;
7973         }
7974         if(value.length > this.maxLength){
7975             return false;
7976         }
7977         if(this.vtype){
7978             var vt = Roo.form.VTypes;
7979             if(!vt[this.vtype](value, this)){
7980                 return false;
7981             }
7982         }
7983         if(typeof this.validator == "function"){
7984             var msg = this.validator(value);
7985             if(msg !== true){
7986                 return false;
7987             }
7988         }
7989         
7990         if(this.regex && !this.regex.test(value)){
7991             return false;
7992         }
7993         
7994         return true;
7995     },
7996
7997     
7998     
7999      // private
8000     fireKey : function(e){
8001         //Roo.log('field ' + e.getKey());
8002         if(e.isNavKeyPress()){
8003             this.fireEvent("specialkey", this, e);
8004         }
8005     },
8006     focus : function (selectText){
8007         if(this.rendered){
8008             this.inputEl().focus();
8009             if(selectText === true){
8010                 this.inputEl().dom.select();
8011             }
8012         }
8013         return this;
8014     } ,
8015     
8016     onFocus : function(){
8017         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8018            // this.el.addClass(this.focusClass);
8019         }
8020         if(!this.hasFocus){
8021             this.hasFocus = true;
8022             this.startValue = this.getValue();
8023             this.fireEvent("focus", this);
8024         }
8025     },
8026     
8027     beforeBlur : Roo.emptyFn,
8028
8029     
8030     // private
8031     onBlur : function(){
8032         this.beforeBlur();
8033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8034             //this.el.removeClass(this.focusClass);
8035         }
8036         this.hasFocus = false;
8037         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8038             this.validate();
8039         }
8040         var v = this.getValue();
8041         if(String(v) !== String(this.startValue)){
8042             this.fireEvent('change', this, v, this.startValue);
8043         }
8044         this.fireEvent("blur", this);
8045     },
8046     
8047     /**
8048      * Resets the current field value to the originally loaded value and clears any validation messages
8049      */
8050     reset : function(){
8051         this.setValue(this.originalValue);
8052         this.validate();
8053     },
8054      /**
8055      * Returns the name of the field
8056      * @return {Mixed} name The name field
8057      */
8058     getName: function(){
8059         return this.name;
8060     },
8061      /**
8062      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8063      * @return {Mixed} value The field value
8064      */
8065     getValue : function(){
8066         
8067         var v = this.inputEl().getValue();
8068         
8069         return v;
8070     },
8071     /**
8072      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8073      * @return {Mixed} value The field value
8074      */
8075     getRawValue : function(){
8076         var v = this.inputEl().getValue();
8077         
8078         return v;
8079     },
8080     
8081     /**
8082      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8083      * @param {Mixed} value The value to set
8084      */
8085     setRawValue : function(v){
8086         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8087     },
8088     
8089     selectText : function(start, end){
8090         var v = this.getRawValue();
8091         if(v.length > 0){
8092             start = start === undefined ? 0 : start;
8093             end = end === undefined ? v.length : end;
8094             var d = this.inputEl().dom;
8095             if(d.setSelectionRange){
8096                 d.setSelectionRange(start, end);
8097             }else if(d.createTextRange){
8098                 var range = d.createTextRange();
8099                 range.moveStart("character", start);
8100                 range.moveEnd("character", v.length-end);
8101                 range.select();
8102             }
8103         }
8104     },
8105     
8106     /**
8107      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8108      * @param {Mixed} value The value to set
8109      */
8110     setValue : function(v){
8111         this.value = v;
8112         if(this.rendered){
8113             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8114             this.validate();
8115         }
8116     },
8117     
8118     /*
8119     processValue : function(value){
8120         if(this.stripCharsRe){
8121             var newValue = value.replace(this.stripCharsRe, '');
8122             if(newValue !== value){
8123                 this.setRawValue(newValue);
8124                 return newValue;
8125             }
8126         }
8127         return value;
8128     },
8129   */
8130     preFocus : function(){
8131         
8132         if(this.selectOnFocus){
8133             this.inputEl().dom.select();
8134         }
8135     },
8136     filterKeys : function(e){
8137         var k = e.getKey();
8138         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8139             return;
8140         }
8141         var c = e.getCharCode(), cc = String.fromCharCode(c);
8142         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8143             return;
8144         }
8145         if(!this.maskRe.test(cc)){
8146             e.stopEvent();
8147         }
8148     },
8149      /**
8150      * Clear any invalid styles/messages for this field
8151      */
8152     clearInvalid : function(){
8153         
8154         if(!this.el || this.preventMark){ // not rendered
8155             return;
8156         }
8157         this.el.removeClass(this.invalidClass);
8158         
8159         this.fireEvent('valid', this);
8160     },
8161     
8162      /**
8163      * Mark this field as valid
8164      */
8165     markValid : function(){
8166         if(!this.el  || this.preventMark){ // not rendered
8167             return;
8168         }
8169         
8170         this.el.removeClass([this.invalidClass, this.validClass]);
8171         
8172         var feedback = this.el.select('.form-control-feedback', true).first();
8173             
8174         if(feedback){
8175             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8176         }
8177
8178         if(this.disabled || this.allowBlank){
8179             return;
8180         }
8181         
8182         this.el.addClass(this.validClass);
8183         
8184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8185             
8186             var feedback = this.el.select('.form-control-feedback', true).first();
8187             
8188             if(feedback){
8189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8191             }
8192             
8193         }
8194         
8195         this.fireEvent('valid', this);
8196     },
8197     
8198      /**
8199      * Mark this field as invalid
8200      * @param {String} msg The validation message
8201      */
8202     markInvalid : function(msg){
8203         if(!this.el  || this.preventMark){ // not rendered
8204             return;
8205         }
8206         
8207         this.el.removeClass([this.invalidClass, this.validClass]);
8208         
8209         var feedback = this.el.select('.form-control-feedback', true).first();
8210             
8211         if(feedback){
8212             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8213         }
8214
8215         if(this.disabled || this.allowBlank){
8216             return;
8217         }
8218         
8219         this.el.addClass(this.invalidClass);
8220         
8221         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8222             
8223             var feedback = this.el.select('.form-control-feedback', true).first();
8224             
8225             if(feedback){
8226                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8227                 
8228                 if(this.getValue().length || this.forceFeedback){
8229                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8230                 }
8231                 
8232             }
8233             
8234         }
8235         
8236         this.fireEvent('invalid', this, msg);
8237     },
8238     // private
8239     SafariOnKeyDown : function(event)
8240     {
8241         // this is a workaround for a password hang bug on chrome/ webkit.
8242         
8243         var isSelectAll = false;
8244         
8245         if(this.inputEl().dom.selectionEnd > 0){
8246             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8247         }
8248         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8249             event.preventDefault();
8250             this.setValue('');
8251             return;
8252         }
8253         
8254         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8255             
8256             event.preventDefault();
8257             // this is very hacky as keydown always get's upper case.
8258             //
8259             var cc = String.fromCharCode(event.getCharCode());
8260             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8261             
8262         }
8263     },
8264     adjustWidth : function(tag, w){
8265         tag = tag.toLowerCase();
8266         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8267             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8268                 if(tag == 'input'){
8269                     return w + 2;
8270                 }
8271                 if(tag == 'textarea'){
8272                     return w-2;
8273                 }
8274             }else if(Roo.isOpera){
8275                 if(tag == 'input'){
8276                     return w + 2;
8277                 }
8278                 if(tag == 'textarea'){
8279                     return w-2;
8280                 }
8281             }
8282         }
8283         return w;
8284     }
8285     
8286 });
8287
8288  
8289 /*
8290  * - LGPL
8291  *
8292  * Input
8293  * 
8294  */
8295
8296 /**
8297  * @class Roo.bootstrap.TextArea
8298  * @extends Roo.bootstrap.Input
8299  * Bootstrap TextArea class
8300  * @cfg {Number} cols Specifies the visible width of a text area
8301  * @cfg {Number} rows Specifies the visible number of lines in a text area
8302  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8303  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8304  * @cfg {string} html text
8305  * 
8306  * @constructor
8307  * Create a new TextArea
8308  * @param {Object} config The config object
8309  */
8310
8311 Roo.bootstrap.TextArea = function(config){
8312     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8313    
8314 };
8315
8316 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8317      
8318     cols : false,
8319     rows : 5,
8320     readOnly : false,
8321     warp : 'soft',
8322     resize : false,
8323     value: false,
8324     html: false,
8325     
8326     getAutoCreate : function(){
8327         
8328         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8329         
8330         var id = Roo.id();
8331         
8332         var cfg = {};
8333         
8334         var input =  {
8335             tag: 'textarea',
8336             id : id,
8337             warp : this.warp,
8338             rows : this.rows,
8339             value : this.value || '',
8340             html: this.html || '',
8341             cls : 'form-control',
8342             placeholder : this.placeholder || '' 
8343             
8344         };
8345         
8346         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8347             input.maxLength = this.maxLength;
8348         }
8349         
8350         if(this.resize){
8351             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8352         }
8353         
8354         if(this.cols){
8355             input.cols = this.cols;
8356         }
8357         
8358         if (this.readOnly) {
8359             input.readonly = true;
8360         }
8361         
8362         if (this.name) {
8363             input.name = this.name;
8364         }
8365         
8366         if (this.size) {
8367             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8368         }
8369         
8370         var settings=this;
8371         ['xs','sm','md','lg'].map(function(size){
8372             if (settings[size]) {
8373                 cfg.cls += ' col-' + size + '-' + settings[size];
8374             }
8375         });
8376         
8377         var inputblock = input;
8378         
8379         if(this.hasFeedback && !this.allowBlank){
8380             
8381             var feedback = {
8382                 tag: 'span',
8383                 cls: 'glyphicon form-control-feedback'
8384             };
8385
8386             inputblock = {
8387                 cls : 'has-feedback',
8388                 cn :  [
8389                     input,
8390                     feedback
8391                 ] 
8392             };  
8393         }
8394         
8395         
8396         if (this.before || this.after) {
8397             
8398             inputblock = {
8399                 cls : 'input-group',
8400                 cn :  [] 
8401             };
8402             if (this.before) {
8403                 inputblock.cn.push({
8404                     tag :'span',
8405                     cls : 'input-group-addon',
8406                     html : this.before
8407                 });
8408             }
8409             
8410             inputblock.cn.push(input);
8411             
8412             if(this.hasFeedback && !this.allowBlank){
8413                 inputblock.cls += ' has-feedback';
8414                 inputblock.cn.push(feedback);
8415             }
8416             
8417             if (this.after) {
8418                 inputblock.cn.push({
8419                     tag :'span',
8420                     cls : 'input-group-addon',
8421                     html : this.after
8422                 });
8423             }
8424             
8425         }
8426         
8427         if (align ==='left' && this.fieldLabel.length) {
8428                 Roo.log("left and has label");
8429                 cfg.cn = [
8430                     
8431                     {
8432                         tag: 'label',
8433                         'for' :  id,
8434                         cls : 'control-label col-sm-' + this.labelWidth,
8435                         html : this.fieldLabel
8436                         
8437                     },
8438                     {
8439                         cls : "col-sm-" + (12 - this.labelWidth), 
8440                         cn: [
8441                             inputblock
8442                         ]
8443                     }
8444                     
8445                 ];
8446         } else if ( this.fieldLabel.length) {
8447                 Roo.log(" label");
8448                  cfg.cn = [
8449                    
8450                     {
8451                         tag: 'label',
8452                         //cls : 'input-group-addon',
8453                         html : this.fieldLabel
8454                         
8455                     },
8456                     
8457                     inputblock
8458                     
8459                 ];
8460
8461         } else {
8462             
8463                    Roo.log(" no label && no align");
8464                 cfg.cn = [
8465                     
8466                         inputblock
8467                     
8468                 ];
8469                 
8470                 
8471         }
8472         
8473         if (this.disabled) {
8474             input.disabled=true;
8475         }
8476         
8477         return cfg;
8478         
8479     },
8480     /**
8481      * return the real textarea element.
8482      */
8483     inputEl: function ()
8484     {
8485         return this.el.select('textarea.form-control',true).first();
8486     }
8487 });
8488
8489  
8490 /*
8491  * - LGPL
8492  *
8493  * trigger field - base class for combo..
8494  * 
8495  */
8496  
8497 /**
8498  * @class Roo.bootstrap.TriggerField
8499  * @extends Roo.bootstrap.Input
8500  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8501  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8502  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8503  * for which you can provide a custom implementation.  For example:
8504  * <pre><code>
8505 var trigger = new Roo.bootstrap.TriggerField();
8506 trigger.onTriggerClick = myTriggerFn;
8507 trigger.applyTo('my-field');
8508 </code></pre>
8509  *
8510  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8511  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8512  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8513  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8514  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8515
8516  * @constructor
8517  * Create a new TriggerField.
8518  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8519  * to the base TextField)
8520  */
8521 Roo.bootstrap.TriggerField = function(config){
8522     this.mimicing = false;
8523     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8524 };
8525
8526 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8527     /**
8528      * @cfg {String} triggerClass A CSS class to apply to the trigger
8529      */
8530      /**
8531      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8532      */
8533     hideTrigger:false,
8534
8535     /**
8536      * @cfg {Boolean} removable (true|false) special filter default false
8537      */
8538     removable : false,
8539     
8540     /** @cfg {Boolean} grow @hide */
8541     /** @cfg {Number} growMin @hide */
8542     /** @cfg {Number} growMax @hide */
8543
8544     /**
8545      * @hide 
8546      * @method
8547      */
8548     autoSize: Roo.emptyFn,
8549     // private
8550     monitorTab : true,
8551     // private
8552     deferHeight : true,
8553
8554     
8555     actionMode : 'wrap',
8556     
8557     caret : false,
8558     
8559     
8560     getAutoCreate : function(){
8561        
8562         var align = this.labelAlign || this.parentLabelAlign();
8563         
8564         var id = Roo.id();
8565         
8566         var cfg = {
8567             cls: 'form-group' //input-group
8568         };
8569         
8570         
8571         var input =  {
8572             tag: 'input',
8573             id : id,
8574             type : this.inputType,
8575             cls : 'form-control',
8576             autocomplete: 'new-password',
8577             placeholder : this.placeholder || '' 
8578             
8579         };
8580         if (this.name) {
8581             input.name = this.name;
8582         }
8583         if (this.size) {
8584             input.cls += ' input-' + this.size;
8585         }
8586         
8587         if (this.disabled) {
8588             input.disabled=true;
8589         }
8590         
8591         var inputblock = input;
8592         
8593         if(this.hasFeedback && !this.allowBlank){
8594             
8595             var feedback = {
8596                 tag: 'span',
8597                 cls: 'glyphicon form-control-feedback'
8598             };
8599             
8600             if(this.removable && !this.editable && !this.tickable){
8601                 inputblock = {
8602                     cls : 'has-feedback',
8603                     cn :  [
8604                         inputblock,
8605                         {
8606                             tag: 'button',
8607                             html : 'x',
8608                             cls : 'roo-combo-removable-btn close'
8609                         },
8610                         feedback
8611                     ] 
8612                 };
8613             } else {
8614                 inputblock = {
8615                     cls : 'has-feedback',
8616                     cn :  [
8617                         inputblock,
8618                         feedback
8619                     ] 
8620                 };
8621             }
8622
8623         } else {
8624             if(this.removable && !this.editable && !this.tickable){
8625                 inputblock = {
8626                     cls : 'roo-removable',
8627                     cn :  [
8628                         inputblock,
8629                         {
8630                             tag: 'button',
8631                             html : 'x',
8632                             cls : 'roo-combo-removable-btn close'
8633                         }
8634                     ] 
8635                 };
8636             }
8637         }
8638         
8639         if (this.before || this.after) {
8640             
8641             inputblock = {
8642                 cls : 'input-group',
8643                 cn :  [] 
8644             };
8645             if (this.before) {
8646                 inputblock.cn.push({
8647                     tag :'span',
8648                     cls : 'input-group-addon',
8649                     html : this.before
8650                 });
8651             }
8652             
8653             inputblock.cn.push(input);
8654             
8655             if(this.hasFeedback && !this.allowBlank){
8656                 inputblock.cls += ' has-feedback';
8657                 inputblock.cn.push(feedback);
8658             }
8659             
8660             if (this.after) {
8661                 inputblock.cn.push({
8662                     tag :'span',
8663                     cls : 'input-group-addon',
8664                     html : this.after
8665                 });
8666             }
8667             
8668         };
8669         
8670         var box = {
8671             tag: 'div',
8672             cn: [
8673                 {
8674                     tag: 'input',
8675                     type : 'hidden',
8676                     cls: 'form-hidden-field'
8677                 },
8678                 inputblock
8679             ]
8680             
8681         };
8682         
8683         if(this.multiple){
8684             Roo.log('multiple');
8685             
8686             box = {
8687                 tag: 'div',
8688                 cn: [
8689                     {
8690                         tag: 'input',
8691                         type : 'hidden',
8692                         cls: 'form-hidden-field'
8693                     },
8694                     {
8695                         tag: 'ul',
8696                         cls: 'select2-choices',
8697                         cn:[
8698                             {
8699                                 tag: 'li',
8700                                 cls: 'select2-search-field',
8701                                 cn: [
8702
8703                                     inputblock
8704                                 ]
8705                             }
8706                         ]
8707                     }
8708                 ]
8709             }
8710         };
8711         
8712         var combobox = {
8713             cls: 'select2-container input-group',
8714             cn: [
8715                 box
8716 //                {
8717 //                    tag: 'ul',
8718 //                    cls: 'typeahead typeahead-long dropdown-menu',
8719 //                    style: 'display:none'
8720 //                }
8721             ]
8722         };
8723         
8724         if(!this.multiple && this.showToggleBtn){
8725             
8726             var caret = {
8727                         tag: 'span',
8728                         cls: 'caret'
8729              };
8730             if (this.caret != false) {
8731                 caret = {
8732                      tag: 'i',
8733                      cls: 'fa fa-' + this.caret
8734                 };
8735                 
8736             }
8737             
8738             combobox.cn.push({
8739                 tag :'span',
8740                 cls : 'input-group-addon btn dropdown-toggle',
8741                 cn : [
8742                     caret,
8743                     {
8744                         tag: 'span',
8745                         cls: 'combobox-clear',
8746                         cn  : [
8747                             {
8748                                 tag : 'i',
8749                                 cls: 'icon-remove'
8750                             }
8751                         ]
8752                     }
8753                 ]
8754
8755             })
8756         }
8757         
8758         if(this.multiple){
8759             combobox.cls += ' select2-container-multi';
8760         }
8761         
8762         if (align ==='left' && this.fieldLabel.length) {
8763             
8764                 Roo.log("left and has label");
8765                 cfg.cn = [
8766                     
8767                     {
8768                         tag: 'label',
8769                         'for' :  id,
8770                         cls : 'control-label col-sm-' + this.labelWidth,
8771                         html : this.fieldLabel
8772                         
8773                     },
8774                     {
8775                         cls : "col-sm-" + (12 - this.labelWidth), 
8776                         cn: [
8777                             combobox
8778                         ]
8779                     }
8780                     
8781                 ];
8782         } else if ( this.fieldLabel.length) {
8783                 Roo.log(" label");
8784                  cfg.cn = [
8785                    
8786                     {
8787                         tag: 'label',
8788                         //cls : 'input-group-addon',
8789                         html : this.fieldLabel
8790                         
8791                     },
8792                     
8793                     combobox
8794                     
8795                 ];
8796
8797         } else {
8798             
8799                 Roo.log(" no label && no align");
8800                 cfg = combobox
8801                      
8802                 
8803         }
8804          
8805         var settings=this;
8806         ['xs','sm','md','lg'].map(function(size){
8807             if (settings[size]) {
8808                 cfg.cls += ' col-' + size + '-' + settings[size];
8809             }
8810         });
8811         Roo.log(cfg);
8812         return cfg;
8813         
8814     },
8815     
8816     
8817     
8818     // private
8819     onResize : function(w, h){
8820 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8821 //        if(typeof w == 'number'){
8822 //            var x = w - this.trigger.getWidth();
8823 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8824 //            this.trigger.setStyle('left', x+'px');
8825 //        }
8826     },
8827
8828     // private
8829     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8830
8831     // private
8832     getResizeEl : function(){
8833         return this.inputEl();
8834     },
8835
8836     // private
8837     getPositionEl : function(){
8838         return this.inputEl();
8839     },
8840
8841     // private
8842     alignErrorIcon : function(){
8843         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8844     },
8845
8846     // private
8847     initEvents : function(){
8848         
8849         this.createList();
8850         
8851         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8852         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8853         if(!this.multiple && this.showToggleBtn){
8854             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8855             if(this.hideTrigger){
8856                 this.trigger.setDisplayed(false);
8857             }
8858             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8859         }
8860         
8861         if(this.multiple){
8862             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8863         }
8864         
8865         if(this.removable && !this.editable && !this.tickable){
8866             var close = this.closeTriggerEl();
8867             
8868             if(close){
8869                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8870                 close.on('click', this.removeBtnClick, this, close);
8871             }
8872         }
8873         
8874         //this.trigger.addClassOnOver('x-form-trigger-over');
8875         //this.trigger.addClassOnClick('x-form-trigger-click');
8876         
8877         //if(!this.width){
8878         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8879         //}
8880     },
8881     
8882     closeTriggerEl : function()
8883     {
8884         var close = this.el.select('.roo-combo-removable-btn', true).first();
8885         return close ? close : false;
8886     },
8887     
8888     removeBtnClick : function(e, h, el)
8889     {
8890         e.preventDefault();
8891         
8892         if(this.fireEvent("remove", this) !== false){
8893             this.reset();
8894         }
8895     },
8896     
8897     createList : function()
8898     {
8899         this.list = Roo.get(document.body).createChild({
8900             tag: 'ul',
8901             cls: 'typeahead typeahead-long dropdown-menu',
8902             style: 'display:none'
8903         });
8904         
8905         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8906         
8907     },
8908
8909     // private
8910     initTrigger : function(){
8911        
8912     },
8913
8914     // private
8915     onDestroy : function(){
8916         if(this.trigger){
8917             this.trigger.removeAllListeners();
8918           //  this.trigger.remove();
8919         }
8920         //if(this.wrap){
8921         //    this.wrap.remove();
8922         //}
8923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8924     },
8925
8926     // private
8927     onFocus : function(){
8928         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8929         /*
8930         if(!this.mimicing){
8931             this.wrap.addClass('x-trigger-wrap-focus');
8932             this.mimicing = true;
8933             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8934             if(this.monitorTab){
8935                 this.el.on("keydown", this.checkTab, this);
8936             }
8937         }
8938         */
8939     },
8940
8941     // private
8942     checkTab : function(e){
8943         if(e.getKey() == e.TAB){
8944             this.triggerBlur();
8945         }
8946     },
8947
8948     // private
8949     onBlur : function(){
8950         // do nothing
8951     },
8952
8953     // private
8954     mimicBlur : function(e, t){
8955         /*
8956         if(!this.wrap.contains(t) && this.validateBlur()){
8957             this.triggerBlur();
8958         }
8959         */
8960     },
8961
8962     // private
8963     triggerBlur : function(){
8964         this.mimicing = false;
8965         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8966         if(this.monitorTab){
8967             this.el.un("keydown", this.checkTab, this);
8968         }
8969         //this.wrap.removeClass('x-trigger-wrap-focus');
8970         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8971     },
8972
8973     // private
8974     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8975     validateBlur : function(e, t){
8976         return true;
8977     },
8978
8979     // private
8980     onDisable : function(){
8981         this.inputEl().dom.disabled = true;
8982         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8983         //if(this.wrap){
8984         //    this.wrap.addClass('x-item-disabled');
8985         //}
8986     },
8987
8988     // private
8989     onEnable : function(){
8990         this.inputEl().dom.disabled = false;
8991         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8992         //if(this.wrap){
8993         //    this.el.removeClass('x-item-disabled');
8994         //}
8995     },
8996
8997     // private
8998     onShow : function(){
8999         var ae = this.getActionEl();
9000         
9001         if(ae){
9002             ae.dom.style.display = '';
9003             ae.dom.style.visibility = 'visible';
9004         }
9005     },
9006
9007     // private
9008     
9009     onHide : function(){
9010         var ae = this.getActionEl();
9011         ae.dom.style.display = 'none';
9012     },
9013
9014     /**
9015      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9016      * by an implementing function.
9017      * @method
9018      * @param {EventObject} e
9019      */
9020     onTriggerClick : Roo.emptyFn
9021 });
9022  /*
9023  * Based on:
9024  * Ext JS Library 1.1.1
9025  * Copyright(c) 2006-2007, Ext JS, LLC.
9026  *
9027  * Originally Released Under LGPL - original licence link has changed is not relivant.
9028  *
9029  * Fork - LGPL
9030  * <script type="text/javascript">
9031  */
9032
9033
9034 /**
9035  * @class Roo.data.SortTypes
9036  * @singleton
9037  * Defines the default sorting (casting?) comparison functions used when sorting data.
9038  */
9039 Roo.data.SortTypes = {
9040     /**
9041      * Default sort that does nothing
9042      * @param {Mixed} s The value being converted
9043      * @return {Mixed} The comparison value
9044      */
9045     none : function(s){
9046         return s;
9047     },
9048     
9049     /**
9050      * The regular expression used to strip tags
9051      * @type {RegExp}
9052      * @property
9053      */
9054     stripTagsRE : /<\/?[^>]+>/gi,
9055     
9056     /**
9057      * Strips all HTML tags to sort on text only
9058      * @param {Mixed} s The value being converted
9059      * @return {String} The comparison value
9060      */
9061     asText : function(s){
9062         return String(s).replace(this.stripTagsRE, "");
9063     },
9064     
9065     /**
9066      * Strips all HTML tags to sort on text only - Case insensitive
9067      * @param {Mixed} s The value being converted
9068      * @return {String} The comparison value
9069      */
9070     asUCText : function(s){
9071         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9072     },
9073     
9074     /**
9075      * Case insensitive string
9076      * @param {Mixed} s The value being converted
9077      * @return {String} The comparison value
9078      */
9079     asUCString : function(s) {
9080         return String(s).toUpperCase();
9081     },
9082     
9083     /**
9084      * Date sorting
9085      * @param {Mixed} s The value being converted
9086      * @return {Number} The comparison value
9087      */
9088     asDate : function(s) {
9089         if(!s){
9090             return 0;
9091         }
9092         if(s instanceof Date){
9093             return s.getTime();
9094         }
9095         return Date.parse(String(s));
9096     },
9097     
9098     /**
9099      * Float sorting
9100      * @param {Mixed} s The value being converted
9101      * @return {Float} The comparison value
9102      */
9103     asFloat : function(s) {
9104         var val = parseFloat(String(s).replace(/,/g, ""));
9105         if(isNaN(val)) val = 0;
9106         return val;
9107     },
9108     
9109     /**
9110      * Integer sorting
9111      * @param {Mixed} s The value being converted
9112      * @return {Number} The comparison value
9113      */
9114     asInt : function(s) {
9115         var val = parseInt(String(s).replace(/,/g, ""));
9116         if(isNaN(val)) val = 0;
9117         return val;
9118     }
9119 };/*
9120  * Based on:
9121  * Ext JS Library 1.1.1
9122  * Copyright(c) 2006-2007, Ext JS, LLC.
9123  *
9124  * Originally Released Under LGPL - original licence link has changed is not relivant.
9125  *
9126  * Fork - LGPL
9127  * <script type="text/javascript">
9128  */
9129
9130 /**
9131 * @class Roo.data.Record
9132  * Instances of this class encapsulate both record <em>definition</em> information, and record
9133  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9134  * to access Records cached in an {@link Roo.data.Store} object.<br>
9135  * <p>
9136  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9137  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9138  * objects.<br>
9139  * <p>
9140  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9141  * @constructor
9142  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9143  * {@link #create}. The parameters are the same.
9144  * @param {Array} data An associative Array of data values keyed by the field name.
9145  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9146  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9147  * not specified an integer id is generated.
9148  */
9149 Roo.data.Record = function(data, id){
9150     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9151     this.data = data;
9152 };
9153
9154 /**
9155  * Generate a constructor for a specific record layout.
9156  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9157  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9158  * Each field definition object may contain the following properties: <ul>
9159  * <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,
9160  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9161  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9162  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9163  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9164  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9165  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9166  * this may be omitted.</p></li>
9167  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9168  * <ul><li>auto (Default, implies no conversion)</li>
9169  * <li>string</li>
9170  * <li>int</li>
9171  * <li>float</li>
9172  * <li>boolean</li>
9173  * <li>date</li></ul></p></li>
9174  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9175  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9176  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9177  * by the Reader into an object that will be stored in the Record. It is passed the
9178  * following parameters:<ul>
9179  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9180  * </ul></p></li>
9181  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9182  * </ul>
9183  * <br>usage:<br><pre><code>
9184 var TopicRecord = Roo.data.Record.create(
9185     {name: 'title', mapping: 'topic_title'},
9186     {name: 'author', mapping: 'username'},
9187     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9188     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9189     {name: 'lastPoster', mapping: 'user2'},
9190     {name: 'excerpt', mapping: 'post_text'}
9191 );
9192
9193 var myNewRecord = new TopicRecord({
9194     title: 'Do my job please',
9195     author: 'noobie',
9196     totalPosts: 1,
9197     lastPost: new Date(),
9198     lastPoster: 'Animal',
9199     excerpt: 'No way dude!'
9200 });
9201 myStore.add(myNewRecord);
9202 </code></pre>
9203  * @method create
9204  * @static
9205  */
9206 Roo.data.Record.create = function(o){
9207     var f = function(){
9208         f.superclass.constructor.apply(this, arguments);
9209     };
9210     Roo.extend(f, Roo.data.Record);
9211     var p = f.prototype;
9212     p.fields = new Roo.util.MixedCollection(false, function(field){
9213         return field.name;
9214     });
9215     for(var i = 0, len = o.length; i < len; i++){
9216         p.fields.add(new Roo.data.Field(o[i]));
9217     }
9218     f.getField = function(name){
9219         return p.fields.get(name);  
9220     };
9221     return f;
9222 };
9223
9224 Roo.data.Record.AUTO_ID = 1000;
9225 Roo.data.Record.EDIT = 'edit';
9226 Roo.data.Record.REJECT = 'reject';
9227 Roo.data.Record.COMMIT = 'commit';
9228
9229 Roo.data.Record.prototype = {
9230     /**
9231      * Readonly flag - true if this record has been modified.
9232      * @type Boolean
9233      */
9234     dirty : false,
9235     editing : false,
9236     error: null,
9237     modified: null,
9238
9239     // private
9240     join : function(store){
9241         this.store = store;
9242     },
9243
9244     /**
9245      * Set the named field to the specified value.
9246      * @param {String} name The name of the field to set.
9247      * @param {Object} value The value to set the field to.
9248      */
9249     set : function(name, value){
9250         if(this.data[name] == value){
9251             return;
9252         }
9253         this.dirty = true;
9254         if(!this.modified){
9255             this.modified = {};
9256         }
9257         if(typeof this.modified[name] == 'undefined'){
9258             this.modified[name] = this.data[name];
9259         }
9260         this.data[name] = value;
9261         if(!this.editing && this.store){
9262             this.store.afterEdit(this);
9263         }       
9264     },
9265
9266     /**
9267      * Get the value of the named field.
9268      * @param {String} name The name of the field to get the value of.
9269      * @return {Object} The value of the field.
9270      */
9271     get : function(name){
9272         return this.data[name]; 
9273     },
9274
9275     // private
9276     beginEdit : function(){
9277         this.editing = true;
9278         this.modified = {}; 
9279     },
9280
9281     // private
9282     cancelEdit : function(){
9283         this.editing = false;
9284         delete this.modified;
9285     },
9286
9287     // private
9288     endEdit : function(){
9289         this.editing = false;
9290         if(this.dirty && this.store){
9291             this.store.afterEdit(this);
9292         }
9293     },
9294
9295     /**
9296      * Usually called by the {@link Roo.data.Store} which owns the Record.
9297      * Rejects all changes made to the Record since either creation, or the last commit operation.
9298      * Modified fields are reverted to their original values.
9299      * <p>
9300      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9301      * of reject operations.
9302      */
9303     reject : function(){
9304         var m = this.modified;
9305         for(var n in m){
9306             if(typeof m[n] != "function"){
9307                 this.data[n] = m[n];
9308             }
9309         }
9310         this.dirty = false;
9311         delete this.modified;
9312         this.editing = false;
9313         if(this.store){
9314             this.store.afterReject(this);
9315         }
9316     },
9317
9318     /**
9319      * Usually called by the {@link Roo.data.Store} which owns the Record.
9320      * Commits all changes made to the Record since either creation, or the last commit operation.
9321      * <p>
9322      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9323      * of commit operations.
9324      */
9325     commit : function(){
9326         this.dirty = false;
9327         delete this.modified;
9328         this.editing = false;
9329         if(this.store){
9330             this.store.afterCommit(this);
9331         }
9332     },
9333
9334     // private
9335     hasError : function(){
9336         return this.error != null;
9337     },
9338
9339     // private
9340     clearError : function(){
9341         this.error = null;
9342     },
9343
9344     /**
9345      * Creates a copy of this record.
9346      * @param {String} id (optional) A new record id if you don't want to use this record's id
9347      * @return {Record}
9348      */
9349     copy : function(newId) {
9350         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9351     }
9352 };/*
9353  * Based on:
9354  * Ext JS Library 1.1.1
9355  * Copyright(c) 2006-2007, Ext JS, LLC.
9356  *
9357  * Originally Released Under LGPL - original licence link has changed is not relivant.
9358  *
9359  * Fork - LGPL
9360  * <script type="text/javascript">
9361  */
9362
9363
9364
9365 /**
9366  * @class Roo.data.Store
9367  * @extends Roo.util.Observable
9368  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9369  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9370  * <p>
9371  * 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
9372  * has no knowledge of the format of the data returned by the Proxy.<br>
9373  * <p>
9374  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9375  * instances from the data object. These records are cached and made available through accessor functions.
9376  * @constructor
9377  * Creates a new Store.
9378  * @param {Object} config A config object containing the objects needed for the Store to access data,
9379  * and read the data into Records.
9380  */
9381 Roo.data.Store = function(config){
9382     this.data = new Roo.util.MixedCollection(false);
9383     this.data.getKey = function(o){
9384         return o.id;
9385     };
9386     this.baseParams = {};
9387     // private
9388     this.paramNames = {
9389         "start" : "start",
9390         "limit" : "limit",
9391         "sort" : "sort",
9392         "dir" : "dir",
9393         "multisort" : "_multisort"
9394     };
9395
9396     if(config && config.data){
9397         this.inlineData = config.data;
9398         delete config.data;
9399     }
9400
9401     Roo.apply(this, config);
9402     
9403     if(this.reader){ // reader passed
9404         this.reader = Roo.factory(this.reader, Roo.data);
9405         this.reader.xmodule = this.xmodule || false;
9406         if(!this.recordType){
9407             this.recordType = this.reader.recordType;
9408         }
9409         if(this.reader.onMetaChange){
9410             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9411         }
9412     }
9413
9414     if(this.recordType){
9415         this.fields = this.recordType.prototype.fields;
9416     }
9417     this.modified = [];
9418
9419     this.addEvents({
9420         /**
9421          * @event datachanged
9422          * Fires when the data cache has changed, and a widget which is using this Store
9423          * as a Record cache should refresh its view.
9424          * @param {Store} this
9425          */
9426         datachanged : true,
9427         /**
9428          * @event metachange
9429          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9430          * @param {Store} this
9431          * @param {Object} meta The JSON metadata
9432          */
9433         metachange : true,
9434         /**
9435          * @event add
9436          * Fires when Records have been added to the Store
9437          * @param {Store} this
9438          * @param {Roo.data.Record[]} records The array of Records added
9439          * @param {Number} index The index at which the record(s) were added
9440          */
9441         add : true,
9442         /**
9443          * @event remove
9444          * Fires when a Record has been removed from the Store
9445          * @param {Store} this
9446          * @param {Roo.data.Record} record The Record that was removed
9447          * @param {Number} index The index at which the record was removed
9448          */
9449         remove : true,
9450         /**
9451          * @event update
9452          * Fires when a Record has been updated
9453          * @param {Store} this
9454          * @param {Roo.data.Record} record The Record that was updated
9455          * @param {String} operation The update operation being performed.  Value may be one of:
9456          * <pre><code>
9457  Roo.data.Record.EDIT
9458  Roo.data.Record.REJECT
9459  Roo.data.Record.COMMIT
9460          * </code></pre>
9461          */
9462         update : true,
9463         /**
9464          * @event clear
9465          * Fires when the data cache has been cleared.
9466          * @param {Store} this
9467          */
9468         clear : true,
9469         /**
9470          * @event beforeload
9471          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9472          * the load action will be canceled.
9473          * @param {Store} this
9474          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9475          */
9476         beforeload : true,
9477         /**
9478          * @event beforeloadadd
9479          * Fires after a new set of Records has been loaded.
9480          * @param {Store} this
9481          * @param {Roo.data.Record[]} records The Records that were loaded
9482          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9483          */
9484         beforeloadadd : true,
9485         /**
9486          * @event load
9487          * Fires after a new set of Records has been loaded, before they are added to the store.
9488          * @param {Store} this
9489          * @param {Roo.data.Record[]} records The Records that were loaded
9490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9491          * @params {Object} return from reader
9492          */
9493         load : true,
9494         /**
9495          * @event loadexception
9496          * Fires if an exception occurs in the Proxy during loading.
9497          * Called with the signature of the Proxy's "loadexception" event.
9498          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9499          * 
9500          * @param {Proxy} 
9501          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9502          * @param {Object} load options 
9503          * @param {Object} jsonData from your request (normally this contains the Exception)
9504          */
9505         loadexception : true
9506     });
9507     
9508     if(this.proxy){
9509         this.proxy = Roo.factory(this.proxy, Roo.data);
9510         this.proxy.xmodule = this.xmodule || false;
9511         this.relayEvents(this.proxy,  ["loadexception"]);
9512     }
9513     this.sortToggle = {};
9514     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9515
9516     Roo.data.Store.superclass.constructor.call(this);
9517
9518     if(this.inlineData){
9519         this.loadData(this.inlineData);
9520         delete this.inlineData;
9521     }
9522 };
9523
9524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9525      /**
9526     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9527     * without a remote query - used by combo/forms at present.
9528     */
9529     
9530     /**
9531     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9532     */
9533     /**
9534     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9535     */
9536     /**
9537     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9538     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9539     */
9540     /**
9541     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9542     * on any HTTP request
9543     */
9544     /**
9545     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9546     */
9547     /**
9548     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9549     */
9550     multiSort: false,
9551     /**
9552     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9553     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9554     */
9555     remoteSort : false,
9556
9557     /**
9558     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9559      * loaded or when a record is removed. (defaults to false).
9560     */
9561     pruneModifiedRecords : false,
9562
9563     // private
9564     lastOptions : null,
9565
9566     /**
9567      * Add Records to the Store and fires the add event.
9568      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9569      */
9570     add : function(records){
9571         records = [].concat(records);
9572         for(var i = 0, len = records.length; i < len; i++){
9573             records[i].join(this);
9574         }
9575         var index = this.data.length;
9576         this.data.addAll(records);
9577         this.fireEvent("add", this, records, index);
9578     },
9579
9580     /**
9581      * Remove a Record from the Store and fires the remove event.
9582      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9583      */
9584     remove : function(record){
9585         var index = this.data.indexOf(record);
9586         this.data.removeAt(index);
9587         if(this.pruneModifiedRecords){
9588             this.modified.remove(record);
9589         }
9590         this.fireEvent("remove", this, record, index);
9591     },
9592
9593     /**
9594      * Remove all Records from the Store and fires the clear event.
9595      */
9596     removeAll : function(){
9597         this.data.clear();
9598         if(this.pruneModifiedRecords){
9599             this.modified = [];
9600         }
9601         this.fireEvent("clear", this);
9602     },
9603
9604     /**
9605      * Inserts Records to the Store at the given index and fires the add event.
9606      * @param {Number} index The start index at which to insert the passed Records.
9607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9608      */
9609     insert : function(index, records){
9610         records = [].concat(records);
9611         for(var i = 0, len = records.length; i < len; i++){
9612             this.data.insert(index, records[i]);
9613             records[i].join(this);
9614         }
9615         this.fireEvent("add", this, records, index);
9616     },
9617
9618     /**
9619      * Get the index within the cache of the passed Record.
9620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9621      * @return {Number} The index of the passed Record. Returns -1 if not found.
9622      */
9623     indexOf : function(record){
9624         return this.data.indexOf(record);
9625     },
9626
9627     /**
9628      * Get the index within the cache of the Record with the passed id.
9629      * @param {String} id The id of the Record to find.
9630      * @return {Number} The index of the Record. Returns -1 if not found.
9631      */
9632     indexOfId : function(id){
9633         return this.data.indexOfKey(id);
9634     },
9635
9636     /**
9637      * Get the Record with the specified id.
9638      * @param {String} id The id of the Record to find.
9639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9640      */
9641     getById : function(id){
9642         return this.data.key(id);
9643     },
9644
9645     /**
9646      * Get the Record at the specified index.
9647      * @param {Number} index The index of the Record to find.
9648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9649      */
9650     getAt : function(index){
9651         return this.data.itemAt(index);
9652     },
9653
9654     /**
9655      * Returns a range of Records between specified indices.
9656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9658      * @return {Roo.data.Record[]} An array of Records
9659      */
9660     getRange : function(start, end){
9661         return this.data.getRange(start, end);
9662     },
9663
9664     // private
9665     storeOptions : function(o){
9666         o = Roo.apply({}, o);
9667         delete o.callback;
9668         delete o.scope;
9669         this.lastOptions = o;
9670     },
9671
9672     /**
9673      * Loads the Record cache from the configured Proxy using the configured Reader.
9674      * <p>
9675      * If using remote paging, then the first load call must specify the <em>start</em>
9676      * and <em>limit</em> properties in the options.params property to establish the initial
9677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9678      * <p>
9679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9680      * and this call will return before the new data has been loaded. Perform any post-processing
9681      * in a callback function, or in a "load" event handler.</strong>
9682      * <p>
9683      * @param {Object} options An object containing properties which control loading options:<ul>
9684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9686      * passed the following arguments:<ul>
9687      * <li>r : Roo.data.Record[]</li>
9688      * <li>options: Options object from the load call</li>
9689      * <li>success: Boolean success indicator</li></ul></li>
9690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9692      * </ul>
9693      */
9694     load : function(options){
9695         options = options || {};
9696         if(this.fireEvent("beforeload", this, options) !== false){
9697             this.storeOptions(options);
9698             var p = Roo.apply(options.params || {}, this.baseParams);
9699             // if meta was not loaded from remote source.. try requesting it.
9700             if (!this.reader.metaFromRemote) {
9701                 p._requestMeta = 1;
9702             }
9703             if(this.sortInfo && this.remoteSort){
9704                 var pn = this.paramNames;
9705                 p[pn["sort"]] = this.sortInfo.field;
9706                 p[pn["dir"]] = this.sortInfo.direction;
9707             }
9708             if (this.multiSort) {
9709                 var pn = this.paramNames;
9710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9711             }
9712             
9713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9714         }
9715     },
9716
9717     /**
9718      * Reloads the Record cache from the configured Proxy using the configured Reader and
9719      * the options from the last load operation performed.
9720      * @param {Object} options (optional) An object containing properties which may override the options
9721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9722      * the most recently used options are reused).
9723      */
9724     reload : function(options){
9725         this.load(Roo.applyIf(options||{}, this.lastOptions));
9726     },
9727
9728     // private
9729     // Called as a callback by the Reader during a load operation.
9730     loadRecords : function(o, options, success){
9731         if(!o || success === false){
9732             if(success !== false){
9733                 this.fireEvent("load", this, [], options, o);
9734             }
9735             if(options.callback){
9736                 options.callback.call(options.scope || this, [], options, false);
9737             }
9738             return;
9739         }
9740         // if data returned failure - throw an exception.
9741         if (o.success === false) {
9742             // show a message if no listener is registered.
9743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9745             }
9746             // loadmask wil be hooked into this..
9747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9748             return;
9749         }
9750         var r = o.records, t = o.totalRecords || r.length;
9751         
9752         this.fireEvent("beforeloadadd", this, r, options, o);
9753         
9754         if(!options || options.add !== true){
9755             if(this.pruneModifiedRecords){
9756                 this.modified = [];
9757             }
9758             for(var i = 0, len = r.length; i < len; i++){
9759                 r[i].join(this);
9760             }
9761             if(this.snapshot){
9762                 this.data = this.snapshot;
9763                 delete this.snapshot;
9764             }
9765             this.data.clear();
9766             this.data.addAll(r);
9767             this.totalLength = t;
9768             this.applySort();
9769             this.fireEvent("datachanged", this);
9770         }else{
9771             this.totalLength = Math.max(t, this.data.length+r.length);
9772             this.add(r);
9773         }
9774         this.fireEvent("load", this, r, options, o);
9775         if(options.callback){
9776             options.callback.call(options.scope || this, r, options, true);
9777         }
9778     },
9779
9780
9781     /**
9782      * Loads data from a passed data block. A Reader which understands the format of the data
9783      * must have been configured in the constructor.
9784      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9785      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9786      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9787      */
9788     loadData : function(o, append){
9789         var r = this.reader.readRecords(o);
9790         this.loadRecords(r, {add: append}, true);
9791     },
9792
9793     /**
9794      * Gets the number of cached records.
9795      * <p>
9796      * <em>If using paging, this may not be the total size of the dataset. If the data object
9797      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9798      * the data set size</em>
9799      */
9800     getCount : function(){
9801         return this.data.length || 0;
9802     },
9803
9804     /**
9805      * Gets the total number of records in the dataset as returned by the server.
9806      * <p>
9807      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9808      * the dataset size</em>
9809      */
9810     getTotalCount : function(){
9811         return this.totalLength || 0;
9812     },
9813
9814     /**
9815      * Returns the sort state of the Store as an object with two properties:
9816      * <pre><code>
9817  field {String} The name of the field by which the Records are sorted
9818  direction {String} The sort order, "ASC" or "DESC"
9819      * </code></pre>
9820      */
9821     getSortState : function(){
9822         return this.sortInfo;
9823     },
9824
9825     // private
9826     applySort : function(){
9827         if(this.sortInfo && !this.remoteSort){
9828             var s = this.sortInfo, f = s.field;
9829             var st = this.fields.get(f).sortType;
9830             var fn = function(r1, r2){
9831                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9832                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9833             };
9834             this.data.sort(s.direction, fn);
9835             if(this.snapshot && this.snapshot != this.data){
9836                 this.snapshot.sort(s.direction, fn);
9837             }
9838         }
9839     },
9840
9841     /**
9842      * Sets the default sort column and order to be used by the next load operation.
9843      * @param {String} fieldName The name of the field to sort by.
9844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9845      */
9846     setDefaultSort : function(field, dir){
9847         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9848     },
9849
9850     /**
9851      * Sort the Records.
9852      * If remote sorting is used, the sort is performed on the server, and the cache is
9853      * reloaded. If local sorting is used, the cache is sorted internally.
9854      * @param {String} fieldName The name of the field to sort by.
9855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9856      */
9857     sort : function(fieldName, dir){
9858         var f = this.fields.get(fieldName);
9859         if(!dir){
9860             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9861             
9862             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9863                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9864             }else{
9865                 dir = f.sortDir;
9866             }
9867         }
9868         this.sortToggle[f.name] = dir;
9869         this.sortInfo = {field: f.name, direction: dir};
9870         if(!this.remoteSort){
9871             this.applySort();
9872             this.fireEvent("datachanged", this);
9873         }else{
9874             this.load(this.lastOptions);
9875         }
9876     },
9877
9878     /**
9879      * Calls the specified function for each of the Records in the cache.
9880      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9881      * Returning <em>false</em> aborts and exits the iteration.
9882      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9883      */
9884     each : function(fn, scope){
9885         this.data.each(fn, scope);
9886     },
9887
9888     /**
9889      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9890      * (e.g., during paging).
9891      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9892      */
9893     getModifiedRecords : function(){
9894         return this.modified;
9895     },
9896
9897     // private
9898     createFilterFn : function(property, value, anyMatch){
9899         if(!value.exec){ // not a regex
9900             value = String(value);
9901             if(value.length == 0){
9902                 return false;
9903             }
9904             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9905         }
9906         return function(r){
9907             return value.test(r.data[property]);
9908         };
9909     },
9910
9911     /**
9912      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9913      * @param {String} property A field on your records
9914      * @param {Number} start The record index to start at (defaults to 0)
9915      * @param {Number} end The last record index to include (defaults to length - 1)
9916      * @return {Number} The sum
9917      */
9918     sum : function(property, start, end){
9919         var rs = this.data.items, v = 0;
9920         start = start || 0;
9921         end = (end || end === 0) ? end : rs.length-1;
9922
9923         for(var i = start; i <= end; i++){
9924             v += (rs[i].data[property] || 0);
9925         }
9926         return v;
9927     },
9928
9929     /**
9930      * Filter the records by a specified property.
9931      * @param {String} field A field on your records
9932      * @param {String/RegExp} value Either a string that the field
9933      * should start with or a RegExp to test against the field
9934      * @param {Boolean} anyMatch True to match any part not just the beginning
9935      */
9936     filter : function(property, value, anyMatch){
9937         var fn = this.createFilterFn(property, value, anyMatch);
9938         return fn ? this.filterBy(fn) : this.clearFilter();
9939     },
9940
9941     /**
9942      * Filter by a function. The specified function will be called with each
9943      * record in this data source. If the function returns true the record is included,
9944      * otherwise it is filtered.
9945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9946      * @param {Object} scope (optional) The scope of the function (defaults to this)
9947      */
9948     filterBy : function(fn, scope){
9949         this.snapshot = this.snapshot || this.data;
9950         this.data = this.queryBy(fn, scope||this);
9951         this.fireEvent("datachanged", this);
9952     },
9953
9954     /**
9955      * Query the records by a specified property.
9956      * @param {String} field A field on your records
9957      * @param {String/RegExp} value Either a string that the field
9958      * should start with or a RegExp to test against the field
9959      * @param {Boolean} anyMatch True to match any part not just the beginning
9960      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9961      */
9962     query : function(property, value, anyMatch){
9963         var fn = this.createFilterFn(property, value, anyMatch);
9964         return fn ? this.queryBy(fn) : this.data.clone();
9965     },
9966
9967     /**
9968      * Query by a function. The specified function will be called with each
9969      * record in this data source. If the function returns true the record is included
9970      * in the results.
9971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9972      * @param {Object} scope (optional) The scope of the function (defaults to this)
9973       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9974      **/
9975     queryBy : function(fn, scope){
9976         var data = this.snapshot || this.data;
9977         return data.filterBy(fn, scope||this);
9978     },
9979
9980     /**
9981      * Collects unique values for a particular dataIndex from this store.
9982      * @param {String} dataIndex The property to collect
9983      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9984      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9985      * @return {Array} An array of the unique values
9986      **/
9987     collect : function(dataIndex, allowNull, bypassFilter){
9988         var d = (bypassFilter === true && this.snapshot) ?
9989                 this.snapshot.items : this.data.items;
9990         var v, sv, r = [], l = {};
9991         for(var i = 0, len = d.length; i < len; i++){
9992             v = d[i].data[dataIndex];
9993             sv = String(v);
9994             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9995                 l[sv] = true;
9996                 r[r.length] = v;
9997             }
9998         }
9999         return r;
10000     },
10001
10002     /**
10003      * Revert to a view of the Record cache with no filtering applied.
10004      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10005      */
10006     clearFilter : function(suppressEvent){
10007         if(this.snapshot && this.snapshot != this.data){
10008             this.data = this.snapshot;
10009             delete this.snapshot;
10010             if(suppressEvent !== true){
10011                 this.fireEvent("datachanged", this);
10012             }
10013         }
10014     },
10015
10016     // private
10017     afterEdit : function(record){
10018         if(this.modified.indexOf(record) == -1){
10019             this.modified.push(record);
10020         }
10021         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10022     },
10023     
10024     // private
10025     afterReject : function(record){
10026         this.modified.remove(record);
10027         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10028     },
10029
10030     // private
10031     afterCommit : function(record){
10032         this.modified.remove(record);
10033         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10034     },
10035
10036     /**
10037      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10038      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10039      */
10040     commitChanges : function(){
10041         var m = this.modified.slice(0);
10042         this.modified = [];
10043         for(var i = 0, len = m.length; i < len; i++){
10044             m[i].commit();
10045         }
10046     },
10047
10048     /**
10049      * Cancel outstanding changes on all changed records.
10050      */
10051     rejectChanges : function(){
10052         var m = this.modified.slice(0);
10053         this.modified = [];
10054         for(var i = 0, len = m.length; i < len; i++){
10055             m[i].reject();
10056         }
10057     },
10058
10059     onMetaChange : function(meta, rtype, o){
10060         this.recordType = rtype;
10061         this.fields = rtype.prototype.fields;
10062         delete this.snapshot;
10063         this.sortInfo = meta.sortInfo || this.sortInfo;
10064         this.modified = [];
10065         this.fireEvent('metachange', this, this.reader.meta);
10066     },
10067     
10068     moveIndex : function(data, type)
10069     {
10070         var index = this.indexOf(data);
10071         
10072         var newIndex = index + type;
10073         
10074         this.remove(data);
10075         
10076         this.insert(newIndex, data);
10077         
10078     }
10079 });/*
10080  * Based on:
10081  * Ext JS Library 1.1.1
10082  * Copyright(c) 2006-2007, Ext JS, LLC.
10083  *
10084  * Originally Released Under LGPL - original licence link has changed is not relivant.
10085  *
10086  * Fork - LGPL
10087  * <script type="text/javascript">
10088  */
10089
10090 /**
10091  * @class Roo.data.SimpleStore
10092  * @extends Roo.data.Store
10093  * Small helper class to make creating Stores from Array data easier.
10094  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10095  * @cfg {Array} fields An array of field definition objects, or field name strings.
10096  * @cfg {Array} data The multi-dimensional array of data
10097  * @constructor
10098  * @param {Object} config
10099  */
10100 Roo.data.SimpleStore = function(config){
10101     Roo.data.SimpleStore.superclass.constructor.call(this, {
10102         isLocal : true,
10103         reader: new Roo.data.ArrayReader({
10104                 id: config.id
10105             },
10106             Roo.data.Record.create(config.fields)
10107         ),
10108         proxy : new Roo.data.MemoryProxy(config.data)
10109     });
10110     this.load();
10111 };
10112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10113  * Based on:
10114  * Ext JS Library 1.1.1
10115  * Copyright(c) 2006-2007, Ext JS, LLC.
10116  *
10117  * Originally Released Under LGPL - original licence link has changed is not relivant.
10118  *
10119  * Fork - LGPL
10120  * <script type="text/javascript">
10121  */
10122
10123 /**
10124 /**
10125  * @extends Roo.data.Store
10126  * @class Roo.data.JsonStore
10127  * Small helper class to make creating Stores for JSON data easier. <br/>
10128 <pre><code>
10129 var store = new Roo.data.JsonStore({
10130     url: 'get-images.php',
10131     root: 'images',
10132     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10133 });
10134 </code></pre>
10135  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10136  * JsonReader and HttpProxy (unless inline data is provided).</b>
10137  * @cfg {Array} fields An array of field definition objects, or field name strings.
10138  * @constructor
10139  * @param {Object} config
10140  */
10141 Roo.data.JsonStore = function(c){
10142     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10143         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10144         reader: new Roo.data.JsonReader(c, c.fields)
10145     }));
10146 };
10147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10148  * Based on:
10149  * Ext JS Library 1.1.1
10150  * Copyright(c) 2006-2007, Ext JS, LLC.
10151  *
10152  * Originally Released Under LGPL - original licence link has changed is not relivant.
10153  *
10154  * Fork - LGPL
10155  * <script type="text/javascript">
10156  */
10157
10158  
10159 Roo.data.Field = function(config){
10160     if(typeof config == "string"){
10161         config = {name: config};
10162     }
10163     Roo.apply(this, config);
10164     
10165     if(!this.type){
10166         this.type = "auto";
10167     }
10168     
10169     var st = Roo.data.SortTypes;
10170     // named sortTypes are supported, here we look them up
10171     if(typeof this.sortType == "string"){
10172         this.sortType = st[this.sortType];
10173     }
10174     
10175     // set default sortType for strings and dates
10176     if(!this.sortType){
10177         switch(this.type){
10178             case "string":
10179                 this.sortType = st.asUCString;
10180                 break;
10181             case "date":
10182                 this.sortType = st.asDate;
10183                 break;
10184             default:
10185                 this.sortType = st.none;
10186         }
10187     }
10188
10189     // define once
10190     var stripRe = /[\$,%]/g;
10191
10192     // prebuilt conversion function for this field, instead of
10193     // switching every time we're reading a value
10194     if(!this.convert){
10195         var cv, dateFormat = this.dateFormat;
10196         switch(this.type){
10197             case "":
10198             case "auto":
10199             case undefined:
10200                 cv = function(v){ return v; };
10201                 break;
10202             case "string":
10203                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10204                 break;
10205             case "int":
10206                 cv = function(v){
10207                     return v !== undefined && v !== null && v !== '' ?
10208                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10209                     };
10210                 break;
10211             case "float":
10212                 cv = function(v){
10213                     return v !== undefined && v !== null && v !== '' ?
10214                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10215                     };
10216                 break;
10217             case "bool":
10218             case "boolean":
10219                 cv = function(v){ return v === true || v === "true" || v == 1; };
10220                 break;
10221             case "date":
10222                 cv = function(v){
10223                     if(!v){
10224                         return '';
10225                     }
10226                     if(v instanceof Date){
10227                         return v;
10228                     }
10229                     if(dateFormat){
10230                         if(dateFormat == "timestamp"){
10231                             return new Date(v*1000);
10232                         }
10233                         return Date.parseDate(v, dateFormat);
10234                     }
10235                     var parsed = Date.parse(v);
10236                     return parsed ? new Date(parsed) : null;
10237                 };
10238              break;
10239             
10240         }
10241         this.convert = cv;
10242     }
10243 };
10244
10245 Roo.data.Field.prototype = {
10246     dateFormat: null,
10247     defaultValue: "",
10248     mapping: null,
10249     sortType : null,
10250     sortDir : "ASC"
10251 };/*
10252  * Based on:
10253  * Ext JS Library 1.1.1
10254  * Copyright(c) 2006-2007, Ext JS, LLC.
10255  *
10256  * Originally Released Under LGPL - original licence link has changed is not relivant.
10257  *
10258  * Fork - LGPL
10259  * <script type="text/javascript">
10260  */
10261  
10262 // Base class for reading structured data from a data source.  This class is intended to be
10263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10264
10265 /**
10266  * @class Roo.data.DataReader
10267  * Base class for reading structured data from a data source.  This class is intended to be
10268  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10269  */
10270
10271 Roo.data.DataReader = function(meta, recordType){
10272     
10273     this.meta = meta;
10274     
10275     this.recordType = recordType instanceof Array ? 
10276         Roo.data.Record.create(recordType) : recordType;
10277 };
10278
10279 Roo.data.DataReader.prototype = {
10280      /**
10281      * Create an empty record
10282      * @param {Object} data (optional) - overlay some values
10283      * @return {Roo.data.Record} record created.
10284      */
10285     newRow :  function(d) {
10286         var da =  {};
10287         this.recordType.prototype.fields.each(function(c) {
10288             switch( c.type) {
10289                 case 'int' : da[c.name] = 0; break;
10290                 case 'date' : da[c.name] = new Date(); break;
10291                 case 'float' : da[c.name] = 0.0; break;
10292                 case 'boolean' : da[c.name] = false; break;
10293                 default : da[c.name] = ""; break;
10294             }
10295             
10296         });
10297         return new this.recordType(Roo.apply(da, d));
10298     }
10299     
10300 };/*
10301  * Based on:
10302  * Ext JS Library 1.1.1
10303  * Copyright(c) 2006-2007, Ext JS, LLC.
10304  *
10305  * Originally Released Under LGPL - original licence link has changed is not relivant.
10306  *
10307  * Fork - LGPL
10308  * <script type="text/javascript">
10309  */
10310
10311 /**
10312  * @class Roo.data.DataProxy
10313  * @extends Roo.data.Observable
10314  * This class is an abstract base class for implementations which provide retrieval of
10315  * unformatted data objects.<br>
10316  * <p>
10317  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10318  * (of the appropriate type which knows how to parse the data object) to provide a block of
10319  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10320  * <p>
10321  * Custom implementations must implement the load method as described in
10322  * {@link Roo.data.HttpProxy#load}.
10323  */
10324 Roo.data.DataProxy = function(){
10325     this.addEvents({
10326         /**
10327          * @event beforeload
10328          * Fires before a network request is made to retrieve a data object.
10329          * @param {Object} This DataProxy object.
10330          * @param {Object} params The params parameter to the load function.
10331          */
10332         beforeload : true,
10333         /**
10334          * @event load
10335          * Fires before the load method's callback is called.
10336          * @param {Object} This DataProxy object.
10337          * @param {Object} o The data object.
10338          * @param {Object} arg The callback argument object passed to the load function.
10339          */
10340         load : true,
10341         /**
10342          * @event loadexception
10343          * Fires if an Exception occurs during data retrieval.
10344          * @param {Object} This DataProxy object.
10345          * @param {Object} o The data object.
10346          * @param {Object} arg The callback argument object passed to the load function.
10347          * @param {Object} e The Exception.
10348          */
10349         loadexception : true
10350     });
10351     Roo.data.DataProxy.superclass.constructor.call(this);
10352 };
10353
10354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10355
10356     /**
10357      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10358      */
10359 /*
10360  * Based on:
10361  * Ext JS Library 1.1.1
10362  * Copyright(c) 2006-2007, Ext JS, LLC.
10363  *
10364  * Originally Released Under LGPL - original licence link has changed is not relivant.
10365  *
10366  * Fork - LGPL
10367  * <script type="text/javascript">
10368  */
10369 /**
10370  * @class Roo.data.MemoryProxy
10371  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10372  * to the Reader when its load method is called.
10373  * @constructor
10374  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10375  */
10376 Roo.data.MemoryProxy = function(data){
10377     if (data.data) {
10378         data = data.data;
10379     }
10380     Roo.data.MemoryProxy.superclass.constructor.call(this);
10381     this.data = data;
10382 };
10383
10384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10385     /**
10386      * Load data from the requested source (in this case an in-memory
10387      * data object passed to the constructor), read the data object into
10388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10389      * process that block using the passed callback.
10390      * @param {Object} params This parameter is not used by the MemoryProxy class.
10391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10392      * object into a block of Roo.data.Records.
10393      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10394      * The function must be passed <ul>
10395      * <li>The Record block object</li>
10396      * <li>The "arg" argument from the load function</li>
10397      * <li>A boolean success indicator</li>
10398      * </ul>
10399      * @param {Object} scope The scope in which to call the callback
10400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10401      */
10402     load : function(params, reader, callback, scope, arg){
10403         params = params || {};
10404         var result;
10405         try {
10406             result = reader.readRecords(this.data);
10407         }catch(e){
10408             this.fireEvent("loadexception", this, arg, null, e);
10409             callback.call(scope, null, arg, false);
10410             return;
10411         }
10412         callback.call(scope, result, arg, true);
10413     },
10414     
10415     // private
10416     update : function(params, records){
10417         
10418     }
10419 });/*
10420  * Based on:
10421  * Ext JS Library 1.1.1
10422  * Copyright(c) 2006-2007, Ext JS, LLC.
10423  *
10424  * Originally Released Under LGPL - original licence link has changed is not relivant.
10425  *
10426  * Fork - LGPL
10427  * <script type="text/javascript">
10428  */
10429 /**
10430  * @class Roo.data.HttpProxy
10431  * @extends Roo.data.DataProxy
10432  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10433  * configured to reference a certain URL.<br><br>
10434  * <p>
10435  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10436  * from which the running page was served.<br><br>
10437  * <p>
10438  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10439  * <p>
10440  * Be aware that to enable the browser to parse an XML document, the server must set
10441  * the Content-Type header in the HTTP response to "text/xml".
10442  * @constructor
10443  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10444  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10445  * will be used to make the request.
10446  */
10447 Roo.data.HttpProxy = function(conn){
10448     Roo.data.HttpProxy.superclass.constructor.call(this);
10449     // is conn a conn config or a real conn?
10450     this.conn = conn;
10451     this.useAjax = !conn || !conn.events;
10452   
10453 };
10454
10455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10456     // thse are take from connection...
10457     
10458     /**
10459      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10460      */
10461     /**
10462      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10463      * extra parameters to each request made by this object. (defaults to undefined)
10464      */
10465     /**
10466      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10467      *  to each request made by this object. (defaults to undefined)
10468      */
10469     /**
10470      * @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)
10471      */
10472     /**
10473      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10474      */
10475      /**
10476      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10477      * @type Boolean
10478      */
10479   
10480
10481     /**
10482      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10483      * @type Boolean
10484      */
10485     /**
10486      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10487      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10488      * a finer-grained basis than the DataProxy events.
10489      */
10490     getConnection : function(){
10491         return this.useAjax ? Roo.Ajax : this.conn;
10492     },
10493
10494     /**
10495      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10496      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10497      * process that block using the passed callback.
10498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10499      * for the request to the remote server.
10500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10501      * object into a block of Roo.data.Records.
10502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10503      * The function must be passed <ul>
10504      * <li>The Record block object</li>
10505      * <li>The "arg" argument from the load function</li>
10506      * <li>A boolean success indicator</li>
10507      * </ul>
10508      * @param {Object} scope The scope in which to call the callback
10509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10510      */
10511     load : function(params, reader, callback, scope, arg){
10512         if(this.fireEvent("beforeload", this, params) !== false){
10513             var  o = {
10514                 params : params || {},
10515                 request: {
10516                     callback : callback,
10517                     scope : scope,
10518                     arg : arg
10519                 },
10520                 reader: reader,
10521                 callback : this.loadResponse,
10522                 scope: this
10523             };
10524             if(this.useAjax){
10525                 Roo.applyIf(o, this.conn);
10526                 if(this.activeRequest){
10527                     Roo.Ajax.abort(this.activeRequest);
10528                 }
10529                 this.activeRequest = Roo.Ajax.request(o);
10530             }else{
10531                 this.conn.request(o);
10532             }
10533         }else{
10534             callback.call(scope||this, null, arg, false);
10535         }
10536     },
10537
10538     // private
10539     loadResponse : function(o, success, response){
10540         delete this.activeRequest;
10541         if(!success){
10542             this.fireEvent("loadexception", this, o, response);
10543             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10544             return;
10545         }
10546         var result;
10547         try {
10548             result = o.reader.read(response);
10549         }catch(e){
10550             this.fireEvent("loadexception", this, o, response, e);
10551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10552             return;
10553         }
10554         
10555         this.fireEvent("load", this, o, o.request.arg);
10556         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10557     },
10558
10559     // private
10560     update : function(dataSet){
10561
10562     },
10563
10564     // private
10565     updateResponse : function(dataSet){
10566
10567     }
10568 });/*
10569  * Based on:
10570  * Ext JS Library 1.1.1
10571  * Copyright(c) 2006-2007, Ext JS, LLC.
10572  *
10573  * Originally Released Under LGPL - original licence link has changed is not relivant.
10574  *
10575  * Fork - LGPL
10576  * <script type="text/javascript">
10577  */
10578
10579 /**
10580  * @class Roo.data.ScriptTagProxy
10581  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10582  * other than the originating domain of the running page.<br><br>
10583  * <p>
10584  * <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
10585  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10586  * <p>
10587  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10588  * source code that is used as the source inside a &lt;script> tag.<br><br>
10589  * <p>
10590  * In order for the browser to process the returned data, the server must wrap the data object
10591  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10592  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10593  * depending on whether the callback name was passed:
10594  * <p>
10595  * <pre><code>
10596 boolean scriptTag = false;
10597 String cb = request.getParameter("callback");
10598 if (cb != null) {
10599     scriptTag = true;
10600     response.setContentType("text/javascript");
10601 } else {
10602     response.setContentType("application/x-json");
10603 }
10604 Writer out = response.getWriter();
10605 if (scriptTag) {
10606     out.write(cb + "(");
10607 }
10608 out.print(dataBlock.toJsonString());
10609 if (scriptTag) {
10610     out.write(");");
10611 }
10612 </pre></code>
10613  *
10614  * @constructor
10615  * @param {Object} config A configuration object.
10616  */
10617 Roo.data.ScriptTagProxy = function(config){
10618     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10619     Roo.apply(this, config);
10620     this.head = document.getElementsByTagName("head")[0];
10621 };
10622
10623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10624
10625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10626     /**
10627      * @cfg {String} url The URL from which to request the data object.
10628      */
10629     /**
10630      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10631      */
10632     timeout : 30000,
10633     /**
10634      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10635      * the server the name of the callback function set up by the load call to process the returned data object.
10636      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10637      * javascript output which calls this named function passing the data object as its only parameter.
10638      */
10639     callbackParam : "callback",
10640     /**
10641      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10642      * name to the request.
10643      */
10644     nocache : true,
10645
10646     /**
10647      * Load data from the configured URL, read the data object into
10648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10649      * process that block using the passed callback.
10650      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10651      * for the request to the remote server.
10652      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10653      * object into a block of Roo.data.Records.
10654      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10655      * The function must be passed <ul>
10656      * <li>The Record block object</li>
10657      * <li>The "arg" argument from the load function</li>
10658      * <li>A boolean success indicator</li>
10659      * </ul>
10660      * @param {Object} scope The scope in which to call the callback
10661      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10662      */
10663     load : function(params, reader, callback, scope, arg){
10664         if(this.fireEvent("beforeload", this, params) !== false){
10665
10666             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10667
10668             var url = this.url;
10669             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10670             if(this.nocache){
10671                 url += "&_dc=" + (new Date().getTime());
10672             }
10673             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10674             var trans = {
10675                 id : transId,
10676                 cb : "stcCallback"+transId,
10677                 scriptId : "stcScript"+transId,
10678                 params : params,
10679                 arg : arg,
10680                 url : url,
10681                 callback : callback,
10682                 scope : scope,
10683                 reader : reader
10684             };
10685             var conn = this;
10686
10687             window[trans.cb] = function(o){
10688                 conn.handleResponse(o, trans);
10689             };
10690
10691             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10692
10693             if(this.autoAbort !== false){
10694                 this.abort();
10695             }
10696
10697             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10698
10699             var script = document.createElement("script");
10700             script.setAttribute("src", url);
10701             script.setAttribute("type", "text/javascript");
10702             script.setAttribute("id", trans.scriptId);
10703             this.head.appendChild(script);
10704
10705             this.trans = trans;
10706         }else{
10707             callback.call(scope||this, null, arg, false);
10708         }
10709     },
10710
10711     // private
10712     isLoading : function(){
10713         return this.trans ? true : false;
10714     },
10715
10716     /**
10717      * Abort the current server request.
10718      */
10719     abort : function(){
10720         if(this.isLoading()){
10721             this.destroyTrans(this.trans);
10722         }
10723     },
10724
10725     // private
10726     destroyTrans : function(trans, isLoaded){
10727         this.head.removeChild(document.getElementById(trans.scriptId));
10728         clearTimeout(trans.timeoutId);
10729         if(isLoaded){
10730             window[trans.cb] = undefined;
10731             try{
10732                 delete window[trans.cb];
10733             }catch(e){}
10734         }else{
10735             // if hasn't been loaded, wait for load to remove it to prevent script error
10736             window[trans.cb] = function(){
10737                 window[trans.cb] = undefined;
10738                 try{
10739                     delete window[trans.cb];
10740                 }catch(e){}
10741             };
10742         }
10743     },
10744
10745     // private
10746     handleResponse : function(o, trans){
10747         this.trans = false;
10748         this.destroyTrans(trans, true);
10749         var result;
10750         try {
10751             result = trans.reader.readRecords(o);
10752         }catch(e){
10753             this.fireEvent("loadexception", this, o, trans.arg, e);
10754             trans.callback.call(trans.scope||window, null, trans.arg, false);
10755             return;
10756         }
10757         this.fireEvent("load", this, o, trans.arg);
10758         trans.callback.call(trans.scope||window, result, trans.arg, true);
10759     },
10760
10761     // private
10762     handleFailure : function(trans){
10763         this.trans = false;
10764         this.destroyTrans(trans, false);
10765         this.fireEvent("loadexception", this, null, trans.arg);
10766         trans.callback.call(trans.scope||window, null, trans.arg, false);
10767     }
10768 });/*
10769  * Based on:
10770  * Ext JS Library 1.1.1
10771  * Copyright(c) 2006-2007, Ext JS, LLC.
10772  *
10773  * Originally Released Under LGPL - original licence link has changed is not relivant.
10774  *
10775  * Fork - LGPL
10776  * <script type="text/javascript">
10777  */
10778
10779 /**
10780  * @class Roo.data.JsonReader
10781  * @extends Roo.data.DataReader
10782  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10783  * based on mappings in a provided Roo.data.Record constructor.
10784  * 
10785  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10786  * in the reply previously. 
10787  * 
10788  * <p>
10789  * Example code:
10790  * <pre><code>
10791 var RecordDef = Roo.data.Record.create([
10792     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10793     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10794 ]);
10795 var myReader = new Roo.data.JsonReader({
10796     totalProperty: "results",    // The property which contains the total dataset size (optional)
10797     root: "rows",                // The property which contains an Array of row objects
10798     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10799 }, RecordDef);
10800 </code></pre>
10801  * <p>
10802  * This would consume a JSON file like this:
10803  * <pre><code>
10804 { 'results': 2, 'rows': [
10805     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10806     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10807 }
10808 </code></pre>
10809  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10811  * paged from the remote server.
10812  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10813  * @cfg {String} root name of the property which contains the Array of row objects.
10814  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10815  * @cfg {Array} fields Array of field definition objects
10816  * @constructor
10817  * Create a new JsonReader
10818  * @param {Object} meta Metadata configuration options
10819  * @param {Object} recordType Either an Array of field definition objects,
10820  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10821  */
10822 Roo.data.JsonReader = function(meta, recordType){
10823     
10824     meta = meta || {};
10825     // set some defaults:
10826     Roo.applyIf(meta, {
10827         totalProperty: 'total',
10828         successProperty : 'success',
10829         root : 'data',
10830         id : 'id'
10831     });
10832     
10833     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10834 };
10835 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10836     
10837     /**
10838      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10839      * Used by Store query builder to append _requestMeta to params.
10840      * 
10841      */
10842     metaFromRemote : false,
10843     /**
10844      * This method is only used by a DataProxy which has retrieved data from a remote server.
10845      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10846      * @return {Object} data A data block which is used by an Roo.data.Store object as
10847      * a cache of Roo.data.Records.
10848      */
10849     read : function(response){
10850         var json = response.responseText;
10851        
10852         var o = /* eval:var:o */ eval("("+json+")");
10853         if(!o) {
10854             throw {message: "JsonReader.read: Json object not found"};
10855         }
10856         
10857         if(o.metaData){
10858             
10859             delete this.ef;
10860             this.metaFromRemote = true;
10861             this.meta = o.metaData;
10862             this.recordType = Roo.data.Record.create(o.metaData.fields);
10863             this.onMetaChange(this.meta, this.recordType, o);
10864         }
10865         return this.readRecords(o);
10866     },
10867
10868     // private function a store will implement
10869     onMetaChange : function(meta, recordType, o){
10870
10871     },
10872
10873     /**
10874          * @ignore
10875          */
10876     simpleAccess: function(obj, subsc) {
10877         return obj[subsc];
10878     },
10879
10880         /**
10881          * @ignore
10882          */
10883     getJsonAccessor: function(){
10884         var re = /[\[\.]/;
10885         return function(expr) {
10886             try {
10887                 return(re.test(expr))
10888                     ? new Function("obj", "return obj." + expr)
10889                     : function(obj){
10890                         return obj[expr];
10891                     };
10892             } catch(e){}
10893             return Roo.emptyFn;
10894         };
10895     }(),
10896
10897     /**
10898      * Create a data block containing Roo.data.Records from an XML document.
10899      * @param {Object} o An object which contains an Array of row objects in the property specified
10900      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10901      * which contains the total size of the dataset.
10902      * @return {Object} data A data block which is used by an Roo.data.Store object as
10903      * a cache of Roo.data.Records.
10904      */
10905     readRecords : function(o){
10906         /**
10907          * After any data loads, the raw JSON data is available for further custom processing.
10908          * @type Object
10909          */
10910         this.o = o;
10911         var s = this.meta, Record = this.recordType,
10912             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10913
10914 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10915         if (!this.ef) {
10916             if(s.totalProperty) {
10917                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10918                 }
10919                 if(s.successProperty) {
10920                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10921                 }
10922                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10923                 if (s.id) {
10924                         var g = this.getJsonAccessor(s.id);
10925                         this.getId = function(rec) {
10926                                 var r = g(rec);  
10927                                 return (r === undefined || r === "") ? null : r;
10928                         };
10929                 } else {
10930                         this.getId = function(){return null;};
10931                 }
10932             this.ef = [];
10933             for(var jj = 0; jj < fl; jj++){
10934                 f = fi[jj];
10935                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10936                 this.ef[jj] = this.getJsonAccessor(map);
10937             }
10938         }
10939
10940         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10941         if(s.totalProperty){
10942             var vt = parseInt(this.getTotal(o), 10);
10943             if(!isNaN(vt)){
10944                 totalRecords = vt;
10945             }
10946         }
10947         if(s.successProperty){
10948             var vs = this.getSuccess(o);
10949             if(vs === false || vs === 'false'){
10950                 success = false;
10951             }
10952         }
10953         var records = [];
10954         for(var i = 0; i < c; i++){
10955                 var n = root[i];
10956             var values = {};
10957             var id = this.getId(n);
10958             for(var j = 0; j < fl; j++){
10959                 f = fi[j];
10960             var v = this.ef[j](n);
10961             if (!f.convert) {
10962                 Roo.log('missing convert for ' + f.name);
10963                 Roo.log(f);
10964                 continue;
10965             }
10966             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10967             }
10968             var record = new Record(values, id);
10969             record.json = n;
10970             records[i] = record;
10971         }
10972         return {
10973             raw : o,
10974             success : success,
10975             records : records,
10976             totalRecords : totalRecords
10977         };
10978     }
10979 });/*
10980  * Based on:
10981  * Ext JS Library 1.1.1
10982  * Copyright(c) 2006-2007, Ext JS, LLC.
10983  *
10984  * Originally Released Under LGPL - original licence link has changed is not relivant.
10985  *
10986  * Fork - LGPL
10987  * <script type="text/javascript">
10988  */
10989
10990 /**
10991  * @class Roo.data.ArrayReader
10992  * @extends Roo.data.DataReader
10993  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10994  * Each element of that Array represents a row of data fields. The
10995  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10996  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10997  * <p>
10998  * Example code:.
10999  * <pre><code>
11000 var RecordDef = Roo.data.Record.create([
11001     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11002     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11003 ]);
11004 var myReader = new Roo.data.ArrayReader({
11005     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11006 }, RecordDef);
11007 </code></pre>
11008  * <p>
11009  * This would consume an Array like this:
11010  * <pre><code>
11011 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11012   </code></pre>
11013  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11014  * @constructor
11015  * Create a new JsonReader
11016  * @param {Object} meta Metadata configuration options.
11017  * @param {Object} recordType Either an Array of field definition objects
11018  * as specified to {@link Roo.data.Record#create},
11019  * or an {@link Roo.data.Record} object
11020  * created using {@link Roo.data.Record#create}.
11021  */
11022 Roo.data.ArrayReader = function(meta, recordType){
11023     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11024 };
11025
11026 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11027     /**
11028      * Create a data block containing Roo.data.Records from an XML document.
11029      * @param {Object} o An Array of row objects which represents the dataset.
11030      * @return {Object} data A data block which is used by an Roo.data.Store object as
11031      * a cache of Roo.data.Records.
11032      */
11033     readRecords : function(o){
11034         var sid = this.meta ? this.meta.id : null;
11035         var recordType = this.recordType, fields = recordType.prototype.fields;
11036         var records = [];
11037         var root = o;
11038             for(var i = 0; i < root.length; i++){
11039                     var n = root[i];
11040                 var values = {};
11041                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11042                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11043                 var f = fields.items[j];
11044                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11045                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11046                 v = f.convert(v);
11047                 values[f.name] = v;
11048             }
11049                 var record = new recordType(values, id);
11050                 record.json = n;
11051                 records[records.length] = record;
11052             }
11053             return {
11054                 records : records,
11055                 totalRecords : records.length
11056             };
11057     }
11058 });/*
11059  * - LGPL
11060  * * 
11061  */
11062
11063 /**
11064  * @class Roo.bootstrap.ComboBox
11065  * @extends Roo.bootstrap.TriggerField
11066  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11067  * @cfg {Boolean} append (true|false) default false
11068  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11069  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11070  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11071  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11072  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11073  * @cfg {Boolean} animate default true
11074  * @cfg {Boolean} emptyResultText only for touch device
11075  * @constructor
11076  * Create a new ComboBox.
11077  * @param {Object} config Configuration options
11078  */
11079 Roo.bootstrap.ComboBox = function(config){
11080     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11081     this.addEvents({
11082         /**
11083          * @event expand
11084          * Fires when the dropdown list is expanded
11085              * @param {Roo.bootstrap.ComboBox} combo This combo box
11086              */
11087         'expand' : true,
11088         /**
11089          * @event collapse
11090          * Fires when the dropdown list is collapsed
11091              * @param {Roo.bootstrap.ComboBox} combo This combo box
11092              */
11093         'collapse' : true,
11094         /**
11095          * @event beforeselect
11096          * Fires before a list item is selected. Return false to cancel the selection.
11097              * @param {Roo.bootstrap.ComboBox} combo This combo box
11098              * @param {Roo.data.Record} record The data record returned from the underlying store
11099              * @param {Number} index The index of the selected item in the dropdown list
11100              */
11101         'beforeselect' : true,
11102         /**
11103          * @event select
11104          * Fires when a list item is selected
11105              * @param {Roo.bootstrap.ComboBox} combo This combo box
11106              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11107              * @param {Number} index The index of the selected item in the dropdown list
11108              */
11109         'select' : true,
11110         /**
11111          * @event beforequery
11112          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11113          * The event object passed has these properties:
11114              * @param {Roo.bootstrap.ComboBox} combo This combo box
11115              * @param {String} query The query
11116              * @param {Boolean} forceAll true to force "all" query
11117              * @param {Boolean} cancel true to cancel the query
11118              * @param {Object} e The query event object
11119              */
11120         'beforequery': true,
11121          /**
11122          * @event add
11123          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11124              * @param {Roo.bootstrap.ComboBox} combo This combo box
11125              */
11126         'add' : true,
11127         /**
11128          * @event edit
11129          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11130              * @param {Roo.bootstrap.ComboBox} combo This combo box
11131              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11132              */
11133         'edit' : true,
11134         /**
11135          * @event remove
11136          * Fires when the remove value from the combobox array
11137              * @param {Roo.bootstrap.ComboBox} combo This combo box
11138              */
11139         'remove' : true,
11140         /**
11141          * @event specialfilter
11142          * Fires when specialfilter
11143             * @param {Roo.bootstrap.ComboBox} combo This combo box
11144             */
11145         'specialfilter' : true
11146         
11147     });
11148     
11149     this.item = [];
11150     this.tickItems = [];
11151     
11152     this.selectedIndex = -1;
11153     if(this.mode == 'local'){
11154         if(config.queryDelay === undefined){
11155             this.queryDelay = 10;
11156         }
11157         if(config.minChars === undefined){
11158             this.minChars = 0;
11159         }
11160     }
11161 };
11162
11163 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11164      
11165     /**
11166      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11167      * rendering into an Roo.Editor, defaults to false)
11168      */
11169     /**
11170      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11171      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11172      */
11173     /**
11174      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11175      */
11176     /**
11177      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11178      * the dropdown list (defaults to undefined, with no header element)
11179      */
11180
11181      /**
11182      * @cfg {String/Roo.Template} tpl The template to use to render the output
11183      */
11184      
11185      /**
11186      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11187      */
11188     listWidth: undefined,
11189     /**
11190      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11191      * mode = 'remote' or 'text' if mode = 'local')
11192      */
11193     displayField: undefined,
11194     
11195     /**
11196      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11197      * mode = 'remote' or 'value' if mode = 'local'). 
11198      * Note: use of a valueField requires the user make a selection
11199      * in order for a value to be mapped.
11200      */
11201     valueField: undefined,
11202     
11203     
11204     /**
11205      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11206      * field's data value (defaults to the underlying DOM element's name)
11207      */
11208     hiddenName: undefined,
11209     /**
11210      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11211      */
11212     listClass: '',
11213     /**
11214      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11215      */
11216     selectedClass: 'active',
11217     
11218     /**
11219      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11220      */
11221     shadow:'sides',
11222     /**
11223      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11224      * anchor positions (defaults to 'tl-bl')
11225      */
11226     listAlign: 'tl-bl?',
11227     /**
11228      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11229      */
11230     maxHeight: 300,
11231     /**
11232      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11233      * query specified by the allQuery config option (defaults to 'query')
11234      */
11235     triggerAction: 'query',
11236     /**
11237      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11238      * (defaults to 4, does not apply if editable = false)
11239      */
11240     minChars : 4,
11241     /**
11242      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11243      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11244      */
11245     typeAhead: false,
11246     /**
11247      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11248      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11249      */
11250     queryDelay: 500,
11251     /**
11252      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11253      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11254      */
11255     pageSize: 0,
11256     /**
11257      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11258      * when editable = true (defaults to false)
11259      */
11260     selectOnFocus:false,
11261     /**
11262      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11263      */
11264     queryParam: 'query',
11265     /**
11266      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11267      * when mode = 'remote' (defaults to 'Loading...')
11268      */
11269     loadingText: 'Loading...',
11270     /**
11271      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11272      */
11273     resizable: false,
11274     /**
11275      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11276      */
11277     handleHeight : 8,
11278     /**
11279      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11280      * traditional select (defaults to true)
11281      */
11282     editable: true,
11283     /**
11284      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11285      */
11286     allQuery: '',
11287     /**
11288      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11289      */
11290     mode: 'remote',
11291     /**
11292      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11293      * listWidth has a higher value)
11294      */
11295     minListWidth : 70,
11296     /**
11297      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11298      * allow the user to set arbitrary text into the field (defaults to false)
11299      */
11300     forceSelection:false,
11301     /**
11302      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11303      * if typeAhead = true (defaults to 250)
11304      */
11305     typeAheadDelay : 250,
11306     /**
11307      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11308      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11309      */
11310     valueNotFoundText : undefined,
11311     /**
11312      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11313      */
11314     blockFocus : false,
11315     
11316     /**
11317      * @cfg {Boolean} disableClear Disable showing of clear button.
11318      */
11319     disableClear : false,
11320     /**
11321      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11322      */
11323     alwaysQuery : false,
11324     
11325     /**
11326      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11327      */
11328     multiple : false,
11329     
11330     /**
11331      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11332      */
11333     invalidClass : "has-warning",
11334     
11335     /**
11336      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11337      */
11338     validClass : "has-success",
11339     
11340     /**
11341      * @cfg {Boolean} specialFilter (true|false) special filter default false
11342      */
11343     specialFilter : false,
11344     
11345     /**
11346      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11347      */
11348     mobileTouchView : true,
11349     
11350     //private
11351     addicon : false,
11352     editicon: false,
11353     
11354     page: 0,
11355     hasQuery: false,
11356     append: false,
11357     loadNext: false,
11358     autoFocus : true,
11359     tickable : false,
11360     btnPosition : 'right',
11361     triggerList : true,
11362     showToggleBtn : true,
11363     animate : true,
11364     emptyResultText: 'Empty',
11365     // element that contains real text value.. (when hidden is used..)
11366     
11367     getAutoCreate : function()
11368     {
11369         var cfg = false;
11370         
11371         /*
11372          * Touch Devices
11373          */
11374         
11375         if(Roo.isTouch && this.mobileTouchView){
11376             cfg = this.getAutoCreateTouchView();
11377             return cfg;;
11378         }
11379         
11380         /*
11381          *  Normal ComboBox
11382          */
11383         if(!this.tickable){
11384             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11385             return cfg;
11386         }
11387         
11388         /*
11389          *  ComboBox with tickable selections
11390          */
11391              
11392         var align = this.labelAlign || this.parentLabelAlign();
11393         
11394         cfg = {
11395             cls : 'form-group roo-combobox-tickable' //input-group
11396         };
11397         
11398         var buttons = {
11399             tag : 'div',
11400             cls : 'tickable-buttons',
11401             cn : [
11402                 {
11403                     tag : 'button',
11404                     type : 'button',
11405                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11406                     html : 'Edit'
11407                 },
11408                 {
11409                     tag : 'button',
11410                     type : 'button',
11411                     name : 'ok',
11412                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11413                     html : 'Done'
11414                 },
11415                 {
11416                     tag : 'button',
11417                     type : 'button',
11418                     name : 'cancel',
11419                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11420                     html : 'Cancel'
11421                 }
11422             ]
11423         };
11424         
11425         if(this.editable){
11426             buttons.cn.unshift({
11427                 tag: 'input',
11428                 cls: 'select2-search-field-input'
11429             });
11430         }
11431         
11432         var _this = this;
11433         
11434         Roo.each(buttons.cn, function(c){
11435             if (_this.size) {
11436                 c.cls += ' btn-' + _this.size;
11437             }
11438
11439             if (_this.disabled) {
11440                 c.disabled = true;
11441             }
11442         });
11443         
11444         var box = {
11445             tag: 'div',
11446             cn: [
11447                 {
11448                     tag: 'input',
11449                     type : 'hidden',
11450                     cls: 'form-hidden-field'
11451                 },
11452                 {
11453                     tag: 'ul',
11454                     cls: 'select2-choices',
11455                     cn:[
11456                         {
11457                             tag: 'li',
11458                             cls: 'select2-search-field',
11459                             cn: [
11460
11461                                 buttons
11462                             ]
11463                         }
11464                     ]
11465                 }
11466             ]
11467         }
11468         
11469         var combobox = {
11470             cls: 'select2-container input-group select2-container-multi',
11471             cn: [
11472                 box
11473 //                {
11474 //                    tag: 'ul',
11475 //                    cls: 'typeahead typeahead-long dropdown-menu',
11476 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11477 //                }
11478             ]
11479         };
11480         
11481         if(this.hasFeedback && !this.allowBlank){
11482             
11483             var feedback = {
11484                 tag: 'span',
11485                 cls: 'glyphicon form-control-feedback'
11486             };
11487
11488             combobox.cn.push(feedback);
11489         }
11490         
11491         if (align ==='left' && this.fieldLabel.length) {
11492             
11493                 Roo.log("left and has label");
11494                 cfg.cn = [
11495                     
11496                     {
11497                         tag: 'label',
11498                         'for' :  id,
11499                         cls : 'control-label col-sm-' + this.labelWidth,
11500                         html : this.fieldLabel
11501                         
11502                     },
11503                     {
11504                         cls : "col-sm-" + (12 - this.labelWidth), 
11505                         cn: [
11506                             combobox
11507                         ]
11508                     }
11509                     
11510                 ];
11511         } else if ( this.fieldLabel.length) {
11512                 Roo.log(" label");
11513                  cfg.cn = [
11514                    
11515                     {
11516                         tag: 'label',
11517                         //cls : 'input-group-addon',
11518                         html : this.fieldLabel
11519                         
11520                     },
11521                     
11522                     combobox
11523                     
11524                 ];
11525
11526         } else {
11527             
11528                 Roo.log(" no label && no align");
11529                 cfg = combobox
11530                      
11531                 
11532         }
11533          
11534         var settings=this;
11535         ['xs','sm','md','lg'].map(function(size){
11536             if (settings[size]) {
11537                 cfg.cls += ' col-' + size + '-' + settings[size];
11538             }
11539         });
11540         
11541         return cfg;
11542         
11543     },
11544     
11545     // private
11546     initEvents: function()
11547     {
11548         
11549         if (!this.store) {
11550             throw "can not find store for combo";
11551         }
11552         
11553         this.store = Roo.factory(this.store, Roo.data);
11554         
11555         /*
11556          * Touch Devices
11557          */
11558         
11559         if(Roo.isTouch && this.mobileTouchView){
11560             this.initTouchView();
11561             return;
11562         }
11563         
11564         if(this.tickable){
11565             this.initTickableEvents();
11566             return;
11567         }
11568         
11569         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11570         
11571         if(this.hiddenName){
11572             
11573             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11574             
11575             this.hiddenField.dom.value =
11576                 this.hiddenValue !== undefined ? this.hiddenValue :
11577                 this.value !== undefined ? this.value : '';
11578
11579             // prevent input submission
11580             this.el.dom.removeAttribute('name');
11581             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11582              
11583              
11584         }
11585         //if(Roo.isGecko){
11586         //    this.el.dom.setAttribute('autocomplete', 'off');
11587         //}
11588         
11589         var cls = 'x-combo-list';
11590         
11591         //this.list = new Roo.Layer({
11592         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11593         //});
11594         
11595         var _this = this;
11596         
11597         (function(){
11598             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11599             _this.list.setWidth(lw);
11600         }).defer(100);
11601         
11602         this.list.on('mouseover', this.onViewOver, this);
11603         this.list.on('mousemove', this.onViewMove, this);
11604         
11605         this.list.on('scroll', this.onViewScroll, this);
11606         
11607         /*
11608         this.list.swallowEvent('mousewheel');
11609         this.assetHeight = 0;
11610
11611         if(this.title){
11612             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11613             this.assetHeight += this.header.getHeight();
11614         }
11615
11616         this.innerList = this.list.createChild({cls:cls+'-inner'});
11617         this.innerList.on('mouseover', this.onViewOver, this);
11618         this.innerList.on('mousemove', this.onViewMove, this);
11619         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11620         
11621         if(this.allowBlank && !this.pageSize && !this.disableClear){
11622             this.footer = this.list.createChild({cls:cls+'-ft'});
11623             this.pageTb = new Roo.Toolbar(this.footer);
11624            
11625         }
11626         if(this.pageSize){
11627             this.footer = this.list.createChild({cls:cls+'-ft'});
11628             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11629                     {pageSize: this.pageSize});
11630             
11631         }
11632         
11633         if (this.pageTb && this.allowBlank && !this.disableClear) {
11634             var _this = this;
11635             this.pageTb.add(new Roo.Toolbar.Fill(), {
11636                 cls: 'x-btn-icon x-btn-clear',
11637                 text: '&#160;',
11638                 handler: function()
11639                 {
11640                     _this.collapse();
11641                     _this.clearValue();
11642                     _this.onSelect(false, -1);
11643                 }
11644             });
11645         }
11646         if (this.footer) {
11647             this.assetHeight += this.footer.getHeight();
11648         }
11649         */
11650             
11651         if(!this.tpl){
11652             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11653         }
11654
11655         this.view = new Roo.View(this.list, this.tpl, {
11656             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11657         });
11658         //this.view.wrapEl.setDisplayed(false);
11659         this.view.on('click', this.onViewClick, this);
11660         
11661         
11662         
11663         this.store.on('beforeload', this.onBeforeLoad, this);
11664         this.store.on('load', this.onLoad, this);
11665         this.store.on('loadexception', this.onLoadException, this);
11666         /*
11667         if(this.resizable){
11668             this.resizer = new Roo.Resizable(this.list,  {
11669                pinned:true, handles:'se'
11670             });
11671             this.resizer.on('resize', function(r, w, h){
11672                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11673                 this.listWidth = w;
11674                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11675                 this.restrictHeight();
11676             }, this);
11677             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11678         }
11679         */
11680         if(!this.editable){
11681             this.editable = true;
11682             this.setEditable(false);
11683         }
11684         
11685         /*
11686         
11687         if (typeof(this.events.add.listeners) != 'undefined') {
11688             
11689             this.addicon = this.wrap.createChild(
11690                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11691        
11692             this.addicon.on('click', function(e) {
11693                 this.fireEvent('add', this);
11694             }, this);
11695         }
11696         if (typeof(this.events.edit.listeners) != 'undefined') {
11697             
11698             this.editicon = this.wrap.createChild(
11699                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11700             if (this.addicon) {
11701                 this.editicon.setStyle('margin-left', '40px');
11702             }
11703             this.editicon.on('click', function(e) {
11704                 
11705                 // we fire even  if inothing is selected..
11706                 this.fireEvent('edit', this, this.lastData );
11707                 
11708             }, this);
11709         }
11710         */
11711         
11712         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11713             "up" : function(e){
11714                 this.inKeyMode = true;
11715                 this.selectPrev();
11716             },
11717
11718             "down" : function(e){
11719                 if(!this.isExpanded()){
11720                     this.onTriggerClick();
11721                 }else{
11722                     this.inKeyMode = true;
11723                     this.selectNext();
11724                 }
11725             },
11726
11727             "enter" : function(e){
11728 //                this.onViewClick();
11729                 //return true;
11730                 this.collapse();
11731                 
11732                 if(this.fireEvent("specialkey", this, e)){
11733                     this.onViewClick(false);
11734                 }
11735                 
11736                 return true;
11737             },
11738
11739             "esc" : function(e){
11740                 this.collapse();
11741             },
11742
11743             "tab" : function(e){
11744                 this.collapse();
11745                 
11746                 if(this.fireEvent("specialkey", this, e)){
11747                     this.onViewClick(false);
11748                 }
11749                 
11750                 return true;
11751             },
11752
11753             scope : this,
11754
11755             doRelay : function(foo, bar, hname){
11756                 if(hname == 'down' || this.scope.isExpanded()){
11757                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11758                 }
11759                 return true;
11760             },
11761
11762             forceKeyDown: true
11763         });
11764         
11765         
11766         this.queryDelay = Math.max(this.queryDelay || 10,
11767                 this.mode == 'local' ? 10 : 250);
11768         
11769         
11770         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11771         
11772         if(this.typeAhead){
11773             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11774         }
11775         if(this.editable !== false){
11776             this.inputEl().on("keyup", this.onKeyUp, this);
11777         }
11778         if(this.forceSelection){
11779             this.inputEl().on('blur', this.doForce, this);
11780         }
11781         
11782         if(this.multiple){
11783             this.choices = this.el.select('ul.select2-choices', true).first();
11784             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11785         }
11786     },
11787     
11788     initTickableEvents: function()
11789     {   
11790         this.createList();
11791         
11792         if(this.hiddenName){
11793             
11794             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11795             
11796             this.hiddenField.dom.value =
11797                 this.hiddenValue !== undefined ? this.hiddenValue :
11798                 this.value !== undefined ? this.value : '';
11799
11800             // prevent input submission
11801             this.el.dom.removeAttribute('name');
11802             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11803              
11804              
11805         }
11806         
11807 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11808         
11809         this.choices = this.el.select('ul.select2-choices', true).first();
11810         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11811         if(this.triggerList){
11812             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11813         }
11814          
11815         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11816         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11817         
11818         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11819         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11820         
11821         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11822         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11823         
11824         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11825         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11826         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11827         
11828         this.okBtn.hide();
11829         this.cancelBtn.hide();
11830         
11831         var _this = this;
11832         
11833         (function(){
11834             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11835             _this.list.setWidth(lw);
11836         }).defer(100);
11837         
11838         this.list.on('mouseover', this.onViewOver, this);
11839         this.list.on('mousemove', this.onViewMove, this);
11840         
11841         this.list.on('scroll', this.onViewScroll, this);
11842         
11843         if(!this.tpl){
11844             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>';
11845         }
11846
11847         this.view = new Roo.View(this.list, this.tpl, {
11848             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11849         });
11850         
11851         //this.view.wrapEl.setDisplayed(false);
11852         this.view.on('click', this.onViewClick, this);
11853         
11854         
11855         
11856         this.store.on('beforeload', this.onBeforeLoad, this);
11857         this.store.on('load', this.onLoad, this);
11858         this.store.on('loadexception', this.onLoadException, this);
11859         
11860         if(this.editable){
11861             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11862                 "up" : function(e){
11863                     this.inKeyMode = true;
11864                     this.selectPrev();
11865                 },
11866
11867                 "down" : function(e){
11868                     this.inKeyMode = true;
11869                     this.selectNext();
11870                 },
11871
11872                 "enter" : function(e){
11873                     if(this.fireEvent("specialkey", this, e)){
11874                         this.onViewClick(false);
11875                     }
11876                     
11877                     return true;
11878                 },
11879
11880                 "esc" : function(e){
11881                     this.onTickableFooterButtonClick(e, false, false);
11882                 },
11883
11884                 "tab" : function(e){
11885                     this.fireEvent("specialkey", this, e);
11886                     
11887                     this.onTickableFooterButtonClick(e, false, false);
11888                     
11889                     return true;
11890                 },
11891
11892                 scope : this,
11893
11894                 doRelay : function(e, fn, key){
11895                     if(this.scope.isExpanded()){
11896                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11897                     }
11898                     return true;
11899                 },
11900
11901                 forceKeyDown: true
11902             });
11903         }
11904         
11905         this.queryDelay = Math.max(this.queryDelay || 10,
11906                 this.mode == 'local' ? 10 : 250);
11907         
11908         
11909         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11910         
11911         if(this.typeAhead){
11912             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11913         }
11914         
11915         if(this.editable !== false){
11916             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11917         }
11918         
11919     },
11920
11921     onDestroy : function(){
11922         if(this.view){
11923             this.view.setStore(null);
11924             this.view.el.removeAllListeners();
11925             this.view.el.remove();
11926             this.view.purgeListeners();
11927         }
11928         if(this.list){
11929             this.list.dom.innerHTML  = '';
11930         }
11931         
11932         if(this.store){
11933             this.store.un('beforeload', this.onBeforeLoad, this);
11934             this.store.un('load', this.onLoad, this);
11935             this.store.un('loadexception', this.onLoadException, this);
11936         }
11937         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11938     },
11939
11940     // private
11941     fireKey : function(e){
11942         if(e.isNavKeyPress() && !this.list.isVisible()){
11943             this.fireEvent("specialkey", this, e);
11944         }
11945     },
11946
11947     // private
11948     onResize: function(w, h){
11949 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11950 //        
11951 //        if(typeof w != 'number'){
11952 //            // we do not handle it!?!?
11953 //            return;
11954 //        }
11955 //        var tw = this.trigger.getWidth();
11956 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11957 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11958 //        var x = w - tw;
11959 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11960 //            
11961 //        //this.trigger.setStyle('left', x+'px');
11962 //        
11963 //        if(this.list && this.listWidth === undefined){
11964 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11965 //            this.list.setWidth(lw);
11966 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11967 //        }
11968         
11969     
11970         
11971     },
11972
11973     /**
11974      * Allow or prevent the user from directly editing the field text.  If false is passed,
11975      * the user will only be able to select from the items defined in the dropdown list.  This method
11976      * is the runtime equivalent of setting the 'editable' config option at config time.
11977      * @param {Boolean} value True to allow the user to directly edit the field text
11978      */
11979     setEditable : function(value){
11980         if(value == this.editable){
11981             return;
11982         }
11983         this.editable = value;
11984         if(!value){
11985             this.inputEl().dom.setAttribute('readOnly', true);
11986             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11987             this.inputEl().addClass('x-combo-noedit');
11988         }else{
11989             this.inputEl().dom.setAttribute('readOnly', false);
11990             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11991             this.inputEl().removeClass('x-combo-noedit');
11992         }
11993     },
11994
11995     // private
11996     
11997     onBeforeLoad : function(combo,opts){
11998         if(!this.hasFocus){
11999             return;
12000         }
12001          if (!opts.add) {
12002             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12003          }
12004         this.restrictHeight();
12005         this.selectedIndex = -1;
12006     },
12007
12008     // private
12009     onLoad : function(){
12010         
12011         this.hasQuery = false;
12012         
12013         if(!this.hasFocus){
12014             return;
12015         }
12016         
12017         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12018             this.loading.hide();
12019         }
12020              
12021         if(this.store.getCount() > 0){
12022             this.expand();
12023             this.restrictHeight();
12024             if(this.lastQuery == this.allQuery){
12025                 if(this.editable && !this.tickable){
12026                     this.inputEl().dom.select();
12027                 }
12028                 
12029                 if(
12030                     !this.selectByValue(this.value, true) &&
12031                     this.autoFocus && 
12032                     (
12033                         !this.store.lastOptions ||
12034                         typeof(this.store.lastOptions.add) == 'undefined' || 
12035                         this.store.lastOptions.add != true
12036                     )
12037                 ){
12038                     this.select(0, true);
12039                 }
12040             }else{
12041                 if(this.autoFocus){
12042                     this.selectNext();
12043                 }
12044                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12045                     this.taTask.delay(this.typeAheadDelay);
12046                 }
12047             }
12048         }else{
12049             this.onEmptyResults();
12050         }
12051         
12052         //this.el.focus();
12053     },
12054     // private
12055     onLoadException : function()
12056     {
12057         this.hasQuery = false;
12058         
12059         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12060             this.loading.hide();
12061         }
12062         
12063         if(this.tickable && this.editable){
12064             return;
12065         }
12066         
12067         this.collapse();
12068         
12069         Roo.log(this.store.reader.jsonData);
12070         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12071             // fixme
12072             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12073         }
12074         
12075         
12076     },
12077     // private
12078     onTypeAhead : function(){
12079         if(this.store.getCount() > 0){
12080             var r = this.store.getAt(0);
12081             var newValue = r.data[this.displayField];
12082             var len = newValue.length;
12083             var selStart = this.getRawValue().length;
12084             
12085             if(selStart != len){
12086                 this.setRawValue(newValue);
12087                 this.selectText(selStart, newValue.length);
12088             }
12089         }
12090     },
12091
12092     // private
12093     onSelect : function(record, index){
12094         
12095         if(this.fireEvent('beforeselect', this, record, index) !== false){
12096         
12097             this.setFromData(index > -1 ? record.data : false);
12098             
12099             this.collapse();
12100             this.fireEvent('select', this, record, index);
12101         }
12102     },
12103
12104     /**
12105      * Returns the currently selected field value or empty string if no value is set.
12106      * @return {String} value The selected value
12107      */
12108     getValue : function(){
12109         
12110         if(this.multiple){
12111             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12112         }
12113         
12114         if(this.valueField){
12115             return typeof this.value != 'undefined' ? this.value : '';
12116         }else{
12117             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12118         }
12119     },
12120
12121     /**
12122      * Clears any text/value currently set in the field
12123      */
12124     clearValue : function(){
12125         if(this.hiddenField){
12126             this.hiddenField.dom.value = '';
12127         }
12128         this.value = '';
12129         this.setRawValue('');
12130         this.lastSelectionText = '';
12131         this.lastData = false;
12132         
12133         var close = this.closeTriggerEl();
12134         
12135         if(close){
12136             close.hide();
12137         }
12138         
12139     },
12140
12141     /**
12142      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12143      * will be displayed in the field.  If the value does not match the data value of an existing item,
12144      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12145      * Otherwise the field will be blank (although the value will still be set).
12146      * @param {String} value The value to match
12147      */
12148     setValue : function(v){
12149         if(this.multiple){
12150             this.syncValue();
12151             return;
12152         }
12153         
12154         var text = v;
12155         if(this.valueField){
12156             var r = this.findRecord(this.valueField, v);
12157             if(r){
12158                 text = r.data[this.displayField];
12159             }else if(this.valueNotFoundText !== undefined){
12160                 text = this.valueNotFoundText;
12161             }
12162         }
12163         this.lastSelectionText = text;
12164         if(this.hiddenField){
12165             this.hiddenField.dom.value = v;
12166         }
12167         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12168         this.value = v;
12169         
12170         var close = this.closeTriggerEl();
12171         
12172         if(close){
12173             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12174         }
12175     },
12176     /**
12177      * @property {Object} the last set data for the element
12178      */
12179     
12180     lastData : false,
12181     /**
12182      * Sets the value of the field based on a object which is related to the record format for the store.
12183      * @param {Object} value the value to set as. or false on reset?
12184      */
12185     setFromData : function(o){
12186         
12187         if(this.multiple){
12188             this.addItem(o);
12189             return;
12190         }
12191             
12192         var dv = ''; // display value
12193         var vv = ''; // value value..
12194         this.lastData = o;
12195         if (this.displayField) {
12196             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12197         } else {
12198             // this is an error condition!!!
12199             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12200         }
12201         
12202         if(this.valueField){
12203             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12204         }
12205         
12206         var close = this.closeTriggerEl();
12207         
12208         if(close){
12209             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12210         }
12211         
12212         if(this.hiddenField){
12213             this.hiddenField.dom.value = vv;
12214             
12215             this.lastSelectionText = dv;
12216             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12217             this.value = vv;
12218             return;
12219         }
12220         // no hidden field.. - we store the value in 'value', but still display
12221         // display field!!!!
12222         this.lastSelectionText = dv;
12223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12224         this.value = vv;
12225         
12226         
12227         
12228     },
12229     // private
12230     reset : function(){
12231         // overridden so that last data is reset..
12232         
12233         if(this.multiple){
12234             this.clearItem();
12235             return;
12236         }
12237         
12238         this.setValue(this.originalValue);
12239         this.clearInvalid();
12240         this.lastData = false;
12241         if (this.view) {
12242             this.view.clearSelections();
12243         }
12244     },
12245     // private
12246     findRecord : function(prop, value){
12247         var record;
12248         if(this.store.getCount() > 0){
12249             this.store.each(function(r){
12250                 if(r.data[prop] == value){
12251                     record = r;
12252                     return false;
12253                 }
12254                 return true;
12255             });
12256         }
12257         return record;
12258     },
12259     
12260     getName: function()
12261     {
12262         // returns hidden if it's set..
12263         if (!this.rendered) {return ''};
12264         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12265         
12266     },
12267     // private
12268     onViewMove : function(e, t){
12269         this.inKeyMode = false;
12270     },
12271
12272     // private
12273     onViewOver : function(e, t){
12274         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12275             return;
12276         }
12277         var item = this.view.findItemFromChild(t);
12278         
12279         if(item){
12280             var index = this.view.indexOf(item);
12281             this.select(index, false);
12282         }
12283     },
12284
12285     // private
12286     onViewClick : function(view, doFocus, el, e)
12287     {
12288         var index = this.view.getSelectedIndexes()[0];
12289         
12290         var r = this.store.getAt(index);
12291         
12292         if(this.tickable){
12293             
12294             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12295                 return;
12296             }
12297             
12298             var rm = false;
12299             var _this = this;
12300             
12301             Roo.each(this.tickItems, function(v,k){
12302                 
12303                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12304                     _this.tickItems.splice(k, 1);
12305                     
12306                     if(typeof(e) == 'undefined' && view == false){
12307                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12308                     }
12309                     
12310                     rm = true;
12311                     return;
12312                 }
12313             });
12314             
12315             if(rm){
12316                 return;
12317             }
12318             
12319             this.tickItems.push(r.data);
12320             
12321             if(typeof(e) == 'undefined' && view == false){
12322                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12323             }
12324                     
12325             return;
12326         }
12327         
12328         if(r){
12329             this.onSelect(r, index);
12330         }
12331         if(doFocus !== false && !this.blockFocus){
12332             this.inputEl().focus();
12333         }
12334     },
12335
12336     // private
12337     restrictHeight : function(){
12338         //this.innerList.dom.style.height = '';
12339         //var inner = this.innerList.dom;
12340         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12341         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12342         //this.list.beginUpdate();
12343         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12344         this.list.alignTo(this.inputEl(), this.listAlign);
12345         this.list.alignTo(this.inputEl(), this.listAlign);
12346         //this.list.endUpdate();
12347     },
12348
12349     // private
12350     onEmptyResults : function(){
12351         
12352         if(this.tickable && this.editable){
12353             this.restrictHeight();
12354             return;
12355         }
12356         
12357         this.collapse();
12358     },
12359
12360     /**
12361      * Returns true if the dropdown list is expanded, else false.
12362      */
12363     isExpanded : function(){
12364         return this.list.isVisible();
12365     },
12366
12367     /**
12368      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12369      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12370      * @param {String} value The data value of the item to select
12371      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12372      * selected item if it is not currently in view (defaults to true)
12373      * @return {Boolean} True if the value matched an item in the list, else false
12374      */
12375     selectByValue : function(v, scrollIntoView){
12376         if(v !== undefined && v !== null){
12377             var r = this.findRecord(this.valueField || this.displayField, v);
12378             if(r){
12379                 this.select(this.store.indexOf(r), scrollIntoView);
12380                 return true;
12381             }
12382         }
12383         return false;
12384     },
12385
12386     /**
12387      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12388      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12389      * @param {Number} index The zero-based index of the list item to select
12390      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12391      * selected item if it is not currently in view (defaults to true)
12392      */
12393     select : function(index, scrollIntoView){
12394         this.selectedIndex = index;
12395         this.view.select(index);
12396         if(scrollIntoView !== false){
12397             var el = this.view.getNode(index);
12398             /*
12399              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12400              */
12401             if(el){
12402                 this.list.scrollChildIntoView(el, false);
12403             }
12404         }
12405     },
12406
12407     // private
12408     selectNext : function(){
12409         var ct = this.store.getCount();
12410         if(ct > 0){
12411             if(this.selectedIndex == -1){
12412                 this.select(0);
12413             }else if(this.selectedIndex < ct-1){
12414                 this.select(this.selectedIndex+1);
12415             }
12416         }
12417     },
12418
12419     // private
12420     selectPrev : function(){
12421         var ct = this.store.getCount();
12422         if(ct > 0){
12423             if(this.selectedIndex == -1){
12424                 this.select(0);
12425             }else if(this.selectedIndex != 0){
12426                 this.select(this.selectedIndex-1);
12427             }
12428         }
12429     },
12430
12431     // private
12432     onKeyUp : function(e){
12433         if(this.editable !== false && !e.isSpecialKey()){
12434             this.lastKey = e.getKey();
12435             this.dqTask.delay(this.queryDelay);
12436         }
12437     },
12438
12439     // private
12440     validateBlur : function(){
12441         return !this.list || !this.list.isVisible();   
12442     },
12443
12444     // private
12445     initQuery : function(){
12446         
12447         var v = this.getRawValue();
12448         
12449         if(this.tickable && this.editable){
12450             v = this.tickableInputEl().getValue();
12451         }
12452         
12453         this.doQuery(v);
12454     },
12455
12456     // private
12457     doForce : function(){
12458         if(this.inputEl().dom.value.length > 0){
12459             this.inputEl().dom.value =
12460                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12461              
12462         }
12463     },
12464
12465     /**
12466      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12467      * query allowing the query action to be canceled if needed.
12468      * @param {String} query The SQL query to execute
12469      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12470      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12471      * saved in the current store (defaults to false)
12472      */
12473     doQuery : function(q, forceAll){
12474         
12475         if(q === undefined || q === null){
12476             q = '';
12477         }
12478         var qe = {
12479             query: q,
12480             forceAll: forceAll,
12481             combo: this,
12482             cancel:false
12483         };
12484         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12485             return false;
12486         }
12487         q = qe.query;
12488         
12489         forceAll = qe.forceAll;
12490         if(forceAll === true || (q.length >= this.minChars)){
12491             
12492             this.hasQuery = true;
12493             
12494             if(this.lastQuery != q || this.alwaysQuery){
12495                 this.lastQuery = q;
12496                 if(this.mode == 'local'){
12497                     this.selectedIndex = -1;
12498                     if(forceAll){
12499                         this.store.clearFilter();
12500                     }else{
12501                         
12502                         if(this.specialFilter){
12503                             this.fireEvent('specialfilter', this);
12504                             this.onLoad();
12505                             return;
12506                         }
12507                         
12508                         this.store.filter(this.displayField, q);
12509                     }
12510                     
12511                     this.store.fireEvent("datachanged", this.store);
12512                     
12513                     this.onLoad();
12514                     
12515                     
12516                 }else{
12517                     
12518                     this.store.baseParams[this.queryParam] = q;
12519                     
12520                     var options = {params : this.getParams(q)};
12521                     
12522                     if(this.loadNext){
12523                         options.add = true;
12524                         options.params.start = this.page * this.pageSize;
12525                     }
12526                     
12527                     this.store.load(options);
12528                     
12529                     /*
12530                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12531                      *  we should expand the list on onLoad
12532                      *  so command out it
12533                      */
12534 //                    this.expand();
12535                 }
12536             }else{
12537                 this.selectedIndex = -1;
12538                 this.onLoad();   
12539             }
12540         }
12541         
12542         this.loadNext = false;
12543     },
12544     
12545     // private
12546     getParams : function(q){
12547         var p = {};
12548         //p[this.queryParam] = q;
12549         
12550         if(this.pageSize){
12551             p.start = 0;
12552             p.limit = this.pageSize;
12553         }
12554         return p;
12555     },
12556
12557     /**
12558      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12559      */
12560     collapse : function(){
12561         if(!this.isExpanded()){
12562             return;
12563         }
12564         
12565         this.list.hide();
12566         
12567         if(this.tickable){
12568             this.hasFocus = false;
12569             this.okBtn.hide();
12570             this.cancelBtn.hide();
12571             this.trigger.show();
12572             
12573             if(this.editable){
12574                 this.tickableInputEl().dom.value = '';
12575                 this.tickableInputEl().blur();
12576             }
12577             
12578         }
12579         
12580         Roo.get(document).un('mousedown', this.collapseIf, this);
12581         Roo.get(document).un('mousewheel', this.collapseIf, this);
12582         if (!this.editable) {
12583             Roo.get(document).un('keydown', this.listKeyPress, this);
12584         }
12585         this.fireEvent('collapse', this);
12586     },
12587
12588     // private
12589     collapseIf : function(e){
12590         var in_combo  = e.within(this.el);
12591         var in_list =  e.within(this.list);
12592         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12593         
12594         if (in_combo || in_list || is_list) {
12595             //e.stopPropagation();
12596             return;
12597         }
12598         
12599         if(this.tickable){
12600             this.onTickableFooterButtonClick(e, false, false);
12601         }
12602
12603         this.collapse();
12604         
12605     },
12606
12607     /**
12608      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12609      */
12610     expand : function(){
12611        
12612         if(this.isExpanded() || !this.hasFocus){
12613             return;
12614         }
12615         
12616         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12617         this.list.setWidth(lw);
12618         
12619         
12620          Roo.log('expand');
12621         
12622         this.list.show();
12623         
12624         this.restrictHeight();
12625         
12626         if(this.tickable){
12627             
12628             this.tickItems = Roo.apply([], this.item);
12629             
12630             this.okBtn.show();
12631             this.cancelBtn.show();
12632             this.trigger.hide();
12633             
12634             if(this.editable){
12635                 this.tickableInputEl().focus();
12636             }
12637             
12638         }
12639         
12640         Roo.get(document).on('mousedown', this.collapseIf, this);
12641         Roo.get(document).on('mousewheel', this.collapseIf, this);
12642         if (!this.editable) {
12643             Roo.get(document).on('keydown', this.listKeyPress, this);
12644         }
12645         
12646         this.fireEvent('expand', this);
12647     },
12648
12649     // private
12650     // Implements the default empty TriggerField.onTriggerClick function
12651     onTriggerClick : function(e)
12652     {
12653         Roo.log('trigger click');
12654         
12655         if(this.disabled || !this.triggerList){
12656             return;
12657         }
12658         
12659         this.page = 0;
12660         this.loadNext = false;
12661         
12662         if(this.isExpanded()){
12663             this.collapse();
12664             if (!this.blockFocus) {
12665                 this.inputEl().focus();
12666             }
12667             
12668         }else {
12669             this.hasFocus = true;
12670             if(this.triggerAction == 'all') {
12671                 this.doQuery(this.allQuery, true);
12672             } else {
12673                 this.doQuery(this.getRawValue());
12674             }
12675             if (!this.blockFocus) {
12676                 this.inputEl().focus();
12677             }
12678         }
12679     },
12680     
12681     onTickableTriggerClick : function(e)
12682     {
12683         if(this.disabled){
12684             return;
12685         }
12686         
12687         this.page = 0;
12688         this.loadNext = false;
12689         this.hasFocus = true;
12690         
12691         if(this.triggerAction == 'all') {
12692             this.doQuery(this.allQuery, true);
12693         } else {
12694             this.doQuery(this.getRawValue());
12695         }
12696     },
12697     
12698     onSearchFieldClick : function(e)
12699     {
12700         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12701             this.onTickableFooterButtonClick(e, false, false);
12702             return;
12703         }
12704         
12705         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12706             return;
12707         }
12708         
12709         this.page = 0;
12710         this.loadNext = false;
12711         this.hasFocus = true;
12712         
12713         if(this.triggerAction == 'all') {
12714             this.doQuery(this.allQuery, true);
12715         } else {
12716             this.doQuery(this.getRawValue());
12717         }
12718     },
12719     
12720     listKeyPress : function(e)
12721     {
12722         //Roo.log('listkeypress');
12723         // scroll to first matching element based on key pres..
12724         if (e.isSpecialKey()) {
12725             return false;
12726         }
12727         var k = String.fromCharCode(e.getKey()).toUpperCase();
12728         //Roo.log(k);
12729         var match  = false;
12730         var csel = this.view.getSelectedNodes();
12731         var cselitem = false;
12732         if (csel.length) {
12733             var ix = this.view.indexOf(csel[0]);
12734             cselitem  = this.store.getAt(ix);
12735             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12736                 cselitem = false;
12737             }
12738             
12739         }
12740         
12741         this.store.each(function(v) { 
12742             if (cselitem) {
12743                 // start at existing selection.
12744                 if (cselitem.id == v.id) {
12745                     cselitem = false;
12746                 }
12747                 return true;
12748             }
12749                 
12750             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12751                 match = this.store.indexOf(v);
12752                 return false;
12753             }
12754             return true;
12755         }, this);
12756         
12757         if (match === false) {
12758             return true; // no more action?
12759         }
12760         // scroll to?
12761         this.view.select(match);
12762         var sn = Roo.get(this.view.getSelectedNodes()[0])
12763         sn.scrollIntoView(sn.dom.parentNode, false);
12764     },
12765     
12766     onViewScroll : function(e, t){
12767         
12768         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){
12769             return;
12770         }
12771         
12772         this.hasQuery = true;
12773         
12774         this.loading = this.list.select('.loading', true).first();
12775         
12776         if(this.loading === null){
12777             this.list.createChild({
12778                 tag: 'div',
12779                 cls: 'loading select2-more-results select2-active',
12780                 html: 'Loading more results...'
12781             })
12782             
12783             this.loading = this.list.select('.loading', true).first();
12784             
12785             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12786             
12787             this.loading.hide();
12788         }
12789         
12790         this.loading.show();
12791         
12792         var _combo = this;
12793         
12794         this.page++;
12795         this.loadNext = true;
12796         
12797         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12798         
12799         return;
12800     },
12801     
12802     addItem : function(o)
12803     {   
12804         var dv = ''; // display value
12805         
12806         if (this.displayField) {
12807             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12808         } else {
12809             // this is an error condition!!!
12810             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12811         }
12812         
12813         if(!dv.length){
12814             return;
12815         }
12816         
12817         var choice = this.choices.createChild({
12818             tag: 'li',
12819             cls: 'select2-search-choice',
12820             cn: [
12821                 {
12822                     tag: 'div',
12823                     html: dv
12824                 },
12825                 {
12826                     tag: 'a',
12827                     href: '#',
12828                     cls: 'select2-search-choice-close',
12829                     tabindex: '-1'
12830                 }
12831             ]
12832             
12833         }, this.searchField);
12834         
12835         var close = choice.select('a.select2-search-choice-close', true).first()
12836         
12837         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12838         
12839         this.item.push(o);
12840         
12841         this.lastData = o;
12842         
12843         this.syncValue();
12844         
12845         this.inputEl().dom.value = '';
12846         
12847         this.validate();
12848     },
12849     
12850     onRemoveItem : function(e, _self, o)
12851     {
12852         e.preventDefault();
12853         
12854         this.lastItem = Roo.apply([], this.item);
12855         
12856         var index = this.item.indexOf(o.data) * 1;
12857         
12858         if( index < 0){
12859             Roo.log('not this item?!');
12860             return;
12861         }
12862         
12863         this.item.splice(index, 1);
12864         o.item.remove();
12865         
12866         this.syncValue();
12867         
12868         this.fireEvent('remove', this, e);
12869         
12870         this.validate();
12871         
12872     },
12873     
12874     syncValue : function()
12875     {
12876         if(!this.item.length){
12877             this.clearValue();
12878             return;
12879         }
12880             
12881         var value = [];
12882         var _this = this;
12883         Roo.each(this.item, function(i){
12884             if(_this.valueField){
12885                 value.push(i[_this.valueField]);
12886                 return;
12887             }
12888
12889             value.push(i);
12890         });
12891
12892         this.value = value.join(',');
12893
12894         if(this.hiddenField){
12895             this.hiddenField.dom.value = this.value;
12896         }
12897         
12898         this.store.fireEvent("datachanged", this.store);
12899     },
12900     
12901     clearItem : function()
12902     {
12903         if(!this.multiple){
12904             return;
12905         }
12906         
12907         this.item = [];
12908         
12909         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12910            c.remove();
12911         });
12912         
12913         this.syncValue();
12914         
12915         this.validate();
12916         
12917         if(this.tickable && !Roo.isTouch){
12918             this.view.refresh();
12919         }
12920     },
12921     
12922     inputEl: function ()
12923     {
12924         if(Roo.isTouch && this.mobileTouchView){
12925             return this.el.select('input.form-control',true).first();
12926         }
12927         
12928         if(this.tickable){
12929             return this.searchField;
12930         }
12931         
12932         return this.el.select('input.form-control',true).first();
12933     },
12934     
12935     
12936     onTickableFooterButtonClick : function(e, btn, el)
12937     {
12938         e.preventDefault();
12939         
12940         this.lastItem = Roo.apply([], this.item);
12941         
12942         if(btn && btn.name == 'cancel'){
12943             this.tickItems = Roo.apply([], this.item);
12944             this.collapse();
12945             return;
12946         }
12947         
12948         this.clearItem();
12949         
12950         var _this = this;
12951         
12952         Roo.each(this.tickItems, function(o){
12953             _this.addItem(o);
12954         });
12955         
12956         this.collapse();
12957         
12958     },
12959     
12960     validate : function()
12961     {
12962         var v = this.getRawValue();
12963         
12964         if(this.multiple){
12965             v = this.getValue();
12966         }
12967         
12968         if(this.disabled || this.allowBlank || v.length){
12969             this.markValid();
12970             return true;
12971         }
12972         
12973         this.markInvalid();
12974         return false;
12975     },
12976     
12977     tickableInputEl : function()
12978     {
12979         if(!this.tickable || !this.editable){
12980             return this.inputEl();
12981         }
12982         
12983         return this.inputEl().select('.select2-search-field-input', true).first();
12984     },
12985     
12986     
12987     getAutoCreateTouchView : function()
12988     {
12989         var id = Roo.id();
12990         
12991         var cfg = {
12992             cls: 'form-group' //input-group
12993         };
12994         
12995         var input =  {
12996             tag: 'input',
12997             id : id,
12998             type : this.inputType,
12999             cls : 'form-control x-combo-noedit',
13000             autocomplete: 'new-password',
13001             placeholder : this.placeholder || '',
13002             readonly : true
13003         };
13004         
13005         if (this.name) {
13006             input.name = this.name;
13007         }
13008         
13009         if (this.size) {
13010             input.cls += ' input-' + this.size;
13011         }
13012         
13013         if (this.disabled) {
13014             input.disabled = true;
13015         }
13016         
13017         var inputblock = {
13018             cls : '',
13019             cn : [
13020                 input
13021             ]
13022         };
13023         
13024         if(this.before){
13025             inputblock.cls += ' input-group';
13026             
13027             inputblock.cn.unshift({
13028                 tag :'span',
13029                 cls : 'input-group-addon',
13030                 html : this.before
13031             });
13032         }
13033         
13034         if(this.removable && !this.multiple){
13035             inputblock.cls += ' roo-removable';
13036             
13037             inputblock.cn.push({
13038                 tag: 'button',
13039                 html : 'x',
13040                 cls : 'roo-combo-removable-btn close'
13041             });
13042         }
13043
13044         if(this.hasFeedback && !this.allowBlank){
13045             
13046             inputblock.cls += ' has-feedback';
13047             
13048             inputblock.cn.push({
13049                 tag: 'span',
13050                 cls: 'glyphicon form-control-feedback'
13051             });
13052             
13053         }
13054         
13055         if (this.after) {
13056             
13057             inputblock.cls += (this.before) ? '' : ' input-group';
13058             
13059             inputblock.cn.push({
13060                 tag :'span',
13061                 cls : 'input-group-addon',
13062                 html : this.after
13063             });
13064         }
13065
13066         var box = {
13067             tag: 'div',
13068             cn: [
13069                 {
13070                     tag: 'input',
13071                     type : 'hidden',
13072                     cls: 'form-hidden-field'
13073                 },
13074                 inputblock
13075             ]
13076             
13077         };
13078         
13079         if(this.multiple){
13080             box = {
13081                 tag: 'div',
13082                 cn: [
13083                     {
13084                         tag: 'input',
13085                         type : 'hidden',
13086                         cls: 'form-hidden-field'
13087                     },
13088                     {
13089                         tag: 'ul',
13090                         cls: 'select2-choices',
13091                         cn:[
13092                             {
13093                                 tag: 'li',
13094                                 cls: 'select2-search-field',
13095                                 cn: [
13096
13097                                     inputblock
13098                                 ]
13099                             }
13100                         ]
13101                     }
13102                 ]
13103             }
13104         };
13105         
13106         var combobox = {
13107             cls: 'select2-container input-group',
13108             cn: [
13109                 box
13110             ]
13111         };
13112         
13113         if(this.multiple){
13114             combobox.cls += ' select2-container-multi';
13115         }
13116         
13117         var align = this.labelAlign || this.parentLabelAlign();
13118         
13119         cfg.cn = combobox;
13120         
13121         if(this.fieldLabel.length){
13122             
13123             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13124             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13125             
13126             cfg.cn = [
13127                 {
13128                     tag: 'label',
13129                     cls : 'control-label ' + lw,
13130                     html : this.fieldLabel
13131
13132                 },
13133                 {
13134                     cls : cw, 
13135                     cn: [
13136                         combobox
13137                     ]
13138                 }
13139             ];
13140         }
13141         
13142         var settings = this;
13143         
13144         ['xs','sm','md','lg'].map(function(size){
13145             if (settings[size]) {
13146                 cfg.cls += ' col-' + size + '-' + settings[size];
13147             }
13148         });
13149         
13150         return cfg;
13151     },
13152     
13153     initTouchView : function()
13154     {
13155         this.renderTouchView();
13156         
13157         this.touchViewEl.on('scroll', function(){
13158             this.el.dom.scrollTop = 0;
13159         }, this);
13160         
13161         this.inputEl().on("click", this.showTouchView, this);
13162         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13163         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13164         
13165         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13166         
13167         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13168         this.store.on('load', this.onTouchViewLoad, this);
13169         this.store.on('loadexception', this.onTouchViewLoadException, this);
13170         
13171         if(this.hiddenName){
13172             
13173             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13174             
13175             this.hiddenField.dom.value =
13176                 this.hiddenValue !== undefined ? this.hiddenValue :
13177                 this.value !== undefined ? this.value : '';
13178         
13179             this.el.dom.removeAttribute('name');
13180             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13181         }
13182         
13183         if(this.multiple){
13184             this.choices = this.el.select('ul.select2-choices', true).first();
13185             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13186         }
13187         
13188         if(this.removable && !this.multiple){
13189             var close = this.closeTriggerEl();
13190             if(close){
13191                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13192                 close.on('click', this.removeBtnClick, this, close);
13193             }
13194         }
13195         
13196         return;
13197         
13198         
13199     },
13200     
13201     renderTouchView : function()
13202     {
13203         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13204         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13205         
13206         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13207         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13208         
13209         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13210         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13211         this.touchViewBodyEl.setStyle('overflow', 'auto');
13212         
13213         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13214         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13215         
13216         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13217         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13218         
13219     },
13220     
13221     showTouchView : function()
13222     {
13223         this.touchViewHeaderEl.hide();
13224
13225         if(this.fieldLabel.length){
13226             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13227             this.touchViewHeaderEl.show();
13228         }
13229
13230         this.touchViewEl.show();
13231
13232         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13233         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13234
13235         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13236
13237         if(this.fieldLabel.length){
13238             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13239         }
13240         
13241         this.touchViewBodyEl.setHeight(bodyHeight);
13242
13243         if(this.animate){
13244             var _this = this;
13245             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13246         }else{
13247             this.touchViewEl.addClass('in');
13248         }
13249
13250         this.doTouchViewQuery();
13251         
13252     },
13253     
13254     hideTouchView : function()
13255     {
13256         this.touchViewEl.removeClass('in');
13257
13258         if(this.animate){
13259             var _this = this;
13260             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13261         }else{
13262             this.touchViewEl.setStyle('display', 'none');
13263         }
13264         
13265     },
13266     
13267     setTouchViewValue : function()
13268     {
13269         if(this.multiple){
13270             this.clearItem();
13271         
13272             var _this = this;
13273
13274             Roo.each(this.tickItems, function(o){
13275                 this.addItem(o);
13276             }, this);
13277         }
13278         
13279         this.hideTouchView();
13280     },
13281     
13282     doTouchViewQuery : function()
13283     {
13284         var qe = {
13285             query: '',
13286             forceAll: true,
13287             combo: this,
13288             cancel:false
13289         };
13290         
13291         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13292             return false;
13293         }
13294         
13295         if(!this.alwaysQuery || this.mode == 'local'){
13296             this.onTouchViewLoad();
13297             return;
13298         }
13299         
13300         this.store.load();
13301     },
13302     
13303     onTouchViewBeforeLoad : function(combo,opts)
13304     {
13305         return;
13306     },
13307
13308     // private
13309     onTouchViewLoad : function()
13310     {
13311         if(this.store.getCount() < 1){
13312             this.onTouchViewEmptyResults();
13313             return;
13314         }
13315         
13316         this.clearTouchView();
13317         
13318         var rawValue = this.getRawValue();
13319         
13320         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13321         
13322         this.tickItems = [];
13323         
13324         this.store.data.each(function(d, rowIndex){
13325             var row = this.touchViewListGroup.createChild(template);
13326             
13327             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13328                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13329             }
13330             
13331             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13332                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13333             }
13334             
13335             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13336                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13337                 this.tickItems.push(d.data);
13338             }
13339             
13340             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13341             
13342         }, this);
13343         
13344         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13345         
13346         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13347
13348         if(this.fieldLabel.length){
13349             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13350         }
13351
13352         var listHeight = this.touchViewListGroup.getHeight();
13353         
13354         var _this = this;
13355         
13356         if(firstChecked && listHeight > bodyHeight){
13357             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13358         }
13359         
13360     },
13361     
13362     onTouchViewLoadException : function()
13363     {
13364         this.hideTouchView();
13365     },
13366     
13367     onTouchViewEmptyResults : function()
13368     {
13369         this.clearTouchView();
13370         
13371         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13372         
13373         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13374         
13375     },
13376     
13377     clearTouchView : function()
13378     {
13379         this.touchViewListGroup.dom.innerHTML = '';
13380     },
13381     
13382     onTouchViewClick : function(e, el, o)
13383     {
13384         e.preventDefault();
13385         
13386         var row = o.row;
13387         var rowIndex = o.rowIndex;
13388         
13389         var r = this.store.getAt(rowIndex);
13390         
13391         if(!this.multiple){
13392             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13393                 c.dom.removeAttribute('checked');
13394             }, this);
13395             
13396             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13397         
13398             this.setFromData(r.data);
13399             
13400             var close = this.closeTriggerEl();
13401         
13402             if(close){
13403                 close.show();
13404             }
13405
13406             this.hideTouchView();
13407             
13408             this.fireEvent('select', this, r, rowIndex);
13409             
13410             return;
13411         }
13412         
13413         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13414             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13415             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13416             return;
13417         }
13418         
13419         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13420         this.addItem(r.data);
13421         this.tickItems.push(r.data);
13422         
13423     }
13424     
13425
13426     /** 
13427     * @cfg {Boolean} grow 
13428     * @hide 
13429     */
13430     /** 
13431     * @cfg {Number} growMin 
13432     * @hide 
13433     */
13434     /** 
13435     * @cfg {Number} growMax 
13436     * @hide 
13437     */
13438     /**
13439      * @hide
13440      * @method autoSize
13441      */
13442 });
13443
13444 Roo.apply(Roo.bootstrap.ComboBox,  {
13445     
13446     header : {
13447         tag: 'div',
13448         cls: 'modal-header',
13449         cn: [
13450             {
13451                 tag: 'h4',
13452                 cls: 'modal-title'
13453             }
13454         ]
13455     },
13456     
13457     body : {
13458         tag: 'div',
13459         cls: 'modal-body',
13460         cn: [
13461             {
13462                 tag: 'ul',
13463                 cls: 'list-group'
13464             }
13465         ]
13466     },
13467     
13468     listItemRadio : {
13469         tag: 'li',
13470         cls: 'list-group-item',
13471         cn: [
13472             {
13473                 tag: 'span',
13474                 cls: 'roo-combobox-list-group-item-value'
13475             },
13476             {
13477                 tag: 'div',
13478                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13479                 cn: [
13480                     {
13481                         tag: 'input',
13482                         type: 'radio'
13483                     },
13484                     {
13485                         tag: 'label'
13486                     }
13487                 ]
13488             }
13489         ]
13490     },
13491     
13492     listItemCheckbox : {
13493         tag: 'li',
13494         cls: 'list-group-item',
13495         cn: [
13496             {
13497                 tag: 'span',
13498                 cls: 'roo-combobox-list-group-item-value'
13499             },
13500             {
13501                 tag: 'div',
13502                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13503                 cn: [
13504                     {
13505                         tag: 'input',
13506                         type: 'checkbox'
13507                     },
13508                     {
13509                         tag: 'label'
13510                     }
13511                 ]
13512             }
13513         ]
13514     },
13515     
13516     emptyResult : {
13517         tag: 'div',
13518         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13519     },
13520     
13521     footer : {
13522         tag: 'div',
13523         cls: 'modal-footer',
13524         cn: [
13525             {
13526                 tag: 'div',
13527                 cls: 'row',
13528                 cn: [
13529                     {
13530                         tag: 'div',
13531                         cls: 'col-xs-6 text-left',
13532                         cn: {
13533                             tag: 'button',
13534                             cls: 'btn btn-danger roo-touch-view-cancel',
13535                             html: 'Cancel'
13536                         }
13537                     },
13538                     {
13539                         tag: 'div',
13540                         cls: 'col-xs-6 text-right',
13541                         cn: {
13542                             tag: 'button',
13543                             cls: 'btn btn-success roo-touch-view-ok',
13544                             html: 'OK'
13545                         }
13546                     }
13547                 ]
13548             }
13549         ]
13550         
13551     }
13552 });
13553
13554 Roo.apply(Roo.bootstrap.ComboBox,  {
13555     
13556     touchViewTemplate : {
13557         tag: 'div',
13558         cls: 'modal fade roo-combobox-touch-view',
13559         cn: [
13560             {
13561                 tag: 'div',
13562                 cls: 'modal-dialog',
13563                 cn: [
13564                     {
13565                         tag: 'div',
13566                         cls: 'modal-content',
13567                         cn: [
13568                             Roo.bootstrap.ComboBox.header,
13569                             Roo.bootstrap.ComboBox.body,
13570                             Roo.bootstrap.ComboBox.footer
13571                         ]
13572                     }
13573                 ]
13574             }
13575         ]
13576     }
13577 });/*
13578  * Based on:
13579  * Ext JS Library 1.1.1
13580  * Copyright(c) 2006-2007, Ext JS, LLC.
13581  *
13582  * Originally Released Under LGPL - original licence link has changed is not relivant.
13583  *
13584  * Fork - LGPL
13585  * <script type="text/javascript">
13586  */
13587
13588 /**
13589  * @class Roo.View
13590  * @extends Roo.util.Observable
13591  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13592  * This class also supports single and multi selection modes. <br>
13593  * Create a data model bound view:
13594  <pre><code>
13595  var store = new Roo.data.Store(...);
13596
13597  var view = new Roo.View({
13598     el : "my-element",
13599     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13600  
13601     singleSelect: true,
13602     selectedClass: "ydataview-selected",
13603     store: store
13604  });
13605
13606  // listen for node click?
13607  view.on("click", function(vw, index, node, e){
13608  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13609  });
13610
13611  // load XML data
13612  dataModel.load("foobar.xml");
13613  </code></pre>
13614  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13615  * <br><br>
13616  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13617  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13618  * 
13619  * Note: old style constructor is still suported (container, template, config)
13620  * 
13621  * @constructor
13622  * Create a new View
13623  * @param {Object} config The config object
13624  * 
13625  */
13626 Roo.View = function(config, depreciated_tpl, depreciated_config){
13627     
13628     this.parent = false;
13629     
13630     if (typeof(depreciated_tpl) == 'undefined') {
13631         // new way.. - universal constructor.
13632         Roo.apply(this, config);
13633         this.el  = Roo.get(this.el);
13634     } else {
13635         // old format..
13636         this.el  = Roo.get(config);
13637         this.tpl = depreciated_tpl;
13638         Roo.apply(this, depreciated_config);
13639     }
13640     this.wrapEl  = this.el.wrap().wrap();
13641     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13642     
13643     
13644     if(typeof(this.tpl) == "string"){
13645         this.tpl = new Roo.Template(this.tpl);
13646     } else {
13647         // support xtype ctors..
13648         this.tpl = new Roo.factory(this.tpl, Roo);
13649     }
13650     
13651     
13652     this.tpl.compile();
13653     
13654     /** @private */
13655     this.addEvents({
13656         /**
13657          * @event beforeclick
13658          * Fires before a click is processed. Returns false to cancel the default action.
13659          * @param {Roo.View} this
13660          * @param {Number} index The index of the target node
13661          * @param {HTMLElement} node The target node
13662          * @param {Roo.EventObject} e The raw event object
13663          */
13664             "beforeclick" : true,
13665         /**
13666          * @event click
13667          * Fires when a template node is clicked.
13668          * @param {Roo.View} this
13669          * @param {Number} index The index of the target node
13670          * @param {HTMLElement} node The target node
13671          * @param {Roo.EventObject} e The raw event object
13672          */
13673             "click" : true,
13674         /**
13675          * @event dblclick
13676          * Fires when a template node is double clicked.
13677          * @param {Roo.View} this
13678          * @param {Number} index The index of the target node
13679          * @param {HTMLElement} node The target node
13680          * @param {Roo.EventObject} e The raw event object
13681          */
13682             "dblclick" : true,
13683         /**
13684          * @event contextmenu
13685          * Fires when a template node is right clicked.
13686          * @param {Roo.View} this
13687          * @param {Number} index The index of the target node
13688          * @param {HTMLElement} node The target node
13689          * @param {Roo.EventObject} e The raw event object
13690          */
13691             "contextmenu" : true,
13692         /**
13693          * @event selectionchange
13694          * Fires when the selected nodes change.
13695          * @param {Roo.View} this
13696          * @param {Array} selections Array of the selected nodes
13697          */
13698             "selectionchange" : true,
13699     
13700         /**
13701          * @event beforeselect
13702          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13703          * @param {Roo.View} this
13704          * @param {HTMLElement} node The node to be selected
13705          * @param {Array} selections Array of currently selected nodes
13706          */
13707             "beforeselect" : true,
13708         /**
13709          * @event preparedata
13710          * Fires on every row to render, to allow you to change the data.
13711          * @param {Roo.View} this
13712          * @param {Object} data to be rendered (change this)
13713          */
13714           "preparedata" : true
13715           
13716           
13717         });
13718
13719
13720
13721     this.el.on({
13722         "click": this.onClick,
13723         "dblclick": this.onDblClick,
13724         "contextmenu": this.onContextMenu,
13725         scope:this
13726     });
13727
13728     this.selections = [];
13729     this.nodes = [];
13730     this.cmp = new Roo.CompositeElementLite([]);
13731     if(this.store){
13732         this.store = Roo.factory(this.store, Roo.data);
13733         this.setStore(this.store, true);
13734     }
13735     
13736     if ( this.footer && this.footer.xtype) {
13737            
13738          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13739         
13740         this.footer.dataSource = this.store
13741         this.footer.container = fctr;
13742         this.footer = Roo.factory(this.footer, Roo);
13743         fctr.insertFirst(this.el);
13744         
13745         // this is a bit insane - as the paging toolbar seems to detach the el..
13746 //        dom.parentNode.parentNode.parentNode
13747          // they get detached?
13748     }
13749     
13750     
13751     Roo.View.superclass.constructor.call(this);
13752     
13753     
13754 };
13755
13756 Roo.extend(Roo.View, Roo.util.Observable, {
13757     
13758      /**
13759      * @cfg {Roo.data.Store} store Data store to load data from.
13760      */
13761     store : false,
13762     
13763     /**
13764      * @cfg {String|Roo.Element} el The container element.
13765      */
13766     el : '',
13767     
13768     /**
13769      * @cfg {String|Roo.Template} tpl The template used by this View 
13770      */
13771     tpl : false,
13772     /**
13773      * @cfg {String} dataName the named area of the template to use as the data area
13774      *                          Works with domtemplates roo-name="name"
13775      */
13776     dataName: false,
13777     /**
13778      * @cfg {String} selectedClass The css class to add to selected nodes
13779      */
13780     selectedClass : "x-view-selected",
13781      /**
13782      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13783      */
13784     emptyText : "",
13785     
13786     /**
13787      * @cfg {String} text to display on mask (default Loading)
13788      */
13789     mask : false,
13790     /**
13791      * @cfg {Boolean} multiSelect Allow multiple selection
13792      */
13793     multiSelect : false,
13794     /**
13795      * @cfg {Boolean} singleSelect Allow single selection
13796      */
13797     singleSelect:  false,
13798     
13799     /**
13800      * @cfg {Boolean} toggleSelect - selecting 
13801      */
13802     toggleSelect : false,
13803     
13804     /**
13805      * @cfg {Boolean} tickable - selecting 
13806      */
13807     tickable : false,
13808     
13809     /**
13810      * Returns the element this view is bound to.
13811      * @return {Roo.Element}
13812      */
13813     getEl : function(){
13814         return this.wrapEl;
13815     },
13816     
13817     
13818
13819     /**
13820      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13821      */
13822     refresh : function(){
13823         //Roo.log('refresh');
13824         var t = this.tpl;
13825         
13826         // if we are using something like 'domtemplate', then
13827         // the what gets used is:
13828         // t.applySubtemplate(NAME, data, wrapping data..)
13829         // the outer template then get' applied with
13830         //     the store 'extra data'
13831         // and the body get's added to the
13832         //      roo-name="data" node?
13833         //      <span class='roo-tpl-{name}'></span> ?????
13834         
13835         
13836         
13837         this.clearSelections();
13838         this.el.update("");
13839         var html = [];
13840         var records = this.store.getRange();
13841         if(records.length < 1) {
13842             
13843             // is this valid??  = should it render a template??
13844             
13845             this.el.update(this.emptyText);
13846             return;
13847         }
13848         var el = this.el;
13849         if (this.dataName) {
13850             this.el.update(t.apply(this.store.meta)); //????
13851             el = this.el.child('.roo-tpl-' + this.dataName);
13852         }
13853         
13854         for(var i = 0, len = records.length; i < len; i++){
13855             var data = this.prepareData(records[i].data, i, records[i]);
13856             this.fireEvent("preparedata", this, data, i, records[i]);
13857             
13858             var d = Roo.apply({}, data);
13859             
13860             if(this.tickable){
13861                 Roo.apply(d, {'roo-id' : Roo.id()});
13862                 
13863                 var _this = this;
13864             
13865                 Roo.each(this.parent.item, function(item){
13866                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13867                         return;
13868                     }
13869                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13870                 });
13871             }
13872             
13873             html[html.length] = Roo.util.Format.trim(
13874                 this.dataName ?
13875                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13876                     t.apply(d)
13877             );
13878         }
13879         
13880         
13881         
13882         el.update(html.join(""));
13883         this.nodes = el.dom.childNodes;
13884         this.updateIndexes(0);
13885     },
13886     
13887
13888     /**
13889      * Function to override to reformat the data that is sent to
13890      * the template for each node.
13891      * DEPRICATED - use the preparedata event handler.
13892      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13893      * a JSON object for an UpdateManager bound view).
13894      */
13895     prepareData : function(data, index, record)
13896     {
13897         this.fireEvent("preparedata", this, data, index, record);
13898         return data;
13899     },
13900
13901     onUpdate : function(ds, record){
13902         // Roo.log('on update');   
13903         this.clearSelections();
13904         var index = this.store.indexOf(record);
13905         var n = this.nodes[index];
13906         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13907         n.parentNode.removeChild(n);
13908         this.updateIndexes(index, index);
13909     },
13910
13911     
13912     
13913 // --------- FIXME     
13914     onAdd : function(ds, records, index)
13915     {
13916         //Roo.log(['on Add', ds, records, index] );        
13917         this.clearSelections();
13918         if(this.nodes.length == 0){
13919             this.refresh();
13920             return;
13921         }
13922         var n = this.nodes[index];
13923         for(var i = 0, len = records.length; i < len; i++){
13924             var d = this.prepareData(records[i].data, i, records[i]);
13925             if(n){
13926                 this.tpl.insertBefore(n, d);
13927             }else{
13928                 
13929                 this.tpl.append(this.el, d);
13930             }
13931         }
13932         this.updateIndexes(index);
13933     },
13934
13935     onRemove : function(ds, record, index){
13936        // Roo.log('onRemove');
13937         this.clearSelections();
13938         var el = this.dataName  ?
13939             this.el.child('.roo-tpl-' + this.dataName) :
13940             this.el; 
13941         
13942         el.dom.removeChild(this.nodes[index]);
13943         this.updateIndexes(index);
13944     },
13945
13946     /**
13947      * Refresh an individual node.
13948      * @param {Number} index
13949      */
13950     refreshNode : function(index){
13951         this.onUpdate(this.store, this.store.getAt(index));
13952     },
13953
13954     updateIndexes : function(startIndex, endIndex){
13955         var ns = this.nodes;
13956         startIndex = startIndex || 0;
13957         endIndex = endIndex || ns.length - 1;
13958         for(var i = startIndex; i <= endIndex; i++){
13959             ns[i].nodeIndex = i;
13960         }
13961     },
13962
13963     /**
13964      * Changes the data store this view uses and refresh the view.
13965      * @param {Store} store
13966      */
13967     setStore : function(store, initial){
13968         if(!initial && this.store){
13969             this.store.un("datachanged", this.refresh);
13970             this.store.un("add", this.onAdd);
13971             this.store.un("remove", this.onRemove);
13972             this.store.un("update", this.onUpdate);
13973             this.store.un("clear", this.refresh);
13974             this.store.un("beforeload", this.onBeforeLoad);
13975             this.store.un("load", this.onLoad);
13976             this.store.un("loadexception", this.onLoad);
13977         }
13978         if(store){
13979           
13980             store.on("datachanged", this.refresh, this);
13981             store.on("add", this.onAdd, this);
13982             store.on("remove", this.onRemove, this);
13983             store.on("update", this.onUpdate, this);
13984             store.on("clear", this.refresh, this);
13985             store.on("beforeload", this.onBeforeLoad, this);
13986             store.on("load", this.onLoad, this);
13987             store.on("loadexception", this.onLoad, this);
13988         }
13989         
13990         if(store){
13991             this.refresh();
13992         }
13993     },
13994     /**
13995      * onbeforeLoad - masks the loading area.
13996      *
13997      */
13998     onBeforeLoad : function(store,opts)
13999     {
14000          //Roo.log('onBeforeLoad');   
14001         if (!opts.add) {
14002             this.el.update("");
14003         }
14004         this.el.mask(this.mask ? this.mask : "Loading" ); 
14005     },
14006     onLoad : function ()
14007     {
14008         this.el.unmask();
14009     },
14010     
14011
14012     /**
14013      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14014      * @param {HTMLElement} node
14015      * @return {HTMLElement} The template node
14016      */
14017     findItemFromChild : function(node){
14018         var el = this.dataName  ?
14019             this.el.child('.roo-tpl-' + this.dataName,true) :
14020             this.el.dom; 
14021         
14022         if(!node || node.parentNode == el){
14023                     return node;
14024             }
14025             var p = node.parentNode;
14026             while(p && p != el){
14027             if(p.parentNode == el){
14028                 return p;
14029             }
14030             p = p.parentNode;
14031         }
14032             return null;
14033     },
14034
14035     /** @ignore */
14036     onClick : function(e){
14037         var item = this.findItemFromChild(e.getTarget());
14038         if(item){
14039             var index = this.indexOf(item);
14040             if(this.onItemClick(item, index, e) !== false){
14041                 this.fireEvent("click", this, index, item, e);
14042             }
14043         }else{
14044             this.clearSelections();
14045         }
14046     },
14047
14048     /** @ignore */
14049     onContextMenu : function(e){
14050         var item = this.findItemFromChild(e.getTarget());
14051         if(item){
14052             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14053         }
14054     },
14055
14056     /** @ignore */
14057     onDblClick : function(e){
14058         var item = this.findItemFromChild(e.getTarget());
14059         if(item){
14060             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14061         }
14062     },
14063
14064     onItemClick : function(item, index, e)
14065     {
14066         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14067             return false;
14068         }
14069         if (this.toggleSelect) {
14070             var m = this.isSelected(item) ? 'unselect' : 'select';
14071             //Roo.log(m);
14072             var _t = this;
14073             _t[m](item, true, false);
14074             return true;
14075         }
14076         if(this.multiSelect || this.singleSelect){
14077             if(this.multiSelect && e.shiftKey && this.lastSelection){
14078                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14079             }else{
14080                 this.select(item, this.multiSelect && e.ctrlKey);
14081                 this.lastSelection = item;
14082             }
14083             
14084             if(!this.tickable){
14085                 e.preventDefault();
14086             }
14087             
14088         }
14089         return true;
14090     },
14091
14092     /**
14093      * Get the number of selected nodes.
14094      * @return {Number}
14095      */
14096     getSelectionCount : function(){
14097         return this.selections.length;
14098     },
14099
14100     /**
14101      * Get the currently selected nodes.
14102      * @return {Array} An array of HTMLElements
14103      */
14104     getSelectedNodes : function(){
14105         return this.selections;
14106     },
14107
14108     /**
14109      * Get the indexes of the selected nodes.
14110      * @return {Array}
14111      */
14112     getSelectedIndexes : function(){
14113         var indexes = [], s = this.selections;
14114         for(var i = 0, len = s.length; i < len; i++){
14115             indexes.push(s[i].nodeIndex);
14116         }
14117         return indexes;
14118     },
14119
14120     /**
14121      * Clear all selections
14122      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14123      */
14124     clearSelections : function(suppressEvent){
14125         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14126             this.cmp.elements = this.selections;
14127             this.cmp.removeClass(this.selectedClass);
14128             this.selections = [];
14129             if(!suppressEvent){
14130                 this.fireEvent("selectionchange", this, this.selections);
14131             }
14132         }
14133     },
14134
14135     /**
14136      * Returns true if the passed node is selected
14137      * @param {HTMLElement/Number} node The node or node index
14138      * @return {Boolean}
14139      */
14140     isSelected : function(node){
14141         var s = this.selections;
14142         if(s.length < 1){
14143             return false;
14144         }
14145         node = this.getNode(node);
14146         return s.indexOf(node) !== -1;
14147     },
14148
14149     /**
14150      * Selects nodes.
14151      * @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
14152      * @param {Boolean} keepExisting (optional) true to keep existing selections
14153      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14154      */
14155     select : function(nodeInfo, keepExisting, suppressEvent){
14156         if(nodeInfo instanceof Array){
14157             if(!keepExisting){
14158                 this.clearSelections(true);
14159             }
14160             for(var i = 0, len = nodeInfo.length; i < len; i++){
14161                 this.select(nodeInfo[i], true, true);
14162             }
14163             return;
14164         } 
14165         var node = this.getNode(nodeInfo);
14166         if(!node || this.isSelected(node)){
14167             return; // already selected.
14168         }
14169         if(!keepExisting){
14170             this.clearSelections(true);
14171         }
14172         
14173         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14174             Roo.fly(node).addClass(this.selectedClass);
14175             this.selections.push(node);
14176             if(!suppressEvent){
14177                 this.fireEvent("selectionchange", this, this.selections);
14178             }
14179         }
14180         
14181         
14182     },
14183       /**
14184      * Unselects nodes.
14185      * @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
14186      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14187      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14188      */
14189     unselect : function(nodeInfo, keepExisting, suppressEvent)
14190     {
14191         if(nodeInfo instanceof Array){
14192             Roo.each(this.selections, function(s) {
14193                 this.unselect(s, nodeInfo);
14194             }, this);
14195             return;
14196         }
14197         var node = this.getNode(nodeInfo);
14198         if(!node || !this.isSelected(node)){
14199             //Roo.log("not selected");
14200             return; // not selected.
14201         }
14202         // fireevent???
14203         var ns = [];
14204         Roo.each(this.selections, function(s) {
14205             if (s == node ) {
14206                 Roo.fly(node).removeClass(this.selectedClass);
14207
14208                 return;
14209             }
14210             ns.push(s);
14211         },this);
14212         
14213         this.selections= ns;
14214         this.fireEvent("selectionchange", this, this.selections);
14215     },
14216
14217     /**
14218      * Gets a template 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 {HTMLElement} The node or null if it wasn't found
14221      */
14222     getNode : function(nodeInfo){
14223         if(typeof nodeInfo == "string"){
14224             return document.getElementById(nodeInfo);
14225         }else if(typeof nodeInfo == "number"){
14226             return this.nodes[nodeInfo];
14227         }
14228         return nodeInfo;
14229     },
14230
14231     /**
14232      * Gets a range template nodes.
14233      * @param {Number} startIndex
14234      * @param {Number} endIndex
14235      * @return {Array} An array of nodes
14236      */
14237     getNodes : function(start, end){
14238         var ns = this.nodes;
14239         start = start || 0;
14240         end = typeof end == "undefined" ? ns.length - 1 : end;
14241         var nodes = [];
14242         if(start <= end){
14243             for(var i = start; i <= end; i++){
14244                 nodes.push(ns[i]);
14245             }
14246         } else{
14247             for(var i = start; i >= end; i--){
14248                 nodes.push(ns[i]);
14249             }
14250         }
14251         return nodes;
14252     },
14253
14254     /**
14255      * Finds the index of the passed node
14256      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14257      * @return {Number} The index of the node or -1
14258      */
14259     indexOf : function(node){
14260         node = this.getNode(node);
14261         if(typeof node.nodeIndex == "number"){
14262             return node.nodeIndex;
14263         }
14264         var ns = this.nodes;
14265         for(var i = 0, len = ns.length; i < len; i++){
14266             if(ns[i] == node){
14267                 return i;
14268             }
14269         }
14270         return -1;
14271     }
14272 });
14273 /*
14274  * - LGPL
14275  *
14276  * based on jquery fullcalendar
14277  * 
14278  */
14279
14280 Roo.bootstrap = Roo.bootstrap || {};
14281 /**
14282  * @class Roo.bootstrap.Calendar
14283  * @extends Roo.bootstrap.Component
14284  * Bootstrap Calendar class
14285  * @cfg {Boolean} loadMask (true|false) default false
14286  * @cfg {Object} header generate the user specific header of the calendar, default false
14287
14288  * @constructor
14289  * Create a new Container
14290  * @param {Object} config The config object
14291  */
14292
14293
14294
14295 Roo.bootstrap.Calendar = function(config){
14296     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14297      this.addEvents({
14298         /**
14299              * @event select
14300              * Fires when a date is selected
14301              * @param {DatePicker} this
14302              * @param {Date} date The selected date
14303              */
14304         'select': true,
14305         /**
14306              * @event monthchange
14307              * Fires when the displayed month changes 
14308              * @param {DatePicker} this
14309              * @param {Date} date The selected month
14310              */
14311         'monthchange': true,
14312         /**
14313              * @event evententer
14314              * Fires when mouse over an event
14315              * @param {Calendar} this
14316              * @param {event} Event
14317              */
14318         'evententer': true,
14319         /**
14320              * @event eventleave
14321              * Fires when the mouse leaves an
14322              * @param {Calendar} this
14323              * @param {event}
14324              */
14325         'eventleave': true,
14326         /**
14327              * @event eventclick
14328              * Fires when the mouse click an
14329              * @param {Calendar} this
14330              * @param {event}
14331              */
14332         'eventclick': true
14333         
14334     });
14335
14336 };
14337
14338 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14339     
14340      /**
14341      * @cfg {Number} startDay
14342      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14343      */
14344     startDay : 0,
14345     
14346     loadMask : false,
14347     
14348     header : false,
14349       
14350     getAutoCreate : function(){
14351         
14352         
14353         var fc_button = function(name, corner, style, content ) {
14354             return Roo.apply({},{
14355                 tag : 'span',
14356                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14357                          (corner.length ?
14358                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14359                             ''
14360                         ),
14361                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14362                 unselectable: 'on'
14363             });
14364         };
14365         
14366         var header = {};
14367         
14368         if(!this.header){
14369             header = {
14370                 tag : 'table',
14371                 cls : 'fc-header',
14372                 style : 'width:100%',
14373                 cn : [
14374                     {
14375                         tag: 'tr',
14376                         cn : [
14377                             {
14378                                 tag : 'td',
14379                                 cls : 'fc-header-left',
14380                                 cn : [
14381                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14382                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14383                                     { tag: 'span', cls: 'fc-header-space' },
14384                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14385
14386
14387                                 ]
14388                             },
14389
14390                             {
14391                                 tag : 'td',
14392                                 cls : 'fc-header-center',
14393                                 cn : [
14394                                     {
14395                                         tag: 'span',
14396                                         cls: 'fc-header-title',
14397                                         cn : {
14398                                             tag: 'H2',
14399                                             html : 'month / year'
14400                                         }
14401                                     }
14402
14403                                 ]
14404                             },
14405                             {
14406                                 tag : 'td',
14407                                 cls : 'fc-header-right',
14408                                 cn : [
14409                               /*      fc_button('month', 'left', '', 'month' ),
14410                                     fc_button('week', '', '', 'week' ),
14411                                     fc_button('day', 'right', '', 'day' )
14412                                 */    
14413
14414                                 ]
14415                             }
14416
14417                         ]
14418                     }
14419                 ]
14420             };
14421         }
14422         
14423         header = this.header;
14424         
14425        
14426         var cal_heads = function() {
14427             var ret = [];
14428             // fixme - handle this.
14429             
14430             for (var i =0; i < Date.dayNames.length; i++) {
14431                 var d = Date.dayNames[i];
14432                 ret.push({
14433                     tag: 'th',
14434                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14435                     html : d.substring(0,3)
14436                 });
14437                 
14438             }
14439             ret[0].cls += ' fc-first';
14440             ret[6].cls += ' fc-last';
14441             return ret;
14442         };
14443         var cal_cell = function(n) {
14444             return  {
14445                 tag: 'td',
14446                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14447                 cn : [
14448                     {
14449                         cn : [
14450                             {
14451                                 cls: 'fc-day-number',
14452                                 html: 'D'
14453                             },
14454                             {
14455                                 cls: 'fc-day-content',
14456                              
14457                                 cn : [
14458                                      {
14459                                         style: 'position: relative;' // height: 17px;
14460                                     }
14461                                 ]
14462                             }
14463                             
14464                             
14465                         ]
14466                     }
14467                 ]
14468                 
14469             }
14470         };
14471         var cal_rows = function() {
14472             
14473             var ret = [];
14474             for (var r = 0; r < 6; r++) {
14475                 var row= {
14476                     tag : 'tr',
14477                     cls : 'fc-week',
14478                     cn : []
14479                 };
14480                 
14481                 for (var i =0; i < Date.dayNames.length; i++) {
14482                     var d = Date.dayNames[i];
14483                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14484
14485                 }
14486                 row.cn[0].cls+=' fc-first';
14487                 row.cn[0].cn[0].style = 'min-height:90px';
14488                 row.cn[6].cls+=' fc-last';
14489                 ret.push(row);
14490                 
14491             }
14492             ret[0].cls += ' fc-first';
14493             ret[4].cls += ' fc-prev-last';
14494             ret[5].cls += ' fc-last';
14495             return ret;
14496             
14497         };
14498         
14499         var cal_table = {
14500             tag: 'table',
14501             cls: 'fc-border-separate',
14502             style : 'width:100%',
14503             cellspacing  : 0,
14504             cn : [
14505                 { 
14506                     tag: 'thead',
14507                     cn : [
14508                         { 
14509                             tag: 'tr',
14510                             cls : 'fc-first fc-last',
14511                             cn : cal_heads()
14512                         }
14513                     ]
14514                 },
14515                 { 
14516                     tag: 'tbody',
14517                     cn : cal_rows()
14518                 }
14519                   
14520             ]
14521         };
14522          
14523          var cfg = {
14524             cls : 'fc fc-ltr',
14525             cn : [
14526                 header,
14527                 {
14528                     cls : 'fc-content',
14529                     style : "position: relative;",
14530                     cn : [
14531                         {
14532                             cls : 'fc-view fc-view-month fc-grid',
14533                             style : 'position: relative',
14534                             unselectable : 'on',
14535                             cn : [
14536                                 {
14537                                     cls : 'fc-event-container',
14538                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14539                                 },
14540                                 cal_table
14541                             ]
14542                         }
14543                     ]
14544     
14545                 }
14546            ] 
14547             
14548         };
14549         
14550          
14551         
14552         return cfg;
14553     },
14554     
14555     
14556     initEvents : function()
14557     {
14558         if(!this.store){
14559             throw "can not find store for calendar";
14560         }
14561         
14562         var mark = {
14563             tag: "div",
14564             cls:"x-dlg-mask",
14565             style: "text-align:center",
14566             cn: [
14567                 {
14568                     tag: "div",
14569                     style: "background-color:white;width:50%;margin:250 auto",
14570                     cn: [
14571                         {
14572                             tag: "img",
14573                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14574                         },
14575                         {
14576                             tag: "span",
14577                             html: "Loading"
14578                         }
14579                         
14580                     ]
14581                 }
14582             ]
14583         }
14584         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14585         
14586         var size = this.el.select('.fc-content', true).first().getSize();
14587         this.maskEl.setSize(size.width, size.height);
14588         this.maskEl.enableDisplayMode("block");
14589         if(!this.loadMask){
14590             this.maskEl.hide();
14591         }
14592         
14593         this.store = Roo.factory(this.store, Roo.data);
14594         this.store.on('load', this.onLoad, this);
14595         this.store.on('beforeload', this.onBeforeLoad, this);
14596         
14597         this.resize();
14598         
14599         this.cells = this.el.select('.fc-day',true);
14600         //Roo.log(this.cells);
14601         this.textNodes = this.el.query('.fc-day-number');
14602         this.cells.addClassOnOver('fc-state-hover');
14603         
14604         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14605         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14606         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14607         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14608         
14609         this.on('monthchange', this.onMonthChange, this);
14610         
14611         this.update(new Date().clearTime());
14612     },
14613     
14614     resize : function() {
14615         var sz  = this.el.getSize();
14616         
14617         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14618         this.el.select('.fc-day-content div',true).setHeight(34);
14619     },
14620     
14621     
14622     // private
14623     showPrevMonth : function(e){
14624         this.update(this.activeDate.add("mo", -1));
14625     },
14626     showToday : function(e){
14627         this.update(new Date().clearTime());
14628     },
14629     // private
14630     showNextMonth : function(e){
14631         this.update(this.activeDate.add("mo", 1));
14632     },
14633
14634     // private
14635     showPrevYear : function(){
14636         this.update(this.activeDate.add("y", -1));
14637     },
14638
14639     // private
14640     showNextYear : function(){
14641         this.update(this.activeDate.add("y", 1));
14642     },
14643
14644     
14645    // private
14646     update : function(date)
14647     {
14648         var vd = this.activeDate;
14649         this.activeDate = date;
14650 //        if(vd && this.el){
14651 //            var t = date.getTime();
14652 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14653 //                Roo.log('using add remove');
14654 //                
14655 //                this.fireEvent('monthchange', this, date);
14656 //                
14657 //                this.cells.removeClass("fc-state-highlight");
14658 //                this.cells.each(function(c){
14659 //                   if(c.dateValue == t){
14660 //                       c.addClass("fc-state-highlight");
14661 //                       setTimeout(function(){
14662 //                            try{c.dom.firstChild.focus();}catch(e){}
14663 //                       }, 50);
14664 //                       return false;
14665 //                   }
14666 //                   return true;
14667 //                });
14668 //                return;
14669 //            }
14670 //        }
14671         
14672         var days = date.getDaysInMonth();
14673         
14674         var firstOfMonth = date.getFirstDateOfMonth();
14675         var startingPos = firstOfMonth.getDay()-this.startDay;
14676         
14677         if(startingPos < this.startDay){
14678             startingPos += 7;
14679         }
14680         
14681         var pm = date.add(Date.MONTH, -1);
14682         var prevStart = pm.getDaysInMonth()-startingPos;
14683 //        
14684         this.cells = this.el.select('.fc-day',true);
14685         this.textNodes = this.el.query('.fc-day-number');
14686         this.cells.addClassOnOver('fc-state-hover');
14687         
14688         var cells = this.cells.elements;
14689         var textEls = this.textNodes;
14690         
14691         Roo.each(cells, function(cell){
14692             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14693         });
14694         
14695         days += startingPos;
14696
14697         // convert everything to numbers so it's fast
14698         var day = 86400000;
14699         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14700         //Roo.log(d);
14701         //Roo.log(pm);
14702         //Roo.log(prevStart);
14703         
14704         var today = new Date().clearTime().getTime();
14705         var sel = date.clearTime().getTime();
14706         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14707         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14708         var ddMatch = this.disabledDatesRE;
14709         var ddText = this.disabledDatesText;
14710         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14711         var ddaysText = this.disabledDaysText;
14712         var format = this.format;
14713         
14714         var setCellClass = function(cal, cell){
14715             cell.row = 0;
14716             cell.events = [];
14717             cell.more = [];
14718             //Roo.log('set Cell Class');
14719             cell.title = "";
14720             var t = d.getTime();
14721             
14722             //Roo.log(d);
14723             
14724             cell.dateValue = t;
14725             if(t == today){
14726                 cell.className += " fc-today";
14727                 cell.className += " fc-state-highlight";
14728                 cell.title = cal.todayText;
14729             }
14730             if(t == sel){
14731                 // disable highlight in other month..
14732                 //cell.className += " fc-state-highlight";
14733                 
14734             }
14735             // disabling
14736             if(t < min) {
14737                 cell.className = " fc-state-disabled";
14738                 cell.title = cal.minText;
14739                 return;
14740             }
14741             if(t > max) {
14742                 cell.className = " fc-state-disabled";
14743                 cell.title = cal.maxText;
14744                 return;
14745             }
14746             if(ddays){
14747                 if(ddays.indexOf(d.getDay()) != -1){
14748                     cell.title = ddaysText;
14749                     cell.className = " fc-state-disabled";
14750                 }
14751             }
14752             if(ddMatch && format){
14753                 var fvalue = d.dateFormat(format);
14754                 if(ddMatch.test(fvalue)){
14755                     cell.title = ddText.replace("%0", fvalue);
14756                     cell.className = " fc-state-disabled";
14757                 }
14758             }
14759             
14760             if (!cell.initialClassName) {
14761                 cell.initialClassName = cell.dom.className;
14762             }
14763             
14764             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14765         };
14766
14767         var i = 0;
14768         
14769         for(; i < startingPos; i++) {
14770             textEls[i].innerHTML = (++prevStart);
14771             d.setDate(d.getDate()+1);
14772             
14773             cells[i].className = "fc-past fc-other-month";
14774             setCellClass(this, cells[i]);
14775         }
14776         
14777         var intDay = 0;
14778         
14779         for(; i < days; i++){
14780             intDay = i - startingPos + 1;
14781             textEls[i].innerHTML = (intDay);
14782             d.setDate(d.getDate()+1);
14783             
14784             cells[i].className = ''; // "x-date-active";
14785             setCellClass(this, cells[i]);
14786         }
14787         var extraDays = 0;
14788         
14789         for(; i < 42; i++) {
14790             textEls[i].innerHTML = (++extraDays);
14791             d.setDate(d.getDate()+1);
14792             
14793             cells[i].className = "fc-future fc-other-month";
14794             setCellClass(this, cells[i]);
14795         }
14796         
14797         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14798         
14799         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14800         
14801         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14802         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14803         
14804         if(totalRows != 6){
14805             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14806             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14807         }
14808         
14809         this.fireEvent('monthchange', this, date);
14810         
14811         
14812         /*
14813         if(!this.internalRender){
14814             var main = this.el.dom.firstChild;
14815             var w = main.offsetWidth;
14816             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14817             Roo.fly(main).setWidth(w);
14818             this.internalRender = true;
14819             // opera does not respect the auto grow header center column
14820             // then, after it gets a width opera refuses to recalculate
14821             // without a second pass
14822             if(Roo.isOpera && !this.secondPass){
14823                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14824                 this.secondPass = true;
14825                 this.update.defer(10, this, [date]);
14826             }
14827         }
14828         */
14829         
14830     },
14831     
14832     findCell : function(dt) {
14833         dt = dt.clearTime().getTime();
14834         var ret = false;
14835         this.cells.each(function(c){
14836             //Roo.log("check " +c.dateValue + '?=' + dt);
14837             if(c.dateValue == dt){
14838                 ret = c;
14839                 return false;
14840             }
14841             return true;
14842         });
14843         
14844         return ret;
14845     },
14846     
14847     findCells : function(ev) {
14848         var s = ev.start.clone().clearTime().getTime();
14849        // Roo.log(s);
14850         var e= ev.end.clone().clearTime().getTime();
14851        // Roo.log(e);
14852         var ret = [];
14853         this.cells.each(function(c){
14854              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14855             
14856             if(c.dateValue > e){
14857                 return ;
14858             }
14859             if(c.dateValue < s){
14860                 return ;
14861             }
14862             ret.push(c);
14863         });
14864         
14865         return ret;    
14866     },
14867     
14868 //    findBestRow: function(cells)
14869 //    {
14870 //        var ret = 0;
14871 //        
14872 //        for (var i =0 ; i < cells.length;i++) {
14873 //            ret  = Math.max(cells[i].rows || 0,ret);
14874 //        }
14875 //        return ret;
14876 //        
14877 //    },
14878     
14879     
14880     addItem : function(ev)
14881     {
14882         // look for vertical location slot in
14883         var cells = this.findCells(ev);
14884         
14885 //        ev.row = this.findBestRow(cells);
14886         
14887         // work out the location.
14888         
14889         var crow = false;
14890         var rows = [];
14891         for(var i =0; i < cells.length; i++) {
14892             
14893             cells[i].row = cells[0].row;
14894             
14895             if(i == 0){
14896                 cells[i].row = cells[i].row + 1;
14897             }
14898             
14899             if (!crow) {
14900                 crow = {
14901                     start : cells[i],
14902                     end :  cells[i]
14903                 };
14904                 continue;
14905             }
14906             if (crow.start.getY() == cells[i].getY()) {
14907                 // on same row.
14908                 crow.end = cells[i];
14909                 continue;
14910             }
14911             // different row.
14912             rows.push(crow);
14913             crow = {
14914                 start: cells[i],
14915                 end : cells[i]
14916             };
14917             
14918         }
14919         
14920         rows.push(crow);
14921         ev.els = [];
14922         ev.rows = rows;
14923         ev.cells = cells;
14924         
14925         cells[0].events.push(ev);
14926         
14927         this.calevents.push(ev);
14928     },
14929     
14930     clearEvents: function() {
14931         
14932         if(!this.calevents){
14933             return;
14934         }
14935         
14936         Roo.each(this.cells.elements, function(c){
14937             c.row = 0;
14938             c.events = [];
14939             c.more = [];
14940         });
14941         
14942         Roo.each(this.calevents, function(e) {
14943             Roo.each(e.els, function(el) {
14944                 el.un('mouseenter' ,this.onEventEnter, this);
14945                 el.un('mouseleave' ,this.onEventLeave, this);
14946                 el.remove();
14947             },this);
14948         },this);
14949         
14950         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14951             e.remove();
14952         });
14953         
14954     },
14955     
14956     renderEvents: function()
14957     {   
14958         var _this = this;
14959         
14960         this.cells.each(function(c) {
14961             
14962             if(c.row < 5){
14963                 return;
14964             }
14965             
14966             var ev = c.events;
14967             
14968             var r = 4;
14969             if(c.row != c.events.length){
14970                 r = 4 - (4 - (c.row - c.events.length));
14971             }
14972             
14973             c.events = ev.slice(0, r);
14974             c.more = ev.slice(r);
14975             
14976             if(c.more.length && c.more.length == 1){
14977                 c.events.push(c.more.pop());
14978             }
14979             
14980             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14981             
14982         });
14983             
14984         this.cells.each(function(c) {
14985             
14986             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14987             
14988             
14989             for (var e = 0; e < c.events.length; e++){
14990                 var ev = c.events[e];
14991                 var rows = ev.rows;
14992                 
14993                 for(var i = 0; i < rows.length; i++) {
14994                 
14995                     // how many rows should it span..
14996
14997                     var  cfg = {
14998                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14999                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15000
15001                         unselectable : "on",
15002                         cn : [
15003                             {
15004                                 cls: 'fc-event-inner',
15005                                 cn : [
15006     //                                {
15007     //                                  tag:'span',
15008     //                                  cls: 'fc-event-time',
15009     //                                  html : cells.length > 1 ? '' : ev.time
15010     //                                },
15011                                     {
15012                                       tag:'span',
15013                                       cls: 'fc-event-title',
15014                                       html : String.format('{0}', ev.title)
15015                                     }
15016
15017
15018                                 ]
15019                             },
15020                             {
15021                                 cls: 'ui-resizable-handle ui-resizable-e',
15022                                 html : '&nbsp;&nbsp;&nbsp'
15023                             }
15024
15025                         ]
15026                     };
15027
15028                     if (i == 0) {
15029                         cfg.cls += ' fc-event-start';
15030                     }
15031                     if ((i+1) == rows.length) {
15032                         cfg.cls += ' fc-event-end';
15033                     }
15034
15035                     var ctr = _this.el.select('.fc-event-container',true).first();
15036                     var cg = ctr.createChild(cfg);
15037
15038                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15039                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15040
15041                     var r = (c.more.length) ? 1 : 0;
15042                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15043                     cg.setWidth(ebox.right - sbox.x -2);
15044
15045                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15046                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15047                     cg.on('click', _this.onEventClick, _this, ev);
15048
15049                     ev.els.push(cg);
15050                     
15051                 }
15052                 
15053             }
15054             
15055             
15056             if(c.more.length){
15057                 var  cfg = {
15058                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15059                     style : 'position: absolute',
15060                     unselectable : "on",
15061                     cn : [
15062                         {
15063                             cls: 'fc-event-inner',
15064                             cn : [
15065                                 {
15066                                   tag:'span',
15067                                   cls: 'fc-event-title',
15068                                   html : 'More'
15069                                 }
15070
15071
15072                             ]
15073                         },
15074                         {
15075                             cls: 'ui-resizable-handle ui-resizable-e',
15076                             html : '&nbsp;&nbsp;&nbsp'
15077                         }
15078
15079                     ]
15080                 };
15081
15082                 var ctr = _this.el.select('.fc-event-container',true).first();
15083                 var cg = ctr.createChild(cfg);
15084
15085                 var sbox = c.select('.fc-day-content',true).first().getBox();
15086                 var ebox = c.select('.fc-day-content',true).first().getBox();
15087                 //Roo.log(cg);
15088                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15089                 cg.setWidth(ebox.right - sbox.x -2);
15090
15091                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15092                 
15093             }
15094             
15095         });
15096         
15097         
15098         
15099     },
15100     
15101     onEventEnter: function (e, el,event,d) {
15102         this.fireEvent('evententer', this, el, event);
15103     },
15104     
15105     onEventLeave: function (e, el,event,d) {
15106         this.fireEvent('eventleave', this, el, event);
15107     },
15108     
15109     onEventClick: function (e, el,event,d) {
15110         this.fireEvent('eventclick', this, el, event);
15111     },
15112     
15113     onMonthChange: function () {
15114         this.store.load();
15115     },
15116     
15117     onMoreEventClick: function(e, el, more)
15118     {
15119         var _this = this;
15120         
15121         this.calpopover.placement = 'right';
15122         this.calpopover.setTitle('More');
15123         
15124         this.calpopover.setContent('');
15125         
15126         var ctr = this.calpopover.el.select('.popover-content', true).first();
15127         
15128         Roo.each(more, function(m){
15129             var cfg = {
15130                 cls : 'fc-event-hori fc-event-draggable',
15131                 html : m.title
15132             }
15133             var cg = ctr.createChild(cfg);
15134             
15135             cg.on('click', _this.onEventClick, _this, m);
15136         });
15137         
15138         this.calpopover.show(el);
15139         
15140         
15141     },
15142     
15143     onLoad: function () 
15144     {   
15145         this.calevents = [];
15146         var cal = this;
15147         
15148         if(this.store.getCount() > 0){
15149             this.store.data.each(function(d){
15150                cal.addItem({
15151                     id : d.data.id,
15152                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15153                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15154                     time : d.data.start_time,
15155                     title : d.data.title,
15156                     description : d.data.description,
15157                     venue : d.data.venue
15158                 });
15159             });
15160         }
15161         
15162         this.renderEvents();
15163         
15164         if(this.calevents.length && this.loadMask){
15165             this.maskEl.hide();
15166         }
15167     },
15168     
15169     onBeforeLoad: function()
15170     {
15171         this.clearEvents();
15172         if(this.loadMask){
15173             this.maskEl.show();
15174         }
15175     }
15176 });
15177
15178  
15179  /*
15180  * - LGPL
15181  *
15182  * element
15183  * 
15184  */
15185
15186 /**
15187  * @class Roo.bootstrap.Popover
15188  * @extends Roo.bootstrap.Component
15189  * Bootstrap Popover class
15190  * @cfg {String} html contents of the popover   (or false to use children..)
15191  * @cfg {String} title of popover (or false to hide)
15192  * @cfg {String} placement how it is placed
15193  * @cfg {String} trigger click || hover (or false to trigger manually)
15194  * @cfg {String} over what (parent or false to trigger manually.)
15195  * @cfg {Number} delay - delay before showing
15196  
15197  * @constructor
15198  * Create a new Popover
15199  * @param {Object} config The config object
15200  */
15201
15202 Roo.bootstrap.Popover = function(config){
15203     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15204 };
15205
15206 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15207     
15208     title: 'Fill in a title',
15209     html: false,
15210     
15211     placement : 'right',
15212     trigger : 'hover', // hover
15213     
15214     delay : 0,
15215     
15216     over: 'parent',
15217     
15218     can_build_overlaid : false,
15219     
15220     getChildContainer : function()
15221     {
15222         return this.el.select('.popover-content',true).first();
15223     },
15224     
15225     getAutoCreate : function(){
15226          Roo.log('make popover?');
15227         var cfg = {
15228            cls : 'popover roo-dynamic',
15229            style: 'display:block',
15230            cn : [
15231                 {
15232                     cls : 'arrow'
15233                 },
15234                 {
15235                     cls : 'popover-inner',
15236                     cn : [
15237                         {
15238                             tag: 'h3',
15239                             cls: 'popover-title',
15240                             html : this.title
15241                         },
15242                         {
15243                             cls : 'popover-content',
15244                             html : this.html
15245                         }
15246                     ]
15247                     
15248                 }
15249            ]
15250         };
15251         
15252         return cfg;
15253     },
15254     setTitle: function(str)
15255     {
15256         this.title = str;
15257         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15258     },
15259     setContent: function(str)
15260     {
15261         this.html = str;
15262         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15263     },
15264     // as it get's added to the bottom of the page.
15265     onRender : function(ct, position)
15266     {
15267         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15268         if(!this.el){
15269             var cfg = Roo.apply({},  this.getAutoCreate());
15270             cfg.id = Roo.id();
15271             
15272             if (this.cls) {
15273                 cfg.cls += ' ' + this.cls;
15274             }
15275             if (this.style) {
15276                 cfg.style = this.style;
15277             }
15278             Roo.log("adding to ")
15279             this.el = Roo.get(document.body).createChild(cfg, position);
15280             Roo.log(this.el);
15281         }
15282         this.initEvents();
15283     },
15284     
15285     initEvents : function()
15286     {
15287         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15288         this.el.enableDisplayMode('block');
15289         this.el.hide();
15290         if (this.over === false) {
15291             return; 
15292         }
15293         if (this.triggers === false) {
15294             return;
15295         }
15296         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15297         var triggers = this.trigger ? this.trigger.split(' ') : [];
15298         Roo.each(triggers, function(trigger) {
15299         
15300             if (trigger == 'click') {
15301                 on_el.on('click', this.toggle, this);
15302             } else if (trigger != 'manual') {
15303                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15304                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15305       
15306                 on_el.on(eventIn  ,this.enter, this);
15307                 on_el.on(eventOut, this.leave, this);
15308             }
15309         }, this);
15310         
15311     },
15312     
15313     
15314     // private
15315     timeout : null,
15316     hoverState : null,
15317     
15318     toggle : function () {
15319         this.hoverState == 'in' ? this.leave() : this.enter();
15320     },
15321     
15322     enter : function () {
15323        
15324     
15325         clearTimeout(this.timeout);
15326     
15327         this.hoverState = 'in';
15328     
15329         if (!this.delay || !this.delay.show) {
15330             this.show();
15331             return;
15332         }
15333         var _t = this;
15334         this.timeout = setTimeout(function () {
15335             if (_t.hoverState == 'in') {
15336                 _t.show();
15337             }
15338         }, this.delay.show)
15339     },
15340     leave : function() {
15341         clearTimeout(this.timeout);
15342     
15343         this.hoverState = 'out';
15344     
15345         if (!this.delay || !this.delay.hide) {
15346             this.hide();
15347             return;
15348         }
15349         var _t = this;
15350         this.timeout = setTimeout(function () {
15351             if (_t.hoverState == 'out') {
15352                 _t.hide();
15353             }
15354         }, this.delay.hide)
15355     },
15356     
15357     show : function (on_el)
15358     {
15359         if (!on_el) {
15360             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15361         }
15362         // set content.
15363         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15364         if (this.html !== false) {
15365             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15366         }
15367         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15368         if (!this.title.length) {
15369             this.el.select('.popover-title',true).hide();
15370         }
15371         
15372         var placement = typeof this.placement == 'function' ?
15373             this.placement.call(this, this.el, on_el) :
15374             this.placement;
15375             
15376         var autoToken = /\s?auto?\s?/i;
15377         var autoPlace = autoToken.test(placement);
15378         if (autoPlace) {
15379             placement = placement.replace(autoToken, '') || 'top';
15380         }
15381         
15382         //this.el.detach()
15383         //this.el.setXY([0,0]);
15384         this.el.show();
15385         this.el.dom.style.display='block';
15386         this.el.addClass(placement);
15387         
15388         //this.el.appendTo(on_el);
15389         
15390         var p = this.getPosition();
15391         var box = this.el.getBox();
15392         
15393         if (autoPlace) {
15394             // fixme..
15395         }
15396         var align = Roo.bootstrap.Popover.alignment[placement];
15397         this.el.alignTo(on_el, align[0],align[1]);
15398         //var arrow = this.el.select('.arrow',true).first();
15399         //arrow.set(align[2], 
15400         
15401         this.el.addClass('in');
15402         
15403         
15404         if (this.el.hasClass('fade')) {
15405             // fade it?
15406         }
15407         
15408     },
15409     hide : function()
15410     {
15411         this.el.setXY([0,0]);
15412         this.el.removeClass('in');
15413         this.el.hide();
15414         this.hoverState = null;
15415         
15416     }
15417     
15418 });
15419
15420 Roo.bootstrap.Popover.alignment = {
15421     'left' : ['r-l', [-10,0], 'right'],
15422     'right' : ['l-r', [10,0], 'left'],
15423     'bottom' : ['t-b', [0,10], 'top'],
15424     'top' : [ 'b-t', [0,-10], 'bottom']
15425 };
15426
15427  /*
15428  * - LGPL
15429  *
15430  * Progress
15431  * 
15432  */
15433
15434 /**
15435  * @class Roo.bootstrap.Progress
15436  * @extends Roo.bootstrap.Component
15437  * Bootstrap Progress class
15438  * @cfg {Boolean} striped striped of the progress bar
15439  * @cfg {Boolean} active animated of the progress bar
15440  * 
15441  * 
15442  * @constructor
15443  * Create a new Progress
15444  * @param {Object} config The config object
15445  */
15446
15447 Roo.bootstrap.Progress = function(config){
15448     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15449 };
15450
15451 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15452     
15453     striped : false,
15454     active: false,
15455     
15456     getAutoCreate : function(){
15457         var cfg = {
15458             tag: 'div',
15459             cls: 'progress'
15460         };
15461         
15462         
15463         if(this.striped){
15464             cfg.cls += ' progress-striped';
15465         }
15466       
15467         if(this.active){
15468             cfg.cls += ' active';
15469         }
15470         
15471         
15472         return cfg;
15473     }
15474    
15475 });
15476
15477  
15478
15479  /*
15480  * - LGPL
15481  *
15482  * ProgressBar
15483  * 
15484  */
15485
15486 /**
15487  * @class Roo.bootstrap.ProgressBar
15488  * @extends Roo.bootstrap.Component
15489  * Bootstrap ProgressBar class
15490  * @cfg {Number} aria_valuenow aria-value now
15491  * @cfg {Number} aria_valuemin aria-value min
15492  * @cfg {Number} aria_valuemax aria-value max
15493  * @cfg {String} label label for the progress bar
15494  * @cfg {String} panel (success | info | warning | danger )
15495  * @cfg {String} role role of the progress bar
15496  * @cfg {String} sr_only text
15497  * 
15498  * 
15499  * @constructor
15500  * Create a new ProgressBar
15501  * @param {Object} config The config object
15502  */
15503
15504 Roo.bootstrap.ProgressBar = function(config){
15505     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15506 };
15507
15508 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15509     
15510     aria_valuenow : 0,
15511     aria_valuemin : 0,
15512     aria_valuemax : 100,
15513     label : false,
15514     panel : false,
15515     role : false,
15516     sr_only: false,
15517     
15518     getAutoCreate : function()
15519     {
15520         
15521         var cfg = {
15522             tag: 'div',
15523             cls: 'progress-bar',
15524             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15525         };
15526         
15527         if(this.sr_only){
15528             cfg.cn = {
15529                 tag: 'span',
15530                 cls: 'sr-only',
15531                 html: this.sr_only
15532             }
15533         }
15534         
15535         if(this.role){
15536             cfg.role = this.role;
15537         }
15538         
15539         if(this.aria_valuenow){
15540             cfg['aria-valuenow'] = this.aria_valuenow;
15541         }
15542         
15543         if(this.aria_valuemin){
15544             cfg['aria-valuemin'] = this.aria_valuemin;
15545         }
15546         
15547         if(this.aria_valuemax){
15548             cfg['aria-valuemax'] = this.aria_valuemax;
15549         }
15550         
15551         if(this.label && !this.sr_only){
15552             cfg.html = this.label;
15553         }
15554         
15555         if(this.panel){
15556             cfg.cls += ' progress-bar-' + this.panel;
15557         }
15558         
15559         return cfg;
15560     },
15561     
15562     update : function(aria_valuenow)
15563     {
15564         this.aria_valuenow = aria_valuenow;
15565         
15566         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15567     }
15568    
15569 });
15570
15571  
15572
15573  /*
15574  * - LGPL
15575  *
15576  * column
15577  * 
15578  */
15579
15580 /**
15581  * @class Roo.bootstrap.TabGroup
15582  * @extends Roo.bootstrap.Column
15583  * Bootstrap Column class
15584  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15585  * @cfg {Boolean} carousel true to make the group behave like a carousel
15586  * @cfg {Number} bullets show the panel pointer.. default 0
15587  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15588  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15589  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15590  * 
15591  * @constructor
15592  * Create a new TabGroup
15593  * @param {Object} config The config object
15594  */
15595
15596 Roo.bootstrap.TabGroup = function(config){
15597     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15598     if (!this.navId) {
15599         this.navId = Roo.id();
15600     }
15601     this.tabs = [];
15602     Roo.bootstrap.TabGroup.register(this);
15603     
15604 };
15605
15606 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15607     
15608     carousel : false,
15609     transition : false,
15610     bullets : 0,
15611     timer : 0,
15612     autoslide : false,
15613     slideFn : false,
15614     slideOnTouch : false,
15615     
15616     getAutoCreate : function()
15617     {
15618         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15619         
15620         cfg.cls += ' tab-content';
15621         
15622         Roo.log('get auto create...............');
15623         
15624         if (this.carousel) {
15625             cfg.cls += ' carousel slide';
15626             
15627             cfg.cn = [{
15628                cls : 'carousel-inner'
15629             }];
15630         
15631             if(this.bullets > 0 && !Roo.isTouch){
15632                 
15633                 var bullets = {
15634                     cls : 'carousel-bullets',
15635                     cn : []
15636                 };
15637                 
15638                 if(this.bullets_cls){
15639                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15640                 }
15641                 
15642                 for (var i = 0; i < this.bullets; i++){
15643                     bullets.cn.push({
15644                         cls : 'bullet bullet-' + i
15645                     });
15646                 }
15647                 
15648                 bullets.cn.push({
15649                     cls : 'clear'
15650                 });
15651                 
15652                 cfg.cn[0].cn = bullets;
15653             }
15654         }
15655         
15656         return cfg;
15657     },
15658     
15659     initEvents:  function()
15660     {
15661         Roo.log('-------- init events on tab group ---------');
15662         
15663         if(this.bullets > 0 && !Roo.isTouch){
15664             this.initBullet();
15665         }
15666         
15667         Roo.log(this);
15668         
15669         if(Roo.isTouch && this.slideOnTouch){
15670             this.el.on("touchstart", this.onTouchStart, this);
15671         }
15672         
15673         if(this.autoslide){
15674             var _this = this;
15675             
15676             this.slideFn = window.setInterval(function() {
15677                 _this.showPanelNext();
15678             }, this.timer);
15679         }
15680         
15681     },
15682     
15683     onTouchStart : function(e, el, o)
15684     {
15685         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15686             return;
15687         }
15688         
15689         this.showPanelNext();
15690     },
15691     
15692     getChildContainer : function()
15693     {
15694         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15695     },
15696     
15697     /**
15698     * register a Navigation item
15699     * @param {Roo.bootstrap.NavItem} the navitem to add
15700     */
15701     register : function(item)
15702     {
15703         this.tabs.push( item);
15704         item.navId = this.navId; // not really needed..
15705     
15706     },
15707     
15708     getActivePanel : function()
15709     {
15710         var r = false;
15711         Roo.each(this.tabs, function(t) {
15712             if (t.active) {
15713                 r = t;
15714                 return false;
15715             }
15716             return null;
15717         });
15718         return r;
15719         
15720     },
15721     getPanelByName : function(n)
15722     {
15723         var r = false;
15724         Roo.each(this.tabs, function(t) {
15725             if (t.tabId == n) {
15726                 r = t;
15727                 return false;
15728             }
15729             return null;
15730         });
15731         return r;
15732     },
15733     indexOfPanel : function(p)
15734     {
15735         var r = false;
15736         Roo.each(this.tabs, function(t,i) {
15737             if (t.tabId == p.tabId) {
15738                 r = i;
15739                 return false;
15740             }
15741             return null;
15742         });
15743         return r;
15744     },
15745     /**
15746      * show a specific panel
15747      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15748      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15749      */
15750     showPanel : function (pan)
15751     {
15752         if(this.transition){
15753             Roo.log("waiting for the transitionend");
15754             return;
15755         }
15756         
15757         if (typeof(pan) == 'number') {
15758             pan = this.tabs[pan];
15759         }
15760         if (typeof(pan) == 'string') {
15761             pan = this.getPanelByName(pan);
15762         }
15763         if (pan.tabId == this.getActivePanel().tabId) {
15764             return true;
15765         }
15766         var cur = this.getActivePanel();
15767         
15768         if (false === cur.fireEvent('beforedeactivate')) {
15769             return false;
15770         }
15771         
15772         if(this.bullets > 0 && !Roo.isTouch){
15773             this.setActiveBullet(this.indexOfPanel(pan));
15774         }
15775         
15776         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15777             
15778             this.transition = true;
15779             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15780             var lr = dir == 'next' ? 'left' : 'right';
15781             pan.el.addClass(dir); // or prev
15782             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15783             cur.el.addClass(lr); // or right
15784             pan.el.addClass(lr);
15785             
15786             var _this = this;
15787             cur.el.on('transitionend', function() {
15788                 Roo.log("trans end?");
15789                 
15790                 pan.el.removeClass([lr,dir]);
15791                 pan.setActive(true);
15792                 
15793                 cur.el.removeClass([lr]);
15794                 cur.setActive(false);
15795                 
15796                 _this.transition = false;
15797                 
15798             }, this, { single:  true } );
15799             
15800             return true;
15801         }
15802         
15803         cur.setActive(false);
15804         pan.setActive(true);
15805         
15806         return true;
15807         
15808     },
15809     showPanelNext : function()
15810     {
15811         var i = this.indexOfPanel(this.getActivePanel());
15812         
15813         if (i >= this.tabs.length - 1 && !this.autoslide) {
15814             return;
15815         }
15816         
15817         if (i >= this.tabs.length - 1 && this.autoslide) {
15818             i = -1;
15819         }
15820         
15821         this.showPanel(this.tabs[i+1]);
15822     },
15823     
15824     showPanelPrev : function()
15825     {
15826         var i = this.indexOfPanel(this.getActivePanel());
15827         
15828         if (i  < 1 && !this.autoslide) {
15829             return;
15830         }
15831         
15832         if (i < 1 && this.autoslide) {
15833             i = this.tabs.length;
15834         }
15835         
15836         this.showPanel(this.tabs[i-1]);
15837     },
15838     
15839     initBullet : function()
15840     {
15841         if(Roo.isTouch){
15842             return;
15843         }
15844         
15845         var _this = this;
15846         
15847         for (var i = 0; i < this.bullets; i++){
15848             var bullet = this.el.select('.bullet-' + i, true).first();
15849
15850             if(!bullet){
15851                 continue;
15852             }
15853
15854             bullet.on('click', (function(e, el, o, ii, t){
15855
15856                 e.preventDefault();
15857
15858                 _this.showPanel(ii);
15859
15860                 if(_this.autoslide && _this.slideFn){
15861                     clearInterval(_this.slideFn);
15862                     _this.slideFn = window.setInterval(function() {
15863                         _this.showPanelNext();
15864                     }, _this.timer);
15865                 }
15866
15867             }).createDelegate(this, [i, bullet], true));
15868         }
15869     },
15870     
15871     setActiveBullet : function(i)
15872     {
15873         if(Roo.isTouch){
15874             return;
15875         }
15876         
15877         Roo.each(this.el.select('.bullet', true).elements, function(el){
15878             el.removeClass('selected');
15879         });
15880
15881         var bullet = this.el.select('.bullet-' + i, true).first();
15882         
15883         if(!bullet){
15884             return;
15885         }
15886         
15887         bullet.addClass('selected');
15888     }
15889     
15890     
15891   
15892 });
15893
15894  
15895
15896  
15897  
15898 Roo.apply(Roo.bootstrap.TabGroup, {
15899     
15900     groups: {},
15901      /**
15902     * register a Navigation Group
15903     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15904     */
15905     register : function(navgrp)
15906     {
15907         this.groups[navgrp.navId] = navgrp;
15908         
15909     },
15910     /**
15911     * fetch a Navigation Group based on the navigation ID
15912     * if one does not exist , it will get created.
15913     * @param {string} the navgroup to add
15914     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15915     */
15916     get: function(navId) {
15917         if (typeof(this.groups[navId]) == 'undefined') {
15918             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15919         }
15920         return this.groups[navId] ;
15921     }
15922     
15923     
15924     
15925 });
15926
15927  /*
15928  * - LGPL
15929  *
15930  * TabPanel
15931  * 
15932  */
15933
15934 /**
15935  * @class Roo.bootstrap.TabPanel
15936  * @extends Roo.bootstrap.Component
15937  * Bootstrap TabPanel class
15938  * @cfg {Boolean} active panel active
15939  * @cfg {String} html panel content
15940  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15941  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15942  * 
15943  * 
15944  * @constructor
15945  * Create a new TabPanel
15946  * @param {Object} config The config object
15947  */
15948
15949 Roo.bootstrap.TabPanel = function(config){
15950     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15951     this.addEvents({
15952         /**
15953              * @event changed
15954              * Fires when the active status changes
15955              * @param {Roo.bootstrap.TabPanel} this
15956              * @param {Boolean} state the new state
15957             
15958          */
15959         'changed': true,
15960         /**
15961              * @event beforedeactivate
15962              * Fires before a tab is de-activated - can be used to do validation on a form.
15963              * @param {Roo.bootstrap.TabPanel} this
15964              * @return {Boolean} false if there is an error
15965             
15966          */
15967         'beforedeactivate': true
15968      });
15969     
15970     this.tabId = this.tabId || Roo.id();
15971   
15972 };
15973
15974 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15975     
15976     active: false,
15977     html: false,
15978     tabId: false,
15979     navId : false,
15980     
15981     getAutoCreate : function(){
15982         var cfg = {
15983             tag: 'div',
15984             // item is needed for carousel - not sure if it has any effect otherwise
15985             cls: 'tab-pane item',
15986             html: this.html || ''
15987         };
15988         
15989         if(this.active){
15990             cfg.cls += ' active';
15991         }
15992         
15993         if(this.tabId){
15994             cfg.tabId = this.tabId;
15995         }
15996         
15997         
15998         return cfg;
15999     },
16000     
16001     initEvents:  function()
16002     {
16003         Roo.log('-------- init events on tab panel ---------');
16004         
16005         var p = this.parent();
16006         this.navId = this.navId || p.navId;
16007         
16008         if (typeof(this.navId) != 'undefined') {
16009             // not really needed.. but just in case.. parent should be a NavGroup.
16010             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16011             Roo.log(['register', tg, this]);
16012             tg.register(this);
16013             
16014             var i = tg.tabs.length - 1;
16015             
16016             if(this.active && tg.bullets > 0 && i < tg.bullets){
16017                 tg.setActiveBullet(i);
16018             }
16019         }
16020         
16021     },
16022     
16023     
16024     onRender : function(ct, position)
16025     {
16026        // Roo.log("Call onRender: " + this.xtype);
16027         
16028         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16029         
16030         
16031         
16032         
16033         
16034     },
16035     
16036     setActive: function(state)
16037     {
16038         Roo.log("panel - set active " + this.tabId + "=" + state);
16039         
16040         this.active = state;
16041         if (!state) {
16042             this.el.removeClass('active');
16043             
16044         } else  if (!this.el.hasClass('active')) {
16045             this.el.addClass('active');
16046         }
16047         
16048         this.fireEvent('changed', this, state);
16049     }
16050     
16051     
16052 });
16053  
16054
16055  
16056
16057  /*
16058  * - LGPL
16059  *
16060  * DateField
16061  * 
16062  */
16063
16064 /**
16065  * @class Roo.bootstrap.DateField
16066  * @extends Roo.bootstrap.Input
16067  * Bootstrap DateField class
16068  * @cfg {Number} weekStart default 0
16069  * @cfg {String} viewMode default empty, (months|years)
16070  * @cfg {String} minViewMode default empty, (months|years)
16071  * @cfg {Number} startDate default -Infinity
16072  * @cfg {Number} endDate default Infinity
16073  * @cfg {Boolean} todayHighlight default false
16074  * @cfg {Boolean} todayBtn default false
16075  * @cfg {Boolean} calendarWeeks default false
16076  * @cfg {Object} daysOfWeekDisabled default empty
16077  * @cfg {Boolean} singleMode default false (true | false)
16078  * 
16079  * @cfg {Boolean} keyboardNavigation default true
16080  * @cfg {String} language default en
16081  * 
16082  * @constructor
16083  * Create a new DateField
16084  * @param {Object} config The config object
16085  */
16086
16087 Roo.bootstrap.DateField = function(config){
16088     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16089      this.addEvents({
16090             /**
16091              * @event show
16092              * Fires when this field show.
16093              * @param {Roo.bootstrap.DateField} this
16094              * @param {Mixed} date The date value
16095              */
16096             show : true,
16097             /**
16098              * @event show
16099              * Fires when this field hide.
16100              * @param {Roo.bootstrap.DateField} this
16101              * @param {Mixed} date The date value
16102              */
16103             hide : true,
16104             /**
16105              * @event select
16106              * Fires when select a date.
16107              * @param {Roo.bootstrap.DateField} this
16108              * @param {Mixed} date The date value
16109              */
16110             select : true
16111         });
16112 };
16113
16114 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16115     
16116     /**
16117      * @cfg {String} format
16118      * The default date format string which can be overriden for localization support.  The format must be
16119      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16120      */
16121     format : "m/d/y",
16122     /**
16123      * @cfg {String} altFormats
16124      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16125      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16126      */
16127     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16128     
16129     weekStart : 0,
16130     
16131     viewMode : '',
16132     
16133     minViewMode : '',
16134     
16135     todayHighlight : false,
16136     
16137     todayBtn: false,
16138     
16139     language: 'en',
16140     
16141     keyboardNavigation: true,
16142     
16143     calendarWeeks: false,
16144     
16145     startDate: -Infinity,
16146     
16147     endDate: Infinity,
16148     
16149     daysOfWeekDisabled: [],
16150     
16151     _events: [],
16152     
16153     singleMode : false,
16154     
16155     UTCDate: function()
16156     {
16157         return new Date(Date.UTC.apply(Date, arguments));
16158     },
16159     
16160     UTCToday: function()
16161     {
16162         var today = new Date();
16163         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16164     },
16165     
16166     getDate: function() {
16167             var d = this.getUTCDate();
16168             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16169     },
16170     
16171     getUTCDate: function() {
16172             return this.date;
16173     },
16174     
16175     setDate: function(d) {
16176             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16177     },
16178     
16179     setUTCDate: function(d) {
16180             this.date = d;
16181             this.setValue(this.formatDate(this.date));
16182     },
16183         
16184     onRender: function(ct, position)
16185     {
16186         
16187         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16188         
16189         this.language = this.language || 'en';
16190         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16191         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16192         
16193         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16194         this.format = this.format || 'm/d/y';
16195         this.isInline = false;
16196         this.isInput = true;
16197         this.component = this.el.select('.add-on', true).first() || false;
16198         this.component = (this.component && this.component.length === 0) ? false : this.component;
16199         this.hasInput = this.component && this.inputEL().length;
16200         
16201         if (typeof(this.minViewMode === 'string')) {
16202             switch (this.minViewMode) {
16203                 case 'months':
16204                     this.minViewMode = 1;
16205                     break;
16206                 case 'years':
16207                     this.minViewMode = 2;
16208                     break;
16209                 default:
16210                     this.minViewMode = 0;
16211                     break;
16212             }
16213         }
16214         
16215         if (typeof(this.viewMode === 'string')) {
16216             switch (this.viewMode) {
16217                 case 'months':
16218                     this.viewMode = 1;
16219                     break;
16220                 case 'years':
16221                     this.viewMode = 2;
16222                     break;
16223                 default:
16224                     this.viewMode = 0;
16225                     break;
16226             }
16227         }
16228                 
16229         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16230         
16231 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16232         
16233         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16234         
16235         this.picker().on('mousedown', this.onMousedown, this);
16236         this.picker().on('click', this.onClick, this);
16237         
16238         this.picker().addClass('datepicker-dropdown');
16239         
16240         this.startViewMode = this.viewMode;
16241         
16242         if(this.singleMode){
16243             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16244                 v.setVisibilityMode(Roo.Element.DISPLAY)
16245                 v.hide();
16246             });
16247             
16248             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16249                 v.setStyle('width', '189px');
16250             });
16251         }
16252         
16253         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16254             if(!this.calendarWeeks){
16255                 v.remove();
16256                 return;
16257             }
16258             
16259             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16260             v.attr('colspan', function(i, val){
16261                 return parseInt(val) + 1;
16262             });
16263         })
16264                         
16265         
16266         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16267         
16268         this.setStartDate(this.startDate);
16269         this.setEndDate(this.endDate);
16270         
16271         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16272         
16273         this.fillDow();
16274         this.fillMonths();
16275         this.update();
16276         this.showMode();
16277         
16278         if(this.isInline) {
16279             this.show();
16280         }
16281     },
16282     
16283     picker : function()
16284     {
16285         return this.pickerEl;
16286 //        return this.el.select('.datepicker', true).first();
16287     },
16288     
16289     fillDow: function()
16290     {
16291         var dowCnt = this.weekStart;
16292         
16293         var dow = {
16294             tag: 'tr',
16295             cn: [
16296                 
16297             ]
16298         };
16299         
16300         if(this.calendarWeeks){
16301             dow.cn.push({
16302                 tag: 'th',
16303                 cls: 'cw',
16304                 html: '&nbsp;'
16305             })
16306         }
16307         
16308         while (dowCnt < this.weekStart + 7) {
16309             dow.cn.push({
16310                 tag: 'th',
16311                 cls: 'dow',
16312                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16313             });
16314         }
16315         
16316         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16317     },
16318     
16319     fillMonths: function()
16320     {    
16321         var i = 0;
16322         var months = this.picker().select('>.datepicker-months td', true).first();
16323         
16324         months.dom.innerHTML = '';
16325         
16326         while (i < 12) {
16327             var month = {
16328                 tag: 'span',
16329                 cls: 'month',
16330                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16331             }
16332             
16333             months.createChild(month);
16334         }
16335         
16336     },
16337     
16338     update: function()
16339     {
16340         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;
16341         
16342         if (this.date < this.startDate) {
16343             this.viewDate = new Date(this.startDate);
16344         } else if (this.date > this.endDate) {
16345             this.viewDate = new Date(this.endDate);
16346         } else {
16347             this.viewDate = new Date(this.date);
16348         }
16349         
16350         this.fill();
16351     },
16352     
16353     fill: function() 
16354     {
16355         var d = new Date(this.viewDate),
16356                 year = d.getUTCFullYear(),
16357                 month = d.getUTCMonth(),
16358                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16359                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16360                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16361                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16362                 currentDate = this.date && this.date.valueOf(),
16363                 today = this.UTCToday();
16364         
16365         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16366         
16367 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16368         
16369 //        this.picker.select('>tfoot th.today').
16370 //                                              .text(dates[this.language].today)
16371 //                                              .toggle(this.todayBtn !== false);
16372     
16373         this.updateNavArrows();
16374         this.fillMonths();
16375                                                 
16376         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16377         
16378         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16379          
16380         prevMonth.setUTCDate(day);
16381         
16382         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16383         
16384         var nextMonth = new Date(prevMonth);
16385         
16386         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16387         
16388         nextMonth = nextMonth.valueOf();
16389         
16390         var fillMonths = false;
16391         
16392         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16393         
16394         while(prevMonth.valueOf() < nextMonth) {
16395             var clsName = '';
16396             
16397             if (prevMonth.getUTCDay() === this.weekStart) {
16398                 if(fillMonths){
16399                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16400                 }
16401                     
16402                 fillMonths = {
16403                     tag: 'tr',
16404                     cn: []
16405                 };
16406                 
16407                 if(this.calendarWeeks){
16408                     // ISO 8601: First week contains first thursday.
16409                     // ISO also states week starts on Monday, but we can be more abstract here.
16410                     var
16411                     // Start of current week: based on weekstart/current date
16412                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16413                     // Thursday of this week
16414                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16415                     // First Thursday of year, year from thursday
16416                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16417                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16418                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16419                     
16420                     fillMonths.cn.push({
16421                         tag: 'td',
16422                         cls: 'cw',
16423                         html: calWeek
16424                     });
16425                 }
16426             }
16427             
16428             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16429                 clsName += ' old';
16430             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16431                 clsName += ' new';
16432             }
16433             if (this.todayHighlight &&
16434                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16435                 prevMonth.getUTCMonth() == today.getMonth() &&
16436                 prevMonth.getUTCDate() == today.getDate()) {
16437                 clsName += ' today';
16438             }
16439             
16440             if (currentDate && prevMonth.valueOf() === currentDate) {
16441                 clsName += ' active';
16442             }
16443             
16444             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16445                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16446                     clsName += ' disabled';
16447             }
16448             
16449             fillMonths.cn.push({
16450                 tag: 'td',
16451                 cls: 'day ' + clsName,
16452                 html: prevMonth.getDate()
16453             })
16454             
16455             prevMonth.setDate(prevMonth.getDate()+1);
16456         }
16457           
16458         var currentYear = this.date && this.date.getUTCFullYear();
16459         var currentMonth = this.date && this.date.getUTCMonth();
16460         
16461         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16462         
16463         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16464             v.removeClass('active');
16465             
16466             if(currentYear === year && k === currentMonth){
16467                 v.addClass('active');
16468             }
16469             
16470             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16471                 v.addClass('disabled');
16472             }
16473             
16474         });
16475         
16476         
16477         year = parseInt(year/10, 10) * 10;
16478         
16479         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16480         
16481         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16482         
16483         year -= 1;
16484         for (var i = -1; i < 11; i++) {
16485             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16486                 tag: 'span',
16487                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16488                 html: year
16489             })
16490             
16491             year += 1;
16492         }
16493     },
16494     
16495     showMode: function(dir) 
16496     {
16497         if (dir) {
16498             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16499         }
16500         
16501         Roo.each(this.picker().select('>div',true).elements, function(v){
16502             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16503             v.hide();
16504         });
16505         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16506     },
16507     
16508     place: function()
16509     {
16510         if(this.isInline) return;
16511         
16512         this.picker().removeClass(['bottom', 'top']);
16513         
16514         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16515             /*
16516              * place to the top of element!
16517              *
16518              */
16519             
16520             this.picker().addClass('top');
16521             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16522             
16523             return;
16524         }
16525         
16526         this.picker().addClass('bottom');
16527         
16528         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16529     },
16530     
16531     parseDate : function(value)
16532     {
16533         if(!value || value instanceof Date){
16534             return value;
16535         }
16536         var v = Date.parseDate(value, this.format);
16537         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16538             v = Date.parseDate(value, 'Y-m-d');
16539         }
16540         if(!v && this.altFormats){
16541             if(!this.altFormatsArray){
16542                 this.altFormatsArray = this.altFormats.split("|");
16543             }
16544             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16545                 v = Date.parseDate(value, this.altFormatsArray[i]);
16546             }
16547         }
16548         return v;
16549     },
16550     
16551     formatDate : function(date, fmt)
16552     {   
16553         return (!date || !(date instanceof Date)) ?
16554         date : date.dateFormat(fmt || this.format);
16555     },
16556     
16557     onFocus : function()
16558     {
16559         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16560         this.show();
16561     },
16562     
16563     onBlur : function()
16564     {
16565         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16566         
16567         var d = this.inputEl().getValue();
16568         
16569         this.setValue(d);
16570                 
16571         this.hide();
16572     },
16573     
16574     show : function()
16575     {
16576         this.picker().show();
16577         this.update();
16578         this.place();
16579         
16580         this.fireEvent('show', this, this.date);
16581     },
16582     
16583     hide : function()
16584     {
16585         if(this.isInline) return;
16586         this.picker().hide();
16587         this.viewMode = this.startViewMode;
16588         this.showMode();
16589         
16590         this.fireEvent('hide', this, this.date);
16591         
16592     },
16593     
16594     onMousedown: function(e)
16595     {
16596         e.stopPropagation();
16597         e.preventDefault();
16598     },
16599     
16600     keyup: function(e)
16601     {
16602         Roo.bootstrap.DateField.superclass.keyup.call(this);
16603         this.update();
16604     },
16605
16606     setValue: function(v)
16607     {
16608         
16609         // v can be a string or a date..
16610         
16611         
16612         var d = new Date(this.parseDate(v) ).clearTime();
16613         
16614         if(isNaN(d.getTime())){
16615             this.date = this.viewDate = '';
16616             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16617             return;
16618         }
16619         
16620         v = this.formatDate(d);
16621         
16622         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16623         
16624         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16625      
16626         this.update();
16627
16628         this.fireEvent('select', this, this.date);
16629         
16630     },
16631     
16632     getValue: function()
16633     {
16634         return this.formatDate(this.date);
16635     },
16636     
16637     fireKey: function(e)
16638     {
16639         if (!this.picker().isVisible()){
16640             if (e.keyCode == 27) // allow escape to hide and re-show picker
16641                 this.show();
16642             return;
16643         }
16644         
16645         var dateChanged = false,
16646         dir, day, month,
16647         newDate, newViewDate;
16648         
16649         switch(e.keyCode){
16650             case 27: // escape
16651                 this.hide();
16652                 e.preventDefault();
16653                 break;
16654             case 37: // left
16655             case 39: // right
16656                 if (!this.keyboardNavigation) break;
16657                 dir = e.keyCode == 37 ? -1 : 1;
16658                 
16659                 if (e.ctrlKey){
16660                     newDate = this.moveYear(this.date, dir);
16661                     newViewDate = this.moveYear(this.viewDate, dir);
16662                 } else if (e.shiftKey){
16663                     newDate = this.moveMonth(this.date, dir);
16664                     newViewDate = this.moveMonth(this.viewDate, dir);
16665                 } else {
16666                     newDate = new Date(this.date);
16667                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16668                     newViewDate = new Date(this.viewDate);
16669                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16670                 }
16671                 if (this.dateWithinRange(newDate)){
16672                     this.date = newDate;
16673                     this.viewDate = newViewDate;
16674                     this.setValue(this.formatDate(this.date));
16675 //                    this.update();
16676                     e.preventDefault();
16677                     dateChanged = true;
16678                 }
16679                 break;
16680             case 38: // up
16681             case 40: // down
16682                 if (!this.keyboardNavigation) break;
16683                 dir = e.keyCode == 38 ? -1 : 1;
16684                 if (e.ctrlKey){
16685                     newDate = this.moveYear(this.date, dir);
16686                     newViewDate = this.moveYear(this.viewDate, dir);
16687                 } else if (e.shiftKey){
16688                     newDate = this.moveMonth(this.date, dir);
16689                     newViewDate = this.moveMonth(this.viewDate, dir);
16690                 } else {
16691                     newDate = new Date(this.date);
16692                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16693                     newViewDate = new Date(this.viewDate);
16694                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16695                 }
16696                 if (this.dateWithinRange(newDate)){
16697                     this.date = newDate;
16698                     this.viewDate = newViewDate;
16699                     this.setValue(this.formatDate(this.date));
16700 //                    this.update();
16701                     e.preventDefault();
16702                     dateChanged = true;
16703                 }
16704                 break;
16705             case 13: // enter
16706                 this.setValue(this.formatDate(this.date));
16707                 this.hide();
16708                 e.preventDefault();
16709                 break;
16710             case 9: // tab
16711                 this.setValue(this.formatDate(this.date));
16712                 this.hide();
16713                 break;
16714             case 16: // shift
16715             case 17: // ctrl
16716             case 18: // alt
16717                 break;
16718             default :
16719                 this.hide();
16720                 
16721         }
16722     },
16723     
16724     
16725     onClick: function(e) 
16726     {
16727         e.stopPropagation();
16728         e.preventDefault();
16729         
16730         var target = e.getTarget();
16731         
16732         if(target.nodeName.toLowerCase() === 'i'){
16733             target = Roo.get(target).dom.parentNode;
16734         }
16735         
16736         var nodeName = target.nodeName;
16737         var className = target.className;
16738         var html = target.innerHTML;
16739         //Roo.log(nodeName);
16740         
16741         switch(nodeName.toLowerCase()) {
16742             case 'th':
16743                 switch(className) {
16744                     case 'switch':
16745                         this.showMode(1);
16746                         break;
16747                     case 'prev':
16748                     case 'next':
16749                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16750                         switch(this.viewMode){
16751                                 case 0:
16752                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16753                                         break;
16754                                 case 1:
16755                                 case 2:
16756                                         this.viewDate = this.moveYear(this.viewDate, dir);
16757                                         break;
16758                         }
16759                         this.fill();
16760                         break;
16761                     case 'today':
16762                         var date = new Date();
16763                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16764 //                        this.fill()
16765                         this.setValue(this.formatDate(this.date));
16766                         
16767                         this.hide();
16768                         break;
16769                 }
16770                 break;
16771             case 'span':
16772                 if (className.indexOf('disabled') < 0) {
16773                     this.viewDate.setUTCDate(1);
16774                     if (className.indexOf('month') > -1) {
16775                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16776                     } else {
16777                         var year = parseInt(html, 10) || 0;
16778                         this.viewDate.setUTCFullYear(year);
16779                         
16780                     }
16781                     
16782                     if(this.singleMode){
16783                         this.setValue(this.formatDate(this.viewDate));
16784                         this.hide();
16785                         return;
16786                     }
16787                     
16788                     this.showMode(-1);
16789                     this.fill();
16790                 }
16791                 break;
16792                 
16793             case 'td':
16794                 //Roo.log(className);
16795                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16796                     var day = parseInt(html, 10) || 1;
16797                     var year = this.viewDate.getUTCFullYear(),
16798                         month = this.viewDate.getUTCMonth();
16799
16800                     if (className.indexOf('old') > -1) {
16801                         if(month === 0 ){
16802                             month = 11;
16803                             year -= 1;
16804                         }else{
16805                             month -= 1;
16806                         }
16807                     } else if (className.indexOf('new') > -1) {
16808                         if (month == 11) {
16809                             month = 0;
16810                             year += 1;
16811                         } else {
16812                             month += 1;
16813                         }
16814                     }
16815                     //Roo.log([year,month,day]);
16816                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16817                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16818 //                    this.fill();
16819                     //Roo.log(this.formatDate(this.date));
16820                     this.setValue(this.formatDate(this.date));
16821                     this.hide();
16822                 }
16823                 break;
16824         }
16825     },
16826     
16827     setStartDate: function(startDate)
16828     {
16829         this.startDate = startDate || -Infinity;
16830         if (this.startDate !== -Infinity) {
16831             this.startDate = this.parseDate(this.startDate);
16832         }
16833         this.update();
16834         this.updateNavArrows();
16835     },
16836
16837     setEndDate: function(endDate)
16838     {
16839         this.endDate = endDate || Infinity;
16840         if (this.endDate !== Infinity) {
16841             this.endDate = this.parseDate(this.endDate);
16842         }
16843         this.update();
16844         this.updateNavArrows();
16845     },
16846     
16847     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16848     {
16849         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16850         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16851             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16852         }
16853         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16854             return parseInt(d, 10);
16855         });
16856         this.update();
16857         this.updateNavArrows();
16858     },
16859     
16860     updateNavArrows: function() 
16861     {
16862         if(this.singleMode){
16863             return;
16864         }
16865         
16866         var d = new Date(this.viewDate),
16867         year = d.getUTCFullYear(),
16868         month = d.getUTCMonth();
16869         
16870         Roo.each(this.picker().select('.prev', true).elements, function(v){
16871             v.show();
16872             switch (this.viewMode) {
16873                 case 0:
16874
16875                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16876                         v.hide();
16877                     }
16878                     break;
16879                 case 1:
16880                 case 2:
16881                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16882                         v.hide();
16883                     }
16884                     break;
16885             }
16886         });
16887         
16888         Roo.each(this.picker().select('.next', true).elements, function(v){
16889             v.show();
16890             switch (this.viewMode) {
16891                 case 0:
16892
16893                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16894                         v.hide();
16895                     }
16896                     break;
16897                 case 1:
16898                 case 2:
16899                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16900                         v.hide();
16901                     }
16902                     break;
16903             }
16904         })
16905     },
16906     
16907     moveMonth: function(date, dir)
16908     {
16909         if (!dir) return date;
16910         var new_date = new Date(date.valueOf()),
16911         day = new_date.getUTCDate(),
16912         month = new_date.getUTCMonth(),
16913         mag = Math.abs(dir),
16914         new_month, test;
16915         dir = dir > 0 ? 1 : -1;
16916         if (mag == 1){
16917             test = dir == -1
16918             // If going back one month, make sure month is not current month
16919             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16920             ? function(){
16921                 return new_date.getUTCMonth() == month;
16922             }
16923             // If going forward one month, make sure month is as expected
16924             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16925             : function(){
16926                 return new_date.getUTCMonth() != new_month;
16927             };
16928             new_month = month + dir;
16929             new_date.setUTCMonth(new_month);
16930             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16931             if (new_month < 0 || new_month > 11)
16932                 new_month = (new_month + 12) % 12;
16933         } else {
16934             // For magnitudes >1, move one month at a time...
16935             for (var i=0; i<mag; i++)
16936                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16937                 new_date = this.moveMonth(new_date, dir);
16938             // ...then reset the day, keeping it in the new month
16939             new_month = new_date.getUTCMonth();
16940             new_date.setUTCDate(day);
16941             test = function(){
16942                 return new_month != new_date.getUTCMonth();
16943             };
16944         }
16945         // Common date-resetting loop -- if date is beyond end of month, make it
16946         // end of month
16947         while (test()){
16948             new_date.setUTCDate(--day);
16949             new_date.setUTCMonth(new_month);
16950         }
16951         return new_date;
16952     },
16953
16954     moveYear: function(date, dir)
16955     {
16956         return this.moveMonth(date, dir*12);
16957     },
16958
16959     dateWithinRange: function(date)
16960     {
16961         return date >= this.startDate && date <= this.endDate;
16962     },
16963
16964     
16965     remove: function() 
16966     {
16967         this.picker().remove();
16968     }
16969    
16970 });
16971
16972 Roo.apply(Roo.bootstrap.DateField,  {
16973     
16974     head : {
16975         tag: 'thead',
16976         cn: [
16977         {
16978             tag: 'tr',
16979             cn: [
16980             {
16981                 tag: 'th',
16982                 cls: 'prev',
16983                 html: '<i class="fa fa-arrow-left"/>'
16984             },
16985             {
16986                 tag: 'th',
16987                 cls: 'switch',
16988                 colspan: '5'
16989             },
16990             {
16991                 tag: 'th',
16992                 cls: 'next',
16993                 html: '<i class="fa fa-arrow-right"/>'
16994             }
16995
16996             ]
16997         }
16998         ]
16999     },
17000     
17001     content : {
17002         tag: 'tbody',
17003         cn: [
17004         {
17005             tag: 'tr',
17006             cn: [
17007             {
17008                 tag: 'td',
17009                 colspan: '7'
17010             }
17011             ]
17012         }
17013         ]
17014     },
17015     
17016     footer : {
17017         tag: 'tfoot',
17018         cn: [
17019         {
17020             tag: 'tr',
17021             cn: [
17022             {
17023                 tag: 'th',
17024                 colspan: '7',
17025                 cls: 'today'
17026             }
17027                     
17028             ]
17029         }
17030         ]
17031     },
17032     
17033     dates:{
17034         en: {
17035             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17036             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17037             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17038             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17039             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17040             today: "Today"
17041         }
17042     },
17043     
17044     modes: [
17045     {
17046         clsName: 'days',
17047         navFnc: 'Month',
17048         navStep: 1
17049     },
17050     {
17051         clsName: 'months',
17052         navFnc: 'FullYear',
17053         navStep: 1
17054     },
17055     {
17056         clsName: 'years',
17057         navFnc: 'FullYear',
17058         navStep: 10
17059     }]
17060 });
17061
17062 Roo.apply(Roo.bootstrap.DateField,  {
17063   
17064     template : {
17065         tag: 'div',
17066         cls: 'datepicker dropdown-menu roo-dynamic',
17067         cn: [
17068         {
17069             tag: 'div',
17070             cls: 'datepicker-days',
17071             cn: [
17072             {
17073                 tag: 'table',
17074                 cls: 'table-condensed',
17075                 cn:[
17076                 Roo.bootstrap.DateField.head,
17077                 {
17078                     tag: 'tbody'
17079                 },
17080                 Roo.bootstrap.DateField.footer
17081                 ]
17082             }
17083             ]
17084         },
17085         {
17086             tag: 'div',
17087             cls: 'datepicker-months',
17088             cn: [
17089             {
17090                 tag: 'table',
17091                 cls: 'table-condensed',
17092                 cn:[
17093                 Roo.bootstrap.DateField.head,
17094                 Roo.bootstrap.DateField.content,
17095                 Roo.bootstrap.DateField.footer
17096                 ]
17097             }
17098             ]
17099         },
17100         {
17101             tag: 'div',
17102             cls: 'datepicker-years',
17103             cn: [
17104             {
17105                 tag: 'table',
17106                 cls: 'table-condensed',
17107                 cn:[
17108                 Roo.bootstrap.DateField.head,
17109                 Roo.bootstrap.DateField.content,
17110                 Roo.bootstrap.DateField.footer
17111                 ]
17112             }
17113             ]
17114         }
17115         ]
17116     }
17117 });
17118
17119  
17120
17121  /*
17122  * - LGPL
17123  *
17124  * TimeField
17125  * 
17126  */
17127
17128 /**
17129  * @class Roo.bootstrap.TimeField
17130  * @extends Roo.bootstrap.Input
17131  * Bootstrap DateField class
17132  * 
17133  * 
17134  * @constructor
17135  * Create a new TimeField
17136  * @param {Object} config The config object
17137  */
17138
17139 Roo.bootstrap.TimeField = function(config){
17140     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17141     this.addEvents({
17142             /**
17143              * @event show
17144              * Fires when this field show.
17145              * @param {Roo.bootstrap.DateField} thisthis
17146              * @param {Mixed} date The date value
17147              */
17148             show : true,
17149             /**
17150              * @event show
17151              * Fires when this field hide.
17152              * @param {Roo.bootstrap.DateField} this
17153              * @param {Mixed} date The date value
17154              */
17155             hide : true,
17156             /**
17157              * @event select
17158              * Fires when select a date.
17159              * @param {Roo.bootstrap.DateField} this
17160              * @param {Mixed} date The date value
17161              */
17162             select : true
17163         });
17164 };
17165
17166 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17167     
17168     /**
17169      * @cfg {String} format
17170      * The default time format string which can be overriden for localization support.  The format must be
17171      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17172      */
17173     format : "H:i",
17174        
17175     onRender: function(ct, position)
17176     {
17177         
17178         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17179                 
17180         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17181         
17182         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17183         
17184         this.pop = this.picker().select('>.datepicker-time',true).first();
17185         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17186         
17187         this.picker().on('mousedown', this.onMousedown, this);
17188         this.picker().on('click', this.onClick, this);
17189         
17190         this.picker().addClass('datepicker-dropdown');
17191     
17192         this.fillTime();
17193         this.update();
17194             
17195         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17196         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17197         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17198         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17199         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17200         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17201
17202     },
17203     
17204     fireKey: function(e){
17205         if (!this.picker().isVisible()){
17206             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17207                 this.show();
17208             }
17209             return;
17210         }
17211
17212         e.preventDefault();
17213         
17214         switch(e.keyCode){
17215             case 27: // escape
17216                 this.hide();
17217                 break;
17218             case 37: // left
17219             case 39: // right
17220                 this.onTogglePeriod();
17221                 break;
17222             case 38: // up
17223                 this.onIncrementMinutes();
17224                 break;
17225             case 40: // down
17226                 this.onDecrementMinutes();
17227                 break;
17228             case 13: // enter
17229             case 9: // tab
17230                 this.setTime();
17231                 break;
17232         }
17233     },
17234     
17235     onClick: function(e) {
17236         e.stopPropagation();
17237         e.preventDefault();
17238     },
17239     
17240     picker : function()
17241     {
17242         return this.el.select('.datepicker', true).first();
17243     },
17244     
17245     fillTime: function()
17246     {    
17247         var time = this.pop.select('tbody', true).first();
17248         
17249         time.dom.innerHTML = '';
17250         
17251         time.createChild({
17252             tag: 'tr',
17253             cn: [
17254                 {
17255                     tag: 'td',
17256                     cn: [
17257                         {
17258                             tag: 'a',
17259                             href: '#',
17260                             cls: 'btn',
17261                             cn: [
17262                                 {
17263                                     tag: 'span',
17264                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17265                                 }
17266                             ]
17267                         } 
17268                     ]
17269                 },
17270                 {
17271                     tag: 'td',
17272                     cls: 'separator'
17273                 },
17274                 {
17275                     tag: 'td',
17276                     cn: [
17277                         {
17278                             tag: 'a',
17279                             href: '#',
17280                             cls: 'btn',
17281                             cn: [
17282                                 {
17283                                     tag: 'span',
17284                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17285                                 }
17286                             ]
17287                         }
17288                     ]
17289                 },
17290                 {
17291                     tag: 'td',
17292                     cls: 'separator'
17293                 }
17294             ]
17295         });
17296         
17297         time.createChild({
17298             tag: 'tr',
17299             cn: [
17300                 {
17301                     tag: 'td',
17302                     cn: [
17303                         {
17304                             tag: 'span',
17305                             cls: 'timepicker-hour',
17306                             html: '00'
17307                         }  
17308                     ]
17309                 },
17310                 {
17311                     tag: 'td',
17312                     cls: 'separator',
17313                     html: ':'
17314                 },
17315                 {
17316                     tag: 'td',
17317                     cn: [
17318                         {
17319                             tag: 'span',
17320                             cls: 'timepicker-minute',
17321                             html: '00'
17322                         }  
17323                     ]
17324                 },
17325                 {
17326                     tag: 'td',
17327                     cls: 'separator'
17328                 },
17329                 {
17330                     tag: 'td',
17331                     cn: [
17332                         {
17333                             tag: 'button',
17334                             type: 'button',
17335                             cls: 'btn btn-primary period',
17336                             html: 'AM'
17337                             
17338                         }
17339                     ]
17340                 }
17341             ]
17342         });
17343         
17344         time.createChild({
17345             tag: 'tr',
17346             cn: [
17347                 {
17348                     tag: 'td',
17349                     cn: [
17350                         {
17351                             tag: 'a',
17352                             href: '#',
17353                             cls: 'btn',
17354                             cn: [
17355                                 {
17356                                     tag: 'span',
17357                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17358                                 }
17359                             ]
17360                         }
17361                     ]
17362                 },
17363                 {
17364                     tag: 'td',
17365                     cls: 'separator'
17366                 },
17367                 {
17368                     tag: 'td',
17369                     cn: [
17370                         {
17371                             tag: 'a',
17372                             href: '#',
17373                             cls: 'btn',
17374                             cn: [
17375                                 {
17376                                     tag: 'span',
17377                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17378                                 }
17379                             ]
17380                         }
17381                     ]
17382                 },
17383                 {
17384                     tag: 'td',
17385                     cls: 'separator'
17386                 }
17387             ]
17388         });
17389         
17390     },
17391     
17392     update: function()
17393     {
17394         
17395         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17396         
17397         this.fill();
17398     },
17399     
17400     fill: function() 
17401     {
17402         var hours = this.time.getHours();
17403         var minutes = this.time.getMinutes();
17404         var period = 'AM';
17405         
17406         if(hours > 11){
17407             period = 'PM';
17408         }
17409         
17410         if(hours == 0){
17411             hours = 12;
17412         }
17413         
17414         
17415         if(hours > 12){
17416             hours = hours - 12;
17417         }
17418         
17419         if(hours < 10){
17420             hours = '0' + hours;
17421         }
17422         
17423         if(minutes < 10){
17424             minutes = '0' + minutes;
17425         }
17426         
17427         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17428         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17429         this.pop.select('button', true).first().dom.innerHTML = period;
17430         
17431     },
17432     
17433     place: function()
17434     {   
17435         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17436         
17437         var cls = ['bottom'];
17438         
17439         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17440             cls.pop();
17441             cls.push('top');
17442         }
17443         
17444         cls.push('right');
17445         
17446         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17447             cls.pop();
17448             cls.push('left');
17449         }
17450         
17451         this.picker().addClass(cls.join('-'));
17452         
17453         var _this = this;
17454         
17455         Roo.each(cls, function(c){
17456             if(c == 'bottom'){
17457                 _this.picker().setTop(_this.inputEl().getHeight());
17458                 return;
17459             }
17460             if(c == 'top'){
17461                 _this.picker().setTop(0 - _this.picker().getHeight());
17462                 return;
17463             }
17464             
17465             if(c == 'left'){
17466                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17467                 return;
17468             }
17469             if(c == 'right'){
17470                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17471                 return;
17472             }
17473         });
17474         
17475     },
17476   
17477     onFocus : function()
17478     {
17479         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17480         this.show();
17481     },
17482     
17483     onBlur : function()
17484     {
17485         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17486         this.hide();
17487     },
17488     
17489     show : function()
17490     {
17491         this.picker().show();
17492         this.pop.show();
17493         this.update();
17494         this.place();
17495         
17496         this.fireEvent('show', this, this.date);
17497     },
17498     
17499     hide : function()
17500     {
17501         this.picker().hide();
17502         this.pop.hide();
17503         
17504         this.fireEvent('hide', this, this.date);
17505     },
17506     
17507     setTime : function()
17508     {
17509         this.hide();
17510         this.setValue(this.time.format(this.format));
17511         
17512         this.fireEvent('select', this, this.date);
17513         
17514         
17515     },
17516     
17517     onMousedown: function(e){
17518         e.stopPropagation();
17519         e.preventDefault();
17520     },
17521     
17522     onIncrementHours: function()
17523     {
17524         Roo.log('onIncrementHours');
17525         this.time = this.time.add(Date.HOUR, 1);
17526         this.update();
17527         
17528     },
17529     
17530     onDecrementHours: function()
17531     {
17532         Roo.log('onDecrementHours');
17533         this.time = this.time.add(Date.HOUR, -1);
17534         this.update();
17535     },
17536     
17537     onIncrementMinutes: function()
17538     {
17539         Roo.log('onIncrementMinutes');
17540         this.time = this.time.add(Date.MINUTE, 1);
17541         this.update();
17542     },
17543     
17544     onDecrementMinutes: function()
17545     {
17546         Roo.log('onDecrementMinutes');
17547         this.time = this.time.add(Date.MINUTE, -1);
17548         this.update();
17549     },
17550     
17551     onTogglePeriod: function()
17552     {
17553         Roo.log('onTogglePeriod');
17554         this.time = this.time.add(Date.HOUR, 12);
17555         this.update();
17556     }
17557     
17558    
17559 });
17560
17561 Roo.apply(Roo.bootstrap.TimeField,  {
17562     
17563     content : {
17564         tag: 'tbody',
17565         cn: [
17566             {
17567                 tag: 'tr',
17568                 cn: [
17569                 {
17570                     tag: 'td',
17571                     colspan: '7'
17572                 }
17573                 ]
17574             }
17575         ]
17576     },
17577     
17578     footer : {
17579         tag: 'tfoot',
17580         cn: [
17581             {
17582                 tag: 'tr',
17583                 cn: [
17584                 {
17585                     tag: 'th',
17586                     colspan: '7',
17587                     cls: '',
17588                     cn: [
17589                         {
17590                             tag: 'button',
17591                             cls: 'btn btn-info ok',
17592                             html: 'OK'
17593                         }
17594                     ]
17595                 }
17596
17597                 ]
17598             }
17599         ]
17600     }
17601 });
17602
17603 Roo.apply(Roo.bootstrap.TimeField,  {
17604   
17605     template : {
17606         tag: 'div',
17607         cls: 'datepicker dropdown-menu',
17608         cn: [
17609             {
17610                 tag: 'div',
17611                 cls: 'datepicker-time',
17612                 cn: [
17613                 {
17614                     tag: 'table',
17615                     cls: 'table-condensed',
17616                     cn:[
17617                     Roo.bootstrap.TimeField.content,
17618                     Roo.bootstrap.TimeField.footer
17619                     ]
17620                 }
17621                 ]
17622             }
17623         ]
17624     }
17625 });
17626
17627  
17628
17629  /*
17630  * - LGPL
17631  *
17632  * MonthField
17633  * 
17634  */
17635
17636 /**
17637  * @class Roo.bootstrap.MonthField
17638  * @extends Roo.bootstrap.Input
17639  * Bootstrap MonthField class
17640  * 
17641  * @cfg {String} language default en
17642  * 
17643  * @constructor
17644  * Create a new MonthField
17645  * @param {Object} config The config object
17646  */
17647
17648 Roo.bootstrap.MonthField = function(config){
17649     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17650     
17651     this.addEvents({
17652         /**
17653          * @event show
17654          * Fires when this field show.
17655          * @param {Roo.bootstrap.MonthField} this
17656          * @param {Mixed} date The date value
17657          */
17658         show : true,
17659         /**
17660          * @event show
17661          * Fires when this field hide.
17662          * @param {Roo.bootstrap.MonthField} this
17663          * @param {Mixed} date The date value
17664          */
17665         hide : true,
17666         /**
17667          * @event select
17668          * Fires when select a date.
17669          * @param {Roo.bootstrap.MonthField} this
17670          * @param {String} oldvalue The old value
17671          * @param {String} newvalue The new value
17672          */
17673         select : true
17674     });
17675 };
17676
17677 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17678     
17679     onRender: function(ct, position)
17680     {
17681         
17682         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17683         
17684         this.language = this.language || 'en';
17685         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17686         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17687         
17688         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17689         this.isInline = false;
17690         this.isInput = true;
17691         this.component = this.el.select('.add-on', true).first() || false;
17692         this.component = (this.component && this.component.length === 0) ? false : this.component;
17693         this.hasInput = this.component && this.inputEL().length;
17694         
17695         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17696         
17697         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17698         
17699         this.picker().on('mousedown', this.onMousedown, this);
17700         this.picker().on('click', this.onClick, this);
17701         
17702         this.picker().addClass('datepicker-dropdown');
17703         
17704         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17705             v.setStyle('width', '189px');
17706         });
17707         
17708         this.fillMonths();
17709         
17710         this.update();
17711         
17712         if(this.isInline) {
17713             this.show();
17714         }
17715         
17716     },
17717     
17718     setValue: function(v, suppressEvent)
17719     {   
17720         var o = this.getValue();
17721         
17722         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17723         
17724         this.update();
17725
17726         if(suppressEvent !== true){
17727             this.fireEvent('select', this, o, v);
17728         }
17729         
17730     },
17731     
17732     getValue: function()
17733     {
17734         return this.value;
17735     },
17736     
17737     onClick: function(e) 
17738     {
17739         e.stopPropagation();
17740         e.preventDefault();
17741         
17742         var target = e.getTarget();
17743         
17744         if(target.nodeName.toLowerCase() === 'i'){
17745             target = Roo.get(target).dom.parentNode;
17746         }
17747         
17748         var nodeName = target.nodeName;
17749         var className = target.className;
17750         var html = target.innerHTML;
17751         
17752         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17753             return;
17754         }
17755         
17756         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17757         
17758         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17759         
17760         this.hide();
17761                         
17762     },
17763     
17764     picker : function()
17765     {
17766         return this.pickerEl;
17767     },
17768     
17769     fillMonths: function()
17770     {    
17771         var i = 0;
17772         var months = this.picker().select('>.datepicker-months td', true).first();
17773         
17774         months.dom.innerHTML = '';
17775         
17776         while (i < 12) {
17777             var month = {
17778                 tag: 'span',
17779                 cls: 'month',
17780                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17781             }
17782             
17783             months.createChild(month);
17784         }
17785         
17786     },
17787     
17788     update: function()
17789     {
17790         var _this = this;
17791         
17792         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17793             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17794         }
17795         
17796         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17797             e.removeClass('active');
17798             
17799             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17800                 e.addClass('active');
17801             }
17802         })
17803     },
17804     
17805     place: function()
17806     {
17807         if(this.isInline) return;
17808         
17809         this.picker().removeClass(['bottom', 'top']);
17810         
17811         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17812             /*
17813              * place to the top of element!
17814              *
17815              */
17816             
17817             this.picker().addClass('top');
17818             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17819             
17820             return;
17821         }
17822         
17823         this.picker().addClass('bottom');
17824         
17825         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17826     },
17827     
17828     onFocus : function()
17829     {
17830         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17831         this.show();
17832     },
17833     
17834     onBlur : function()
17835     {
17836         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17837         
17838         var d = this.inputEl().getValue();
17839         
17840         this.setValue(d);
17841                 
17842         this.hide();
17843     },
17844     
17845     show : function()
17846     {
17847         this.picker().show();
17848         this.picker().select('>.datepicker-months', true).first().show();
17849         this.update();
17850         this.place();
17851         
17852         this.fireEvent('show', this, this.date);
17853     },
17854     
17855     hide : function()
17856     {
17857         if(this.isInline) return;
17858         this.picker().hide();
17859         this.fireEvent('hide', this, this.date);
17860         
17861     },
17862     
17863     onMousedown: function(e)
17864     {
17865         e.stopPropagation();
17866         e.preventDefault();
17867     },
17868     
17869     keyup: function(e)
17870     {
17871         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17872         this.update();
17873     },
17874
17875     fireKey: function(e)
17876     {
17877         if (!this.picker().isVisible()){
17878             if (e.keyCode == 27) // allow escape to hide and re-show picker
17879                 this.show();
17880             return;
17881         }
17882         
17883         var dir;
17884         
17885         switch(e.keyCode){
17886             case 27: // escape
17887                 this.hide();
17888                 e.preventDefault();
17889                 break;
17890             case 37: // left
17891             case 39: // right
17892                 dir = e.keyCode == 37 ? -1 : 1;
17893                 
17894                 this.vIndex = this.vIndex + dir;
17895                 
17896                 if(this.vIndex < 0){
17897                     this.vIndex = 0;
17898                 }
17899                 
17900                 if(this.vIndex > 11){
17901                     this.vIndex = 11;
17902                 }
17903                 
17904                 if(isNaN(this.vIndex)){
17905                     this.vIndex = 0;
17906                 }
17907                 
17908                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17909                 
17910                 break;
17911             case 38: // up
17912             case 40: // down
17913                 
17914                 dir = e.keyCode == 38 ? -1 : 1;
17915                 
17916                 this.vIndex = this.vIndex + dir * 4;
17917                 
17918                 if(this.vIndex < 0){
17919                     this.vIndex = 0;
17920                 }
17921                 
17922                 if(this.vIndex > 11){
17923                     this.vIndex = 11;
17924                 }
17925                 
17926                 if(isNaN(this.vIndex)){
17927                     this.vIndex = 0;
17928                 }
17929                 
17930                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17931                 break;
17932                 
17933             case 13: // enter
17934                 
17935                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17936                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17937                 }
17938                 
17939                 this.hide();
17940                 e.preventDefault();
17941                 break;
17942             case 9: // tab
17943                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17944                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17945                 }
17946                 this.hide();
17947                 break;
17948             case 16: // shift
17949             case 17: // ctrl
17950             case 18: // alt
17951                 break;
17952             default :
17953                 this.hide();
17954                 
17955         }
17956     },
17957     
17958     remove: function() 
17959     {
17960         this.picker().remove();
17961     }
17962    
17963 });
17964
17965 Roo.apply(Roo.bootstrap.MonthField,  {
17966     
17967     content : {
17968         tag: 'tbody',
17969         cn: [
17970         {
17971             tag: 'tr',
17972             cn: [
17973             {
17974                 tag: 'td',
17975                 colspan: '7'
17976             }
17977             ]
17978         }
17979         ]
17980     },
17981     
17982     dates:{
17983         en: {
17984             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17985             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17986         }
17987     }
17988 });
17989
17990 Roo.apply(Roo.bootstrap.MonthField,  {
17991   
17992     template : {
17993         tag: 'div',
17994         cls: 'datepicker dropdown-menu roo-dynamic',
17995         cn: [
17996             {
17997                 tag: 'div',
17998                 cls: 'datepicker-months',
17999                 cn: [
18000                 {
18001                     tag: 'table',
18002                     cls: 'table-condensed',
18003                     cn:[
18004                         Roo.bootstrap.DateField.content
18005                     ]
18006                 }
18007                 ]
18008             }
18009         ]
18010     }
18011 });
18012
18013  
18014
18015  
18016  /*
18017  * - LGPL
18018  *
18019  * CheckBox
18020  * 
18021  */
18022
18023 /**
18024  * @class Roo.bootstrap.CheckBox
18025  * @extends Roo.bootstrap.Input
18026  * Bootstrap CheckBox class
18027  * 
18028  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18029  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18030  * @cfg {String} boxLabel The text that appears beside the checkbox
18031  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18032  * @cfg {Boolean} checked initnal the element
18033  * @cfg {Boolean} inline inline the element (default false)
18034  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18035  * 
18036  * @constructor
18037  * Create a new CheckBox
18038  * @param {Object} config The config object
18039  */
18040
18041 Roo.bootstrap.CheckBox = function(config){
18042     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18043    
18044     this.addEvents({
18045         /**
18046         * @event check
18047         * Fires when the element is checked or unchecked.
18048         * @param {Roo.bootstrap.CheckBox} this This input
18049         * @param {Boolean} checked The new checked value
18050         */
18051        check : true
18052     });
18053     
18054 };
18055
18056 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18057   
18058     inputType: 'checkbox',
18059     inputValue: 1,
18060     valueOff: 0,
18061     boxLabel: false,
18062     checked: false,
18063     weight : false,
18064     inline: false,
18065     
18066     getAutoCreate : function()
18067     {
18068         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18069         
18070         var id = Roo.id();
18071         
18072         var cfg = {};
18073         
18074         cfg.cls = 'form-group ' + this.inputType; //input-group
18075         
18076         if(this.inline){
18077             cfg.cls += ' ' + this.inputType + '-inline';
18078         }
18079         
18080         var input =  {
18081             tag: 'input',
18082             id : id,
18083             type : this.inputType,
18084             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18085             cls : 'roo-' + this.inputType, //'form-box',
18086             placeholder : this.placeholder || ''
18087             
18088         };
18089         
18090         if (this.weight) { // Validity check?
18091             cfg.cls += " " + this.inputType + "-" + this.weight;
18092         }
18093         
18094         if (this.disabled) {
18095             input.disabled=true;
18096         }
18097         
18098         if(this.checked){
18099             input.checked = this.checked;
18100         }
18101         
18102         if (this.name) {
18103             input.name = this.name;
18104         }
18105         
18106         if (this.size) {
18107             input.cls += ' input-' + this.size;
18108         }
18109         
18110         var settings=this;
18111         
18112         ['xs','sm','md','lg'].map(function(size){
18113             if (settings[size]) {
18114                 cfg.cls += ' col-' + size + '-' + settings[size];
18115             }
18116         });
18117         
18118         var inputblock = input;
18119          
18120         if (this.before || this.after) {
18121             
18122             inputblock = {
18123                 cls : 'input-group',
18124                 cn :  [] 
18125             };
18126             
18127             if (this.before) {
18128                 inputblock.cn.push({
18129                     tag :'span',
18130                     cls : 'input-group-addon',
18131                     html : this.before
18132                 });
18133             }
18134             
18135             inputblock.cn.push(input);
18136             
18137             if (this.after) {
18138                 inputblock.cn.push({
18139                     tag :'span',
18140                     cls : 'input-group-addon',
18141                     html : this.after
18142                 });
18143             }
18144             
18145         }
18146         
18147         if (align ==='left' && this.fieldLabel.length) {
18148                 Roo.log("left and has label");
18149                 cfg.cn = [
18150                     
18151                     {
18152                         tag: 'label',
18153                         'for' :  id,
18154                         cls : 'control-label col-md-' + this.labelWidth,
18155                         html : this.fieldLabel
18156                         
18157                     },
18158                     {
18159                         cls : "col-md-" + (12 - this.labelWidth), 
18160                         cn: [
18161                             inputblock
18162                         ]
18163                     }
18164                     
18165                 ];
18166         } else if ( this.fieldLabel.length) {
18167                 Roo.log(" label");
18168                 cfg.cn = [
18169                    
18170                     {
18171                         tag: this.boxLabel ? 'span' : 'label',
18172                         'for': id,
18173                         cls: 'control-label box-input-label',
18174                         //cls : 'input-group-addon',
18175                         html : this.fieldLabel
18176                         
18177                     },
18178                     
18179                     inputblock
18180                     
18181                 ];
18182
18183         } else {
18184             
18185                 Roo.log(" no label && no align");
18186                 cfg.cn = [  inputblock ] ;
18187                 
18188                 
18189         }
18190         if(this.boxLabel){
18191              var boxLabelCfg = {
18192                 tag: 'label',
18193                 //'for': id, // box label is handled by onclick - so no for...
18194                 cls: 'box-label',
18195                 html: this.boxLabel
18196             }
18197             
18198             if(this.tooltip){
18199                 boxLabelCfg.tooltip = this.tooltip;
18200             }
18201              
18202             cfg.cn.push(boxLabelCfg);
18203         }
18204         
18205         
18206        
18207         return cfg;
18208         
18209     },
18210     
18211     /**
18212      * return the real input element.
18213      */
18214     inputEl: function ()
18215     {
18216         return this.el.select('input.roo-' + this.inputType,true).first();
18217     },
18218     
18219     labelEl: function()
18220     {
18221         return this.el.select('label.control-label',true).first();
18222     },
18223     /* depricated... */
18224     
18225     label: function()
18226     {
18227         return this.labelEl();
18228     },
18229     
18230     initEvents : function()
18231     {
18232 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18233         
18234         this.inputEl().on('click', this.onClick,  this);
18235         
18236         if (this.boxLabel) { 
18237             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18238         }
18239         
18240         this.startValue = this.getValue();
18241         
18242         if(this.groupId){
18243             Roo.bootstrap.CheckBox.register(this);
18244         }
18245     },
18246     
18247     onClick : function()
18248     {   
18249         this.setChecked(!this.checked);
18250     },
18251     
18252     setChecked : function(state,suppressEvent)
18253     {
18254         this.startValue = this.getValue();
18255         
18256         if(this.inputType == 'radio'){
18257             
18258             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18259                 e.dom.checked = false;
18260             });
18261             
18262             this.inputEl().dom.checked = true;
18263             
18264             this.inputEl().dom.value = this.inputValue;
18265             
18266             if(suppressEvent !== true){
18267                 this.fireEvent('check', this, true);
18268             }
18269             
18270             this.validate();
18271             
18272             return;
18273         }
18274         
18275         this.checked = state;
18276         
18277         this.inputEl().dom.checked = state;
18278         
18279         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18280         
18281         if(suppressEvent !== true){
18282             this.fireEvent('check', this, state);
18283         }
18284         
18285         this.validate();
18286     },
18287     
18288     getValue : function()
18289     {
18290         if(this.inputType == 'radio'){
18291             return this.getGroupValue();
18292         }
18293         
18294         return this.inputEl().getValue();
18295         
18296     },
18297     
18298     getGroupValue : function()
18299     {
18300         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18301             return '';
18302         }
18303         
18304         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18305     },
18306     
18307     setValue : function(v,suppressEvent)
18308     {
18309         if(this.inputType == 'radio'){
18310             this.setGroupValue(v, suppressEvent);
18311             return;
18312         }
18313         
18314         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18315         
18316         this.validate();
18317     },
18318     
18319     setGroupValue : function(v, suppressEvent)
18320     {
18321         this.startValue = this.getValue();
18322         
18323         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18324             e.dom.checked = false;
18325             
18326             if(e.dom.value == v){
18327                 e.dom.checked = true;
18328             }
18329         });
18330         
18331         if(suppressEvent !== true){
18332             this.fireEvent('check', this, true);
18333         }
18334
18335         this.validate();
18336         
18337         return;
18338     },
18339     
18340     validate : function()
18341     {
18342         if(
18343                 this.disabled || 
18344                 (this.inputType == 'radio' && this.validateRadio()) ||
18345                 (this.inputType == 'checkbox' && this.validateCheckbox())
18346         ){
18347             this.markValid();
18348             return true;
18349         }
18350         
18351         this.markInvalid();
18352         return false;
18353     },
18354     
18355     validateRadio : function()
18356     {
18357         var valid = false;
18358         
18359         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18360             if(!e.dom.checked){
18361                 return;
18362             }
18363             
18364             valid = true;
18365             
18366             return false;
18367         });
18368         
18369         return valid;
18370     },
18371     
18372     validateCheckbox : function()
18373     {
18374         if(!this.groupId){
18375             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18376         }
18377         
18378         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18379         
18380         if(!group){
18381             return false;
18382         }
18383         
18384         var r = false;
18385         
18386         for(var i in group){
18387             if(r){
18388                 break;
18389             }
18390             
18391             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18392         }
18393         
18394         return r;
18395     },
18396     
18397     /**
18398      * Mark this field as valid
18399      */
18400     markValid : function()
18401     {
18402         if(this.allowBlank){
18403             return;
18404         }
18405         
18406         var _this = this;
18407         
18408         this.fireEvent('valid', this);
18409         
18410         if(this.inputType == 'radio'){
18411             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18412                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18413                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18414             });
18415             
18416             return;
18417         }
18418         
18419         if(!this.groupId){
18420             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18421             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18422             return;
18423         }
18424         
18425         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18426             
18427         if(!group){
18428             return;
18429         }
18430         
18431         for(var i in group){
18432             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18433             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18434         }
18435     },
18436     
18437      /**
18438      * Mark this field as invalid
18439      * @param {String} msg The validation message
18440      */
18441     markInvalid : function(msg)
18442     {
18443         if(this.allowBlank){
18444             return;
18445         }
18446         
18447         var _this = this;
18448         
18449         this.fireEvent('invalid', this, msg);
18450         
18451         if(this.inputType == 'radio'){
18452             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18453                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18454                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18455             });
18456             
18457             return;
18458         }
18459         
18460         if(!this.groupId){
18461             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18462             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18463             return;
18464         }
18465         
18466         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18467             
18468         if(!group){
18469             return;
18470         }
18471         
18472         for(var i in group){
18473             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18474             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18475         }
18476         
18477     }
18478     
18479 });
18480
18481 Roo.apply(Roo.bootstrap.CheckBox, {
18482     
18483     groups: {},
18484     
18485      /**
18486     * register a CheckBox Group
18487     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18488     */
18489     register : function(checkbox)
18490     {
18491         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18492             this.groups[checkbox.groupId] = {};
18493         }
18494         
18495         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18496             return;
18497         }
18498         
18499         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18500         
18501     },
18502     /**
18503     * fetch a CheckBox Group based on the group ID
18504     * @param {string} the group ID
18505     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18506     */
18507     get: function(groupId) {
18508         if (typeof(this.groups[groupId]) == 'undefined') {
18509             return false;
18510         }
18511         
18512         return this.groups[groupId] ;
18513     }
18514     
18515     
18516 });
18517 /*
18518  * - LGPL
18519  *
18520  * Radio
18521  *
18522  *
18523  * not inline
18524  *<div class="radio">
18525   <label>
18526     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18527     Option one is this and that&mdash;be sure to include why it's great
18528   </label>
18529 </div>
18530  *
18531  *
18532  *inline
18533  *<span>
18534  *<label class="radio-inline">fieldLabel</label>
18535  *<label class="radio-inline">
18536   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18537 </label>
18538 <span>
18539  * 
18540  * 
18541  */
18542
18543 /**
18544  * @class Roo.bootstrap.Radio
18545  * @extends Roo.bootstrap.CheckBox
18546  * Bootstrap Radio class
18547
18548  * @constructor
18549  * Create a new Radio
18550  * @param {Object} config The config object
18551  */
18552
18553 Roo.bootstrap.Radio = function(config){
18554     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18555    
18556 };
18557
18558 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18559     
18560     inputType: 'radio',
18561     inputValue: '',
18562     valueOff: '',
18563     
18564     getAutoCreate : function()
18565     {
18566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18567         align = align || 'left'; // default...
18568         
18569         
18570         
18571         var id = Roo.id();
18572         
18573         var cfg = {
18574                 tag : this.inline ? 'span' : 'div',
18575                 cls : '',
18576                 cn : []
18577         };
18578         
18579         var inline = this.inline ? ' radio-inline' : '';
18580         
18581         var lbl = {
18582                 tag: 'label' ,
18583                 // does not need for, as we wrap the input with it..
18584                 'for' : id,
18585                 cls : 'control-label box-label' + inline,
18586                 cn : []
18587         };
18588         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18589         
18590         var fieldLabel = {
18591             tag: 'label' ,
18592             //cls : 'control-label' + inline,
18593             html : this.fieldLabel,
18594             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18595         };
18596         
18597  
18598         
18599         
18600         var input =  {
18601             tag: 'input',
18602             id : id,
18603             type : this.inputType,
18604             //value : (!this.checked) ? this.valueOff : this.inputValue,
18605             value : this.inputValue,
18606             cls : 'roo-radio',
18607             placeholder : this.placeholder || '' // ?? needed????
18608             
18609         };
18610         if (this.weight) { // Validity check?
18611             input.cls += " radio-" + this.weight;
18612         }
18613         if (this.disabled) {
18614             input.disabled=true;
18615         }
18616         
18617         if(this.checked){
18618             input.checked = this.checked;
18619         }
18620         
18621         if (this.name) {
18622             input.name = this.name;
18623         }
18624         
18625         if (this.size) {
18626             input.cls += ' input-' + this.size;
18627         }
18628         
18629         //?? can span's inline have a width??
18630         
18631         var settings=this;
18632         ['xs','sm','md','lg'].map(function(size){
18633             if (settings[size]) {
18634                 cfg.cls += ' col-' + size + '-' + settings[size];
18635             }
18636         });
18637         
18638         var inputblock = input;
18639         
18640         if (this.before || this.after) {
18641             
18642             inputblock = {
18643                 cls : 'input-group',
18644                 tag : 'span',
18645                 cn :  [] 
18646             };
18647             if (this.before) {
18648                 inputblock.cn.push({
18649                     tag :'span',
18650                     cls : 'input-group-addon',
18651                     html : this.before
18652                 });
18653             }
18654             inputblock.cn.push(input);
18655             if (this.after) {
18656                 inputblock.cn.push({
18657                     tag :'span',
18658                     cls : 'input-group-addon',
18659                     html : this.after
18660                 });
18661             }
18662             
18663         };
18664         
18665         
18666         if (this.fieldLabel && this.fieldLabel.length) {
18667             cfg.cn.push(fieldLabel);
18668         }
18669        
18670         // normal bootstrap puts the input inside the label.
18671         // however with our styled version - it has to go after the input.
18672        
18673         //lbl.cn.push(inputblock);
18674         
18675         var lblwrap =  {
18676             tag: 'span',
18677             cls: 'radio' + inline,
18678             cn: [
18679                 inputblock,
18680                 lbl
18681             ]
18682         };
18683         
18684         cfg.cn.push( lblwrap);
18685         
18686         if(this.boxLabel){
18687             lbl.cn.push({
18688                 tag: 'span',
18689                 html: this.boxLabel
18690             })
18691         }
18692          
18693         
18694         return cfg;
18695         
18696     },
18697     
18698     initEvents : function()
18699     {
18700 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18701         
18702         this.inputEl().on('click', this.onClick,  this);
18703         if (this.boxLabel) {
18704             Roo.log('find label')
18705             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18706         }
18707         
18708     },
18709     
18710     inputEl: function ()
18711     {
18712         return this.el.select('input.roo-radio',true).first();
18713     },
18714     onClick : function()
18715     {   
18716         Roo.log("click");
18717         this.setChecked(true);
18718     },
18719     
18720     setChecked : function(state,suppressEvent)
18721     {
18722         if(state){
18723             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18724                 v.dom.checked = false;
18725             });
18726         }
18727         Roo.log(this.inputEl().dom);
18728         this.checked = state;
18729         this.inputEl().dom.checked = state;
18730         
18731         if(suppressEvent !== true){
18732             this.fireEvent('check', this, state);
18733         }
18734         
18735         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18736         
18737     },
18738     
18739     getGroupValue : function()
18740     {
18741         var value = '';
18742         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18743             if(v.dom.checked == true){
18744                 value = v.dom.value;
18745             }
18746         });
18747         
18748         return value;
18749     },
18750     
18751     /**
18752      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18753      * @return {Mixed} value The field value
18754      */
18755     getValue : function(){
18756         return this.getGroupValue();
18757     }
18758     
18759 });
18760
18761  
18762 //<script type="text/javascript">
18763
18764 /*
18765  * Based  Ext JS Library 1.1.1
18766  * Copyright(c) 2006-2007, Ext JS, LLC.
18767  * LGPL
18768  *
18769  */
18770  
18771 /**
18772  * @class Roo.HtmlEditorCore
18773  * @extends Roo.Component
18774  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18775  *
18776  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18777  */
18778
18779 Roo.HtmlEditorCore = function(config){
18780     
18781     
18782     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18783     
18784     
18785     this.addEvents({
18786         /**
18787          * @event initialize
18788          * Fires when the editor is fully initialized (including the iframe)
18789          * @param {Roo.HtmlEditorCore} this
18790          */
18791         initialize: true,
18792         /**
18793          * @event activate
18794          * Fires when the editor is first receives the focus. Any insertion must wait
18795          * until after this event.
18796          * @param {Roo.HtmlEditorCore} this
18797          */
18798         activate: true,
18799          /**
18800          * @event beforesync
18801          * Fires before the textarea is updated with content from the editor iframe. Return false
18802          * to cancel the sync.
18803          * @param {Roo.HtmlEditorCore} this
18804          * @param {String} html
18805          */
18806         beforesync: true,
18807          /**
18808          * @event beforepush
18809          * Fires before the iframe editor is updated with content from the textarea. Return false
18810          * to cancel the push.
18811          * @param {Roo.HtmlEditorCore} this
18812          * @param {String} html
18813          */
18814         beforepush: true,
18815          /**
18816          * @event sync
18817          * Fires when the textarea is updated with content from the editor iframe.
18818          * @param {Roo.HtmlEditorCore} this
18819          * @param {String} html
18820          */
18821         sync: true,
18822          /**
18823          * @event push
18824          * Fires when the iframe editor is updated with content from the textarea.
18825          * @param {Roo.HtmlEditorCore} this
18826          * @param {String} html
18827          */
18828         push: true,
18829         
18830         /**
18831          * @event editorevent
18832          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18833          * @param {Roo.HtmlEditorCore} this
18834          */
18835         editorevent: true
18836         
18837     });
18838     
18839     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18840     
18841     // defaults : white / black...
18842     this.applyBlacklists();
18843     
18844     
18845     
18846 };
18847
18848
18849 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18850
18851
18852      /**
18853      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18854      */
18855     
18856     owner : false,
18857     
18858      /**
18859      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18860      *                        Roo.resizable.
18861      */
18862     resizable : false,
18863      /**
18864      * @cfg {Number} height (in pixels)
18865      */   
18866     height: 300,
18867    /**
18868      * @cfg {Number} width (in pixels)
18869      */   
18870     width: 500,
18871     
18872     /**
18873      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18874      * 
18875      */
18876     stylesheets: false,
18877     
18878     // id of frame..
18879     frameId: false,
18880     
18881     // private properties
18882     validationEvent : false,
18883     deferHeight: true,
18884     initialized : false,
18885     activated : false,
18886     sourceEditMode : false,
18887     onFocus : Roo.emptyFn,
18888     iframePad:3,
18889     hideMode:'offsets',
18890     
18891     clearUp: true,
18892     
18893     // blacklist + whitelisted elements..
18894     black: false,
18895     white: false,
18896      
18897     
18898
18899     /**
18900      * Protected method that will not generally be called directly. It
18901      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18902      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18903      */
18904     getDocMarkup : function(){
18905         // body styles..
18906         var st = '';
18907         
18908         // inherit styels from page...?? 
18909         if (this.stylesheets === false) {
18910             
18911             Roo.get(document.head).select('style').each(function(node) {
18912                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18913             });
18914             
18915             Roo.get(document.head).select('link').each(function(node) { 
18916                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18917             });
18918             
18919         } else if (!this.stylesheets.length) {
18920                 // simple..
18921                 st = '<style type="text/css">' +
18922                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18923                    '</style>';
18924         } else { 
18925             
18926         }
18927         
18928         st +=  '<style type="text/css">' +
18929             'IMG { cursor: pointer } ' +
18930         '</style>';
18931
18932         
18933         return '<html><head>' + st  +
18934             //<style type="text/css">' +
18935             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18936             //'</style>' +
18937             ' </head><body class="roo-htmleditor-body"></body></html>';
18938     },
18939
18940     // private
18941     onRender : function(ct, position)
18942     {
18943         var _t = this;
18944         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18945         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18946         
18947         
18948         this.el.dom.style.border = '0 none';
18949         this.el.dom.setAttribute('tabIndex', -1);
18950         this.el.addClass('x-hidden hide');
18951         
18952         
18953         
18954         if(Roo.isIE){ // fix IE 1px bogus margin
18955             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18956         }
18957        
18958         
18959         this.frameId = Roo.id();
18960         
18961          
18962         
18963         var iframe = this.owner.wrap.createChild({
18964             tag: 'iframe',
18965             cls: 'form-control', // bootstrap..
18966             id: this.frameId,
18967             name: this.frameId,
18968             frameBorder : 'no',
18969             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18970         }, this.el
18971         );
18972         
18973         
18974         this.iframe = iframe.dom;
18975
18976          this.assignDocWin();
18977         
18978         this.doc.designMode = 'on';
18979        
18980         this.doc.open();
18981         this.doc.write(this.getDocMarkup());
18982         this.doc.close();
18983
18984         
18985         var task = { // must defer to wait for browser to be ready
18986             run : function(){
18987                 //console.log("run task?" + this.doc.readyState);
18988                 this.assignDocWin();
18989                 if(this.doc.body || this.doc.readyState == 'complete'){
18990                     try {
18991                         this.doc.designMode="on";
18992                     } catch (e) {
18993                         return;
18994                     }
18995                     Roo.TaskMgr.stop(task);
18996                     this.initEditor.defer(10, this);
18997                 }
18998             },
18999             interval : 10,
19000             duration: 10000,
19001             scope: this
19002         };
19003         Roo.TaskMgr.start(task);
19004
19005     },
19006
19007     // private
19008     onResize : function(w, h)
19009     {
19010          Roo.log('resize: ' +w + ',' + h );
19011         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19012         if(!this.iframe){
19013             return;
19014         }
19015         if(typeof w == 'number'){
19016             
19017             this.iframe.style.width = w + 'px';
19018         }
19019         if(typeof h == 'number'){
19020             
19021             this.iframe.style.height = h + 'px';
19022             if(this.doc){
19023                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19024             }
19025         }
19026         
19027     },
19028
19029     /**
19030      * Toggles the editor between standard and source edit mode.
19031      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19032      */
19033     toggleSourceEdit : function(sourceEditMode){
19034         
19035         this.sourceEditMode = sourceEditMode === true;
19036         
19037         if(this.sourceEditMode){
19038  
19039             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19040             
19041         }else{
19042             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19043             //this.iframe.className = '';
19044             this.deferFocus();
19045         }
19046         //this.setSize(this.owner.wrap.getSize());
19047         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19048     },
19049
19050     
19051   
19052
19053     /**
19054      * Protected method that will not generally be called directly. If you need/want
19055      * custom HTML cleanup, this is the method you should override.
19056      * @param {String} html The HTML to be cleaned
19057      * return {String} The cleaned HTML
19058      */
19059     cleanHtml : function(html){
19060         html = String(html);
19061         if(html.length > 5){
19062             if(Roo.isSafari){ // strip safari nonsense
19063                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19064             }
19065         }
19066         if(html == '&nbsp;'){
19067             html = '';
19068         }
19069         return html;
19070     },
19071
19072     /**
19073      * HTML Editor -> Textarea
19074      * Protected method that will not generally be called directly. Syncs the contents
19075      * of the editor iframe with the textarea.
19076      */
19077     syncValue : function(){
19078         if(this.initialized){
19079             var bd = (this.doc.body || this.doc.documentElement);
19080             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19081             var html = bd.innerHTML;
19082             if(Roo.isSafari){
19083                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19084                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19085                 if(m && m[1]){
19086                     html = '<div style="'+m[0]+'">' + html + '</div>';
19087                 }
19088             }
19089             html = this.cleanHtml(html);
19090             // fix up the special chars.. normaly like back quotes in word...
19091             // however we do not want to do this with chinese..
19092             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19093                 var cc = b.charCodeAt();
19094                 if (
19095                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19096                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19097                     (cc >= 0xf900 && cc < 0xfb00 )
19098                 ) {
19099                         return b;
19100                 }
19101                 return "&#"+cc+";" 
19102             });
19103             if(this.owner.fireEvent('beforesync', this, html) !== false){
19104                 this.el.dom.value = html;
19105                 this.owner.fireEvent('sync', this, html);
19106             }
19107         }
19108     },
19109
19110     /**
19111      * Protected method that will not generally be called directly. Pushes the value of the textarea
19112      * into the iframe editor.
19113      */
19114     pushValue : function(){
19115         if(this.initialized){
19116             var v = this.el.dom.value.trim();
19117             
19118 //            if(v.length < 1){
19119 //                v = '&#160;';
19120 //            }
19121             
19122             if(this.owner.fireEvent('beforepush', this, v) !== false){
19123                 var d = (this.doc.body || this.doc.documentElement);
19124                 d.innerHTML = v;
19125                 this.cleanUpPaste();
19126                 this.el.dom.value = d.innerHTML;
19127                 this.owner.fireEvent('push', this, v);
19128             }
19129         }
19130     },
19131
19132     // private
19133     deferFocus : function(){
19134         this.focus.defer(10, this);
19135     },
19136
19137     // doc'ed in Field
19138     focus : function(){
19139         if(this.win && !this.sourceEditMode){
19140             this.win.focus();
19141         }else{
19142             this.el.focus();
19143         }
19144     },
19145     
19146     assignDocWin: function()
19147     {
19148         var iframe = this.iframe;
19149         
19150          if(Roo.isIE){
19151             this.doc = iframe.contentWindow.document;
19152             this.win = iframe.contentWindow;
19153         } else {
19154 //            if (!Roo.get(this.frameId)) {
19155 //                return;
19156 //            }
19157 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19158 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19159             
19160             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19161                 return;
19162             }
19163             
19164             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19165             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19166         }
19167     },
19168     
19169     // private
19170     initEditor : function(){
19171         //console.log("INIT EDITOR");
19172         this.assignDocWin();
19173         
19174         
19175         
19176         this.doc.designMode="on";
19177         this.doc.open();
19178         this.doc.write(this.getDocMarkup());
19179         this.doc.close();
19180         
19181         var dbody = (this.doc.body || this.doc.documentElement);
19182         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19183         // this copies styles from the containing element into thsi one..
19184         // not sure why we need all of this..
19185         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19186         
19187         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19188         //ss['background-attachment'] = 'fixed'; // w3c
19189         dbody.bgProperties = 'fixed'; // ie
19190         //Roo.DomHelper.applyStyles(dbody, ss);
19191         Roo.EventManager.on(this.doc, {
19192             //'mousedown': this.onEditorEvent,
19193             'mouseup': this.onEditorEvent,
19194             'dblclick': this.onEditorEvent,
19195             'click': this.onEditorEvent,
19196             'keyup': this.onEditorEvent,
19197             buffer:100,
19198             scope: this
19199         });
19200         if(Roo.isGecko){
19201             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19202         }
19203         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19204             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19205         }
19206         this.initialized = true;
19207
19208         this.owner.fireEvent('initialize', this);
19209         this.pushValue();
19210     },
19211
19212     // private
19213     onDestroy : function(){
19214         
19215         
19216         
19217         if(this.rendered){
19218             
19219             //for (var i =0; i < this.toolbars.length;i++) {
19220             //    // fixme - ask toolbars for heights?
19221             //    this.toolbars[i].onDestroy();
19222            // }
19223             
19224             //this.wrap.dom.innerHTML = '';
19225             //this.wrap.remove();
19226         }
19227     },
19228
19229     // private
19230     onFirstFocus : function(){
19231         
19232         this.assignDocWin();
19233         
19234         
19235         this.activated = true;
19236          
19237     
19238         if(Roo.isGecko){ // prevent silly gecko errors
19239             this.win.focus();
19240             var s = this.win.getSelection();
19241             if(!s.focusNode || s.focusNode.nodeType != 3){
19242                 var r = s.getRangeAt(0);
19243                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19244                 r.collapse(true);
19245                 this.deferFocus();
19246             }
19247             try{
19248                 this.execCmd('useCSS', true);
19249                 this.execCmd('styleWithCSS', false);
19250             }catch(e){}
19251         }
19252         this.owner.fireEvent('activate', this);
19253     },
19254
19255     // private
19256     adjustFont: function(btn){
19257         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19258         //if(Roo.isSafari){ // safari
19259         //    adjust *= 2;
19260        // }
19261         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19262         if(Roo.isSafari){ // safari
19263             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19264             v =  (v < 10) ? 10 : v;
19265             v =  (v > 48) ? 48 : v;
19266             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19267             
19268         }
19269         
19270         
19271         v = Math.max(1, v+adjust);
19272         
19273         this.execCmd('FontSize', v  );
19274     },
19275
19276     onEditorEvent : function(e)
19277     {
19278         this.owner.fireEvent('editorevent', this, e);
19279       //  this.updateToolbar();
19280         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19281     },
19282
19283     insertTag : function(tg)
19284     {
19285         // could be a bit smarter... -> wrap the current selected tRoo..
19286         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19287             
19288             range = this.createRange(this.getSelection());
19289             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19290             wrappingNode.appendChild(range.extractContents());
19291             range.insertNode(wrappingNode);
19292
19293             return;
19294             
19295             
19296             
19297         }
19298         this.execCmd("formatblock",   tg);
19299         
19300     },
19301     
19302     insertText : function(txt)
19303     {
19304         
19305         
19306         var range = this.createRange();
19307         range.deleteContents();
19308                //alert(Sender.getAttribute('label'));
19309                
19310         range.insertNode(this.doc.createTextNode(txt));
19311     } ,
19312     
19313      
19314
19315     /**
19316      * Executes a Midas editor command on the editor document and performs necessary focus and
19317      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19318      * @param {String} cmd The Midas command
19319      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19320      */
19321     relayCmd : function(cmd, value){
19322         this.win.focus();
19323         this.execCmd(cmd, value);
19324         this.owner.fireEvent('editorevent', this);
19325         //this.updateToolbar();
19326         this.owner.deferFocus();
19327     },
19328
19329     /**
19330      * Executes a Midas editor command directly on the editor document.
19331      * For visual commands, you should use {@link #relayCmd} instead.
19332      * <b>This should only be called after the editor is initialized.</b>
19333      * @param {String} cmd The Midas command
19334      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19335      */
19336     execCmd : function(cmd, value){
19337         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19338         this.syncValue();
19339     },
19340  
19341  
19342    
19343     /**
19344      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19345      * to insert tRoo.
19346      * @param {String} text | dom node.. 
19347      */
19348     insertAtCursor : function(text)
19349     {
19350         
19351         
19352         
19353         if(!this.activated){
19354             return;
19355         }
19356         /*
19357         if(Roo.isIE){
19358             this.win.focus();
19359             var r = this.doc.selection.createRange();
19360             if(r){
19361                 r.collapse(true);
19362                 r.pasteHTML(text);
19363                 this.syncValue();
19364                 this.deferFocus();
19365             
19366             }
19367             return;
19368         }
19369         */
19370         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19371             this.win.focus();
19372             
19373             
19374             // from jquery ui (MIT licenced)
19375             var range, node;
19376             var win = this.win;
19377             
19378             if (win.getSelection && win.getSelection().getRangeAt) {
19379                 range = win.getSelection().getRangeAt(0);
19380                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19381                 range.insertNode(node);
19382             } else if (win.document.selection && win.document.selection.createRange) {
19383                 // no firefox support
19384                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19385                 win.document.selection.createRange().pasteHTML(txt);
19386             } else {
19387                 // no firefox support
19388                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19389                 this.execCmd('InsertHTML', txt);
19390             } 
19391             
19392             this.syncValue();
19393             
19394             this.deferFocus();
19395         }
19396     },
19397  // private
19398     mozKeyPress : function(e){
19399         if(e.ctrlKey){
19400             var c = e.getCharCode(), cmd;
19401           
19402             if(c > 0){
19403                 c = String.fromCharCode(c).toLowerCase();
19404                 switch(c){
19405                     case 'b':
19406                         cmd = 'bold';
19407                         break;
19408                     case 'i':
19409                         cmd = 'italic';
19410                         break;
19411                     
19412                     case 'u':
19413                         cmd = 'underline';
19414                         break;
19415                     
19416                     case 'v':
19417                         this.cleanUpPaste.defer(100, this);
19418                         return;
19419                         
19420                 }
19421                 if(cmd){
19422                     this.win.focus();
19423                     this.execCmd(cmd);
19424                     this.deferFocus();
19425                     e.preventDefault();
19426                 }
19427                 
19428             }
19429         }
19430     },
19431
19432     // private
19433     fixKeys : function(){ // load time branching for fastest keydown performance
19434         if(Roo.isIE){
19435             return function(e){
19436                 var k = e.getKey(), r;
19437                 if(k == e.TAB){
19438                     e.stopEvent();
19439                     r = this.doc.selection.createRange();
19440                     if(r){
19441                         r.collapse(true);
19442                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19443                         this.deferFocus();
19444                     }
19445                     return;
19446                 }
19447                 
19448                 if(k == e.ENTER){
19449                     r = this.doc.selection.createRange();
19450                     if(r){
19451                         var target = r.parentElement();
19452                         if(!target || target.tagName.toLowerCase() != 'li'){
19453                             e.stopEvent();
19454                             r.pasteHTML('<br />');
19455                             r.collapse(false);
19456                             r.select();
19457                         }
19458                     }
19459                 }
19460                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19461                     this.cleanUpPaste.defer(100, this);
19462                     return;
19463                 }
19464                 
19465                 
19466             };
19467         }else if(Roo.isOpera){
19468             return function(e){
19469                 var k = e.getKey();
19470                 if(k == e.TAB){
19471                     e.stopEvent();
19472                     this.win.focus();
19473                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19474                     this.deferFocus();
19475                 }
19476                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19477                     this.cleanUpPaste.defer(100, this);
19478                     return;
19479                 }
19480                 
19481             };
19482         }else if(Roo.isSafari){
19483             return function(e){
19484                 var k = e.getKey();
19485                 
19486                 if(k == e.TAB){
19487                     e.stopEvent();
19488                     this.execCmd('InsertText','\t');
19489                     this.deferFocus();
19490                     return;
19491                 }
19492                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19493                     this.cleanUpPaste.defer(100, this);
19494                     return;
19495                 }
19496                 
19497              };
19498         }
19499     }(),
19500     
19501     getAllAncestors: function()
19502     {
19503         var p = this.getSelectedNode();
19504         var a = [];
19505         if (!p) {
19506             a.push(p); // push blank onto stack..
19507             p = this.getParentElement();
19508         }
19509         
19510         
19511         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19512             a.push(p);
19513             p = p.parentNode;
19514         }
19515         a.push(this.doc.body);
19516         return a;
19517     },
19518     lastSel : false,
19519     lastSelNode : false,
19520     
19521     
19522     getSelection : function() 
19523     {
19524         this.assignDocWin();
19525         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19526     },
19527     
19528     getSelectedNode: function() 
19529     {
19530         // this may only work on Gecko!!!
19531         
19532         // should we cache this!!!!
19533         
19534         
19535         
19536          
19537         var range = this.createRange(this.getSelection()).cloneRange();
19538         
19539         if (Roo.isIE) {
19540             var parent = range.parentElement();
19541             while (true) {
19542                 var testRange = range.duplicate();
19543                 testRange.moveToElementText(parent);
19544                 if (testRange.inRange(range)) {
19545                     break;
19546                 }
19547                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19548                     break;
19549                 }
19550                 parent = parent.parentElement;
19551             }
19552             return parent;
19553         }
19554         
19555         // is ancestor a text element.
19556         var ac =  range.commonAncestorContainer;
19557         if (ac.nodeType == 3) {
19558             ac = ac.parentNode;
19559         }
19560         
19561         var ar = ac.childNodes;
19562          
19563         var nodes = [];
19564         var other_nodes = [];
19565         var has_other_nodes = false;
19566         for (var i=0;i<ar.length;i++) {
19567             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19568                 continue;
19569             }
19570             // fullly contained node.
19571             
19572             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19573                 nodes.push(ar[i]);
19574                 continue;
19575             }
19576             
19577             // probably selected..
19578             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19579                 other_nodes.push(ar[i]);
19580                 continue;
19581             }
19582             // outer..
19583             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19584                 continue;
19585             }
19586             
19587             
19588             has_other_nodes = true;
19589         }
19590         if (!nodes.length && other_nodes.length) {
19591             nodes= other_nodes;
19592         }
19593         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19594             return false;
19595         }
19596         
19597         return nodes[0];
19598     },
19599     createRange: function(sel)
19600     {
19601         // this has strange effects when using with 
19602         // top toolbar - not sure if it's a great idea.
19603         //this.editor.contentWindow.focus();
19604         if (typeof sel != "undefined") {
19605             try {
19606                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19607             } catch(e) {
19608                 return this.doc.createRange();
19609             }
19610         } else {
19611             return this.doc.createRange();
19612         }
19613     },
19614     getParentElement: function()
19615     {
19616         
19617         this.assignDocWin();
19618         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19619         
19620         var range = this.createRange(sel);
19621          
19622         try {
19623             var p = range.commonAncestorContainer;
19624             while (p.nodeType == 3) { // text node
19625                 p = p.parentNode;
19626             }
19627             return p;
19628         } catch (e) {
19629             return null;
19630         }
19631     
19632     },
19633     /***
19634      *
19635      * Range intersection.. the hard stuff...
19636      *  '-1' = before
19637      *  '0' = hits..
19638      *  '1' = after.
19639      *         [ -- selected range --- ]
19640      *   [fail]                        [fail]
19641      *
19642      *    basically..
19643      *      if end is before start or  hits it. fail.
19644      *      if start is after end or hits it fail.
19645      *
19646      *   if either hits (but other is outside. - then it's not 
19647      *   
19648      *    
19649      **/
19650     
19651     
19652     // @see http://www.thismuchiknow.co.uk/?p=64.
19653     rangeIntersectsNode : function(range, node)
19654     {
19655         var nodeRange = node.ownerDocument.createRange();
19656         try {
19657             nodeRange.selectNode(node);
19658         } catch (e) {
19659             nodeRange.selectNodeContents(node);
19660         }
19661     
19662         var rangeStartRange = range.cloneRange();
19663         rangeStartRange.collapse(true);
19664     
19665         var rangeEndRange = range.cloneRange();
19666         rangeEndRange.collapse(false);
19667     
19668         var nodeStartRange = nodeRange.cloneRange();
19669         nodeStartRange.collapse(true);
19670     
19671         var nodeEndRange = nodeRange.cloneRange();
19672         nodeEndRange.collapse(false);
19673     
19674         return rangeStartRange.compareBoundaryPoints(
19675                  Range.START_TO_START, nodeEndRange) == -1 &&
19676                rangeEndRange.compareBoundaryPoints(
19677                  Range.START_TO_START, nodeStartRange) == 1;
19678         
19679          
19680     },
19681     rangeCompareNode : function(range, node)
19682     {
19683         var nodeRange = node.ownerDocument.createRange();
19684         try {
19685             nodeRange.selectNode(node);
19686         } catch (e) {
19687             nodeRange.selectNodeContents(node);
19688         }
19689         
19690         
19691         range.collapse(true);
19692     
19693         nodeRange.collapse(true);
19694      
19695         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19696         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19697          
19698         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19699         
19700         var nodeIsBefore   =  ss == 1;
19701         var nodeIsAfter    = ee == -1;
19702         
19703         if (nodeIsBefore && nodeIsAfter)
19704             return 0; // outer
19705         if (!nodeIsBefore && nodeIsAfter)
19706             return 1; //right trailed.
19707         
19708         if (nodeIsBefore && !nodeIsAfter)
19709             return 2;  // left trailed.
19710         // fully contined.
19711         return 3;
19712     },
19713
19714     // private? - in a new class?
19715     cleanUpPaste :  function()
19716     {
19717         // cleans up the whole document..
19718         Roo.log('cleanuppaste');
19719         
19720         this.cleanUpChildren(this.doc.body);
19721         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19722         if (clean != this.doc.body.innerHTML) {
19723             this.doc.body.innerHTML = clean;
19724         }
19725         
19726     },
19727     
19728     cleanWordChars : function(input) {// change the chars to hex code
19729         var he = Roo.HtmlEditorCore;
19730         
19731         var output = input;
19732         Roo.each(he.swapCodes, function(sw) { 
19733             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19734             
19735             output = output.replace(swapper, sw[1]);
19736         });
19737         
19738         return output;
19739     },
19740     
19741     
19742     cleanUpChildren : function (n)
19743     {
19744         if (!n.childNodes.length) {
19745             return;
19746         }
19747         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19748            this.cleanUpChild(n.childNodes[i]);
19749         }
19750     },
19751     
19752     
19753         
19754     
19755     cleanUpChild : function (node)
19756     {
19757         var ed = this;
19758         //console.log(node);
19759         if (node.nodeName == "#text") {
19760             // clean up silly Windows -- stuff?
19761             return; 
19762         }
19763         if (node.nodeName == "#comment") {
19764             node.parentNode.removeChild(node);
19765             // clean up silly Windows -- stuff?
19766             return; 
19767         }
19768         var lcname = node.tagName.toLowerCase();
19769         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19770         // whitelist of tags..
19771         
19772         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19773             // remove node.
19774             node.parentNode.removeChild(node);
19775             return;
19776             
19777         }
19778         
19779         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19780         
19781         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19782         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19783         
19784         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19785         //    remove_keep_children = true;
19786         //}
19787         
19788         if (remove_keep_children) {
19789             this.cleanUpChildren(node);
19790             // inserts everything just before this node...
19791             while (node.childNodes.length) {
19792                 var cn = node.childNodes[0];
19793                 node.removeChild(cn);
19794                 node.parentNode.insertBefore(cn, node);
19795             }
19796             node.parentNode.removeChild(node);
19797             return;
19798         }
19799         
19800         if (!node.attributes || !node.attributes.length) {
19801             this.cleanUpChildren(node);
19802             return;
19803         }
19804         
19805         function cleanAttr(n,v)
19806         {
19807             
19808             if (v.match(/^\./) || v.match(/^\//)) {
19809                 return;
19810             }
19811             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19812                 return;
19813             }
19814             if (v.match(/^#/)) {
19815                 return;
19816             }
19817 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19818             node.removeAttribute(n);
19819             
19820         }
19821         
19822         var cwhite = this.cwhite;
19823         var cblack = this.cblack;
19824             
19825         function cleanStyle(n,v)
19826         {
19827             if (v.match(/expression/)) { //XSS?? should we even bother..
19828                 node.removeAttribute(n);
19829                 return;
19830             }
19831             
19832             var parts = v.split(/;/);
19833             var clean = [];
19834             
19835             Roo.each(parts, function(p) {
19836                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19837                 if (!p.length) {
19838                     return true;
19839                 }
19840                 var l = p.split(':').shift().replace(/\s+/g,'');
19841                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19842                 
19843                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19844 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19845                     //node.removeAttribute(n);
19846                     return true;
19847                 }
19848                 //Roo.log()
19849                 // only allow 'c whitelisted system attributes'
19850                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19851 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19852                     //node.removeAttribute(n);
19853                     return true;
19854                 }
19855                 
19856                 
19857                  
19858                 
19859                 clean.push(p);
19860                 return true;
19861             });
19862             if (clean.length) { 
19863                 node.setAttribute(n, clean.join(';'));
19864             } else {
19865                 node.removeAttribute(n);
19866             }
19867             
19868         }
19869         
19870         
19871         for (var i = node.attributes.length-1; i > -1 ; i--) {
19872             var a = node.attributes[i];
19873             //console.log(a);
19874             
19875             if (a.name.toLowerCase().substr(0,2)=='on')  {
19876                 node.removeAttribute(a.name);
19877                 continue;
19878             }
19879             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19880                 node.removeAttribute(a.name);
19881                 continue;
19882             }
19883             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19884                 cleanAttr(a.name,a.value); // fixme..
19885                 continue;
19886             }
19887             if (a.name == 'style') {
19888                 cleanStyle(a.name,a.value);
19889                 continue;
19890             }
19891             /// clean up MS crap..
19892             // tecnically this should be a list of valid class'es..
19893             
19894             
19895             if (a.name == 'class') {
19896                 if (a.value.match(/^Mso/)) {
19897                     node.className = '';
19898                 }
19899                 
19900                 if (a.value.match(/body/)) {
19901                     node.className = '';
19902                 }
19903                 continue;
19904             }
19905             
19906             // style cleanup!?
19907             // class cleanup?
19908             
19909         }
19910         
19911         
19912         this.cleanUpChildren(node);
19913         
19914         
19915     },
19916     
19917     /**
19918      * Clean up MS wordisms...
19919      */
19920     cleanWord : function(node)
19921     {
19922         
19923         
19924         if (!node) {
19925             this.cleanWord(this.doc.body);
19926             return;
19927         }
19928         if (node.nodeName == "#text") {
19929             // clean up silly Windows -- stuff?
19930             return; 
19931         }
19932         if (node.nodeName == "#comment") {
19933             node.parentNode.removeChild(node);
19934             // clean up silly Windows -- stuff?
19935             return; 
19936         }
19937         
19938         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19939             node.parentNode.removeChild(node);
19940             return;
19941         }
19942         
19943         // remove - but keep children..
19944         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19945             while (node.childNodes.length) {
19946                 var cn = node.childNodes[0];
19947                 node.removeChild(cn);
19948                 node.parentNode.insertBefore(cn, node);
19949             }
19950             node.parentNode.removeChild(node);
19951             this.iterateChildren(node, this.cleanWord);
19952             return;
19953         }
19954         // clean styles
19955         if (node.className.length) {
19956             
19957             var cn = node.className.split(/\W+/);
19958             var cna = [];
19959             Roo.each(cn, function(cls) {
19960                 if (cls.match(/Mso[a-zA-Z]+/)) {
19961                     return;
19962                 }
19963                 cna.push(cls);
19964             });
19965             node.className = cna.length ? cna.join(' ') : '';
19966             if (!cna.length) {
19967                 node.removeAttribute("class");
19968             }
19969         }
19970         
19971         if (node.hasAttribute("lang")) {
19972             node.removeAttribute("lang");
19973         }
19974         
19975         if (node.hasAttribute("style")) {
19976             
19977             var styles = node.getAttribute("style").split(";");
19978             var nstyle = [];
19979             Roo.each(styles, function(s) {
19980                 if (!s.match(/:/)) {
19981                     return;
19982                 }
19983                 var kv = s.split(":");
19984                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19985                     return;
19986                 }
19987                 // what ever is left... we allow.
19988                 nstyle.push(s);
19989             });
19990             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19991             if (!nstyle.length) {
19992                 node.removeAttribute('style');
19993             }
19994         }
19995         this.iterateChildren(node, this.cleanWord);
19996         
19997         
19998         
19999     },
20000     /**
20001      * iterateChildren of a Node, calling fn each time, using this as the scole..
20002      * @param {DomNode} node node to iterate children of.
20003      * @param {Function} fn method of this class to call on each item.
20004      */
20005     iterateChildren : function(node, fn)
20006     {
20007         if (!node.childNodes.length) {
20008                 return;
20009         }
20010         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20011            fn.call(this, node.childNodes[i])
20012         }
20013     },
20014     
20015     
20016     /**
20017      * cleanTableWidths.
20018      *
20019      * Quite often pasting from word etc.. results in tables with column and widths.
20020      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20021      *
20022      */
20023     cleanTableWidths : function(node)
20024     {
20025          
20026          
20027         if (!node) {
20028             this.cleanTableWidths(this.doc.body);
20029             return;
20030         }
20031         
20032         // ignore list...
20033         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20034             return; 
20035         }
20036         Roo.log(node.tagName);
20037         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20038             this.iterateChildren(node, this.cleanTableWidths);
20039             return;
20040         }
20041         if (node.hasAttribute('width')) {
20042             node.removeAttribute('width');
20043         }
20044         
20045          
20046         if (node.hasAttribute("style")) {
20047             // pretty basic...
20048             
20049             var styles = node.getAttribute("style").split(";");
20050             var nstyle = [];
20051             Roo.each(styles, function(s) {
20052                 if (!s.match(/:/)) {
20053                     return;
20054                 }
20055                 var kv = s.split(":");
20056                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20057                     return;
20058                 }
20059                 // what ever is left... we allow.
20060                 nstyle.push(s);
20061             });
20062             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20063             if (!nstyle.length) {
20064                 node.removeAttribute('style');
20065             }
20066         }
20067         
20068         this.iterateChildren(node, this.cleanTableWidths);
20069         
20070         
20071     },
20072     
20073     
20074     
20075     
20076     domToHTML : function(currentElement, depth, nopadtext) {
20077         
20078         depth = depth || 0;
20079         nopadtext = nopadtext || false;
20080     
20081         if (!currentElement) {
20082             return this.domToHTML(this.doc.body);
20083         }
20084         
20085         //Roo.log(currentElement);
20086         var j;
20087         var allText = false;
20088         var nodeName = currentElement.nodeName;
20089         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20090         
20091         if  (nodeName == '#text') {
20092             
20093             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20094         }
20095         
20096         
20097         var ret = '';
20098         if (nodeName != 'BODY') {
20099              
20100             var i = 0;
20101             // Prints the node tagName, such as <A>, <IMG>, etc
20102             if (tagName) {
20103                 var attr = [];
20104                 for(i = 0; i < currentElement.attributes.length;i++) {
20105                     // quoting?
20106                     var aname = currentElement.attributes.item(i).name;
20107                     if (!currentElement.attributes.item(i).value.length) {
20108                         continue;
20109                     }
20110                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20111                 }
20112                 
20113                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20114             } 
20115             else {
20116                 
20117                 // eack
20118             }
20119         } else {
20120             tagName = false;
20121         }
20122         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20123             return ret;
20124         }
20125         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20126             nopadtext = true;
20127         }
20128         
20129         
20130         // Traverse the tree
20131         i = 0;
20132         var currentElementChild = currentElement.childNodes.item(i);
20133         var allText = true;
20134         var innerHTML  = '';
20135         lastnode = '';
20136         while (currentElementChild) {
20137             // Formatting code (indent the tree so it looks nice on the screen)
20138             var nopad = nopadtext;
20139             if (lastnode == 'SPAN') {
20140                 nopad  = true;
20141             }
20142             // text
20143             if  (currentElementChild.nodeName == '#text') {
20144                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20145                 toadd = nopadtext ? toadd : toadd.trim();
20146                 if (!nopad && toadd.length > 80) {
20147                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20148                 }
20149                 innerHTML  += toadd;
20150                 
20151                 i++;
20152                 currentElementChild = currentElement.childNodes.item(i);
20153                 lastNode = '';
20154                 continue;
20155             }
20156             allText = false;
20157             
20158             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20159                 
20160             // Recursively traverse the tree structure of the child node
20161             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20162             lastnode = currentElementChild.nodeName;
20163             i++;
20164             currentElementChild=currentElement.childNodes.item(i);
20165         }
20166         
20167         ret += innerHTML;
20168         
20169         if (!allText) {
20170                 // The remaining code is mostly for formatting the tree
20171             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20172         }
20173         
20174         
20175         if (tagName) {
20176             ret+= "</"+tagName+">";
20177         }
20178         return ret;
20179         
20180     },
20181         
20182     applyBlacklists : function()
20183     {
20184         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20185         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20186         
20187         this.white = [];
20188         this.black = [];
20189         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20190             if (b.indexOf(tag) > -1) {
20191                 return;
20192             }
20193             this.white.push(tag);
20194             
20195         }, this);
20196         
20197         Roo.each(w, function(tag) {
20198             if (b.indexOf(tag) > -1) {
20199                 return;
20200             }
20201             if (this.white.indexOf(tag) > -1) {
20202                 return;
20203             }
20204             this.white.push(tag);
20205             
20206         }, this);
20207         
20208         
20209         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20210             if (w.indexOf(tag) > -1) {
20211                 return;
20212             }
20213             this.black.push(tag);
20214             
20215         }, this);
20216         
20217         Roo.each(b, function(tag) {
20218             if (w.indexOf(tag) > -1) {
20219                 return;
20220             }
20221             if (this.black.indexOf(tag) > -1) {
20222                 return;
20223             }
20224             this.black.push(tag);
20225             
20226         }, this);
20227         
20228         
20229         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20230         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20231         
20232         this.cwhite = [];
20233         this.cblack = [];
20234         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20235             if (b.indexOf(tag) > -1) {
20236                 return;
20237             }
20238             this.cwhite.push(tag);
20239             
20240         }, this);
20241         
20242         Roo.each(w, function(tag) {
20243             if (b.indexOf(tag) > -1) {
20244                 return;
20245             }
20246             if (this.cwhite.indexOf(tag) > -1) {
20247                 return;
20248             }
20249             this.cwhite.push(tag);
20250             
20251         }, this);
20252         
20253         
20254         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20255             if (w.indexOf(tag) > -1) {
20256                 return;
20257             }
20258             this.cblack.push(tag);
20259             
20260         }, this);
20261         
20262         Roo.each(b, function(tag) {
20263             if (w.indexOf(tag) > -1) {
20264                 return;
20265             }
20266             if (this.cblack.indexOf(tag) > -1) {
20267                 return;
20268             }
20269             this.cblack.push(tag);
20270             
20271         }, this);
20272     },
20273     
20274     setStylesheets : function(stylesheets)
20275     {
20276         if(typeof(stylesheets) == 'string'){
20277             Roo.get(this.iframe.contentDocument.head).createChild({
20278                 tag : 'link',
20279                 rel : 'stylesheet',
20280                 type : 'text/css',
20281                 href : stylesheets
20282             });
20283             
20284             return;
20285         }
20286         var _this = this;
20287      
20288         Roo.each(stylesheets, function(s) {
20289             if(!s.length){
20290                 return;
20291             }
20292             
20293             Roo.get(_this.iframe.contentDocument.head).createChild({
20294                 tag : 'link',
20295                 rel : 'stylesheet',
20296                 type : 'text/css',
20297                 href : s
20298             });
20299         });
20300
20301         
20302     },
20303     
20304     removeStylesheets : function()
20305     {
20306         var _this = this;
20307         
20308         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20309             s.remove();
20310         });
20311     }
20312     
20313     // hide stuff that is not compatible
20314     /**
20315      * @event blur
20316      * @hide
20317      */
20318     /**
20319      * @event change
20320      * @hide
20321      */
20322     /**
20323      * @event focus
20324      * @hide
20325      */
20326     /**
20327      * @event specialkey
20328      * @hide
20329      */
20330     /**
20331      * @cfg {String} fieldClass @hide
20332      */
20333     /**
20334      * @cfg {String} focusClass @hide
20335      */
20336     /**
20337      * @cfg {String} autoCreate @hide
20338      */
20339     /**
20340      * @cfg {String} inputType @hide
20341      */
20342     /**
20343      * @cfg {String} invalidClass @hide
20344      */
20345     /**
20346      * @cfg {String} invalidText @hide
20347      */
20348     /**
20349      * @cfg {String} msgFx @hide
20350      */
20351     /**
20352      * @cfg {String} validateOnBlur @hide
20353      */
20354 });
20355
20356 Roo.HtmlEditorCore.white = [
20357         'area', 'br', 'img', 'input', 'hr', 'wbr',
20358         
20359        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20360        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20361        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20362        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20363        'table',   'ul',         'xmp', 
20364        
20365        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20366       'thead',   'tr', 
20367      
20368       'dir', 'menu', 'ol', 'ul', 'dl',
20369        
20370       'embed',  'object'
20371 ];
20372
20373
20374 Roo.HtmlEditorCore.black = [
20375     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20376         'applet', // 
20377         'base',   'basefont', 'bgsound', 'blink',  'body', 
20378         'frame',  'frameset', 'head',    'html',   'ilayer', 
20379         'iframe', 'layer',  'link',     'meta',    'object',   
20380         'script', 'style' ,'title',  'xml' // clean later..
20381 ];
20382 Roo.HtmlEditorCore.clean = [
20383     'script', 'style', 'title', 'xml'
20384 ];
20385 Roo.HtmlEditorCore.remove = [
20386     'font'
20387 ];
20388 // attributes..
20389
20390 Roo.HtmlEditorCore.ablack = [
20391     'on'
20392 ];
20393     
20394 Roo.HtmlEditorCore.aclean = [ 
20395     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20396 ];
20397
20398 // protocols..
20399 Roo.HtmlEditorCore.pwhite= [
20400         'http',  'https',  'mailto'
20401 ];
20402
20403 // white listed style attributes.
20404 Roo.HtmlEditorCore.cwhite= [
20405       //  'text-align', /// default is to allow most things..
20406       
20407          
20408 //        'font-size'//??
20409 ];
20410
20411 // black listed style attributes.
20412 Roo.HtmlEditorCore.cblack= [
20413       //  'font-size' -- this can be set by the project 
20414 ];
20415
20416
20417 Roo.HtmlEditorCore.swapCodes   =[ 
20418     [    8211, "--" ], 
20419     [    8212, "--" ], 
20420     [    8216,  "'" ],  
20421     [    8217, "'" ],  
20422     [    8220, '"' ],  
20423     [    8221, '"' ],  
20424     [    8226, "*" ],  
20425     [    8230, "..." ]
20426 ]; 
20427
20428     /*
20429  * - LGPL
20430  *
20431  * HtmlEditor
20432  * 
20433  */
20434
20435 /**
20436  * @class Roo.bootstrap.HtmlEditor
20437  * @extends Roo.bootstrap.TextArea
20438  * Bootstrap HtmlEditor class
20439
20440  * @constructor
20441  * Create a new HtmlEditor
20442  * @param {Object} config The config object
20443  */
20444
20445 Roo.bootstrap.HtmlEditor = function(config){
20446     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20447     if (!this.toolbars) {
20448         this.toolbars = [];
20449     }
20450     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20451     this.addEvents({
20452             /**
20453              * @event initialize
20454              * Fires when the editor is fully initialized (including the iframe)
20455              * @param {HtmlEditor} this
20456              */
20457             initialize: true,
20458             /**
20459              * @event activate
20460              * Fires when the editor is first receives the focus. Any insertion must wait
20461              * until after this event.
20462              * @param {HtmlEditor} this
20463              */
20464             activate: true,
20465              /**
20466              * @event beforesync
20467              * Fires before the textarea is updated with content from the editor iframe. Return false
20468              * to cancel the sync.
20469              * @param {HtmlEditor} this
20470              * @param {String} html
20471              */
20472             beforesync: true,
20473              /**
20474              * @event beforepush
20475              * Fires before the iframe editor is updated with content from the textarea. Return false
20476              * to cancel the push.
20477              * @param {HtmlEditor} this
20478              * @param {String} html
20479              */
20480             beforepush: true,
20481              /**
20482              * @event sync
20483              * Fires when the textarea is updated with content from the editor iframe.
20484              * @param {HtmlEditor} this
20485              * @param {String} html
20486              */
20487             sync: true,
20488              /**
20489              * @event push
20490              * Fires when the iframe editor is updated with content from the textarea.
20491              * @param {HtmlEditor} this
20492              * @param {String} html
20493              */
20494             push: true,
20495              /**
20496              * @event editmodechange
20497              * Fires when the editor switches edit modes
20498              * @param {HtmlEditor} this
20499              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20500              */
20501             editmodechange: true,
20502             /**
20503              * @event editorevent
20504              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20505              * @param {HtmlEditor} this
20506              */
20507             editorevent: true,
20508             /**
20509              * @event firstfocus
20510              * Fires when on first focus - needed by toolbars..
20511              * @param {HtmlEditor} this
20512              */
20513             firstfocus: true,
20514             /**
20515              * @event autosave
20516              * Auto save the htmlEditor value as a file into Events
20517              * @param {HtmlEditor} this
20518              */
20519             autosave: true,
20520             /**
20521              * @event savedpreview
20522              * preview the saved version of htmlEditor
20523              * @param {HtmlEditor} this
20524              */
20525             savedpreview: true
20526         });
20527 };
20528
20529
20530 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20531     
20532     
20533       /**
20534      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20535      */
20536     toolbars : false,
20537    
20538      /**
20539      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20540      *                        Roo.resizable.
20541      */
20542     resizable : false,
20543      /**
20544      * @cfg {Number} height (in pixels)
20545      */   
20546     height: 300,
20547    /**
20548      * @cfg {Number} width (in pixels)
20549      */   
20550     width: false,
20551     
20552     /**
20553      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20554      * 
20555      */
20556     stylesheets: false,
20557     
20558     // id of frame..
20559     frameId: false,
20560     
20561     // private properties
20562     validationEvent : false,
20563     deferHeight: true,
20564     initialized : false,
20565     activated : false,
20566     
20567     onFocus : Roo.emptyFn,
20568     iframePad:3,
20569     hideMode:'offsets',
20570     
20571     
20572     tbContainer : false,
20573     
20574     toolbarContainer :function() {
20575         return this.wrap.select('.x-html-editor-tb',true).first();
20576     },
20577
20578     /**
20579      * Protected method that will not generally be called directly. It
20580      * is called when the editor creates its toolbar. Override this method if you need to
20581      * add custom toolbar buttons.
20582      * @param {HtmlEditor} editor
20583      */
20584     createToolbar : function(){
20585         
20586         Roo.log("create toolbars");
20587         
20588         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20589         this.toolbars[0].render(this.toolbarContainer());
20590         
20591         return;
20592         
20593 //        if (!editor.toolbars || !editor.toolbars.length) {
20594 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20595 //        }
20596 //        
20597 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20598 //            editor.toolbars[i] = Roo.factory(
20599 //                    typeof(editor.toolbars[i]) == 'string' ?
20600 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20601 //                Roo.bootstrap.HtmlEditor);
20602 //            editor.toolbars[i].init(editor);
20603 //        }
20604     },
20605
20606      
20607     // private
20608     onRender : function(ct, position)
20609     {
20610        // Roo.log("Call onRender: " + this.xtype);
20611         var _t = this;
20612         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20613       
20614         this.wrap = this.inputEl().wrap({
20615             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20616         });
20617         
20618         this.editorcore.onRender(ct, position);
20619          
20620         if (this.resizable) {
20621             this.resizeEl = new Roo.Resizable(this.wrap, {
20622                 pinned : true,
20623                 wrap: true,
20624                 dynamic : true,
20625                 minHeight : this.height,
20626                 height: this.height,
20627                 handles : this.resizable,
20628                 width: this.width,
20629                 listeners : {
20630                     resize : function(r, w, h) {
20631                         _t.onResize(w,h); // -something
20632                     }
20633                 }
20634             });
20635             
20636         }
20637         this.createToolbar(this);
20638        
20639         
20640         if(!this.width && this.resizable){
20641             this.setSize(this.wrap.getSize());
20642         }
20643         if (this.resizeEl) {
20644             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20645             // should trigger onReize..
20646         }
20647         
20648     },
20649
20650     // private
20651     onResize : function(w, h)
20652     {
20653         Roo.log('resize: ' +w + ',' + h );
20654         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20655         var ew = false;
20656         var eh = false;
20657         
20658         if(this.inputEl() ){
20659             if(typeof w == 'number'){
20660                 var aw = w - this.wrap.getFrameWidth('lr');
20661                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20662                 ew = aw;
20663             }
20664             if(typeof h == 'number'){
20665                  var tbh = -11;  // fixme it needs to tool bar size!
20666                 for (var i =0; i < this.toolbars.length;i++) {
20667                     // fixme - ask toolbars for heights?
20668                     tbh += this.toolbars[i].el.getHeight();
20669                     //if (this.toolbars[i].footer) {
20670                     //    tbh += this.toolbars[i].footer.el.getHeight();
20671                     //}
20672                 }
20673               
20674                 
20675                 
20676                 
20677                 
20678                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20679                 ah -= 5; // knock a few pixes off for look..
20680                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20681                 var eh = ah;
20682             }
20683         }
20684         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20685         this.editorcore.onResize(ew,eh);
20686         
20687     },
20688
20689     /**
20690      * Toggles the editor between standard and source edit mode.
20691      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20692      */
20693     toggleSourceEdit : function(sourceEditMode)
20694     {
20695         this.editorcore.toggleSourceEdit(sourceEditMode);
20696         
20697         if(this.editorcore.sourceEditMode){
20698             Roo.log('editor - showing textarea');
20699             
20700 //            Roo.log('in');
20701 //            Roo.log(this.syncValue());
20702             this.syncValue();
20703             this.inputEl().removeClass(['hide', 'x-hidden']);
20704             this.inputEl().dom.removeAttribute('tabIndex');
20705             this.inputEl().focus();
20706         }else{
20707             Roo.log('editor - hiding textarea');
20708 //            Roo.log('out')
20709 //            Roo.log(this.pushValue()); 
20710             this.pushValue();
20711             
20712             this.inputEl().addClass(['hide', 'x-hidden']);
20713             this.inputEl().dom.setAttribute('tabIndex', -1);
20714             //this.deferFocus();
20715         }
20716          
20717         if(this.resizable){
20718             this.setSize(this.wrap.getSize());
20719         }
20720         
20721         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20722     },
20723  
20724     // private (for BoxComponent)
20725     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20726
20727     // private (for BoxComponent)
20728     getResizeEl : function(){
20729         return this.wrap;
20730     },
20731
20732     // private (for BoxComponent)
20733     getPositionEl : function(){
20734         return this.wrap;
20735     },
20736
20737     // private
20738     initEvents : function(){
20739         this.originalValue = this.getValue();
20740     },
20741
20742 //    /**
20743 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20744 //     * @method
20745 //     */
20746 //    markInvalid : Roo.emptyFn,
20747 //    /**
20748 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20749 //     * @method
20750 //     */
20751 //    clearInvalid : Roo.emptyFn,
20752
20753     setValue : function(v){
20754         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20755         this.editorcore.pushValue();
20756     },
20757
20758      
20759     // private
20760     deferFocus : function(){
20761         this.focus.defer(10, this);
20762     },
20763
20764     // doc'ed in Field
20765     focus : function(){
20766         this.editorcore.focus();
20767         
20768     },
20769       
20770
20771     // private
20772     onDestroy : function(){
20773         
20774         
20775         
20776         if(this.rendered){
20777             
20778             for (var i =0; i < this.toolbars.length;i++) {
20779                 // fixme - ask toolbars for heights?
20780                 this.toolbars[i].onDestroy();
20781             }
20782             
20783             this.wrap.dom.innerHTML = '';
20784             this.wrap.remove();
20785         }
20786     },
20787
20788     // private
20789     onFirstFocus : function(){
20790         //Roo.log("onFirstFocus");
20791         this.editorcore.onFirstFocus();
20792          for (var i =0; i < this.toolbars.length;i++) {
20793             this.toolbars[i].onFirstFocus();
20794         }
20795         
20796     },
20797     
20798     // private
20799     syncValue : function()
20800     {   
20801         this.editorcore.syncValue();
20802     },
20803     
20804     pushValue : function()
20805     {   
20806         this.editorcore.pushValue();
20807     }
20808      
20809     
20810     // hide stuff that is not compatible
20811     /**
20812      * @event blur
20813      * @hide
20814      */
20815     /**
20816      * @event change
20817      * @hide
20818      */
20819     /**
20820      * @event focus
20821      * @hide
20822      */
20823     /**
20824      * @event specialkey
20825      * @hide
20826      */
20827     /**
20828      * @cfg {String} fieldClass @hide
20829      */
20830     /**
20831      * @cfg {String} focusClass @hide
20832      */
20833     /**
20834      * @cfg {String} autoCreate @hide
20835      */
20836     /**
20837      * @cfg {String} inputType @hide
20838      */
20839     /**
20840      * @cfg {String} invalidClass @hide
20841      */
20842     /**
20843      * @cfg {String} invalidText @hide
20844      */
20845     /**
20846      * @cfg {String} msgFx @hide
20847      */
20848     /**
20849      * @cfg {String} validateOnBlur @hide
20850      */
20851 });
20852  
20853     
20854    
20855    
20856    
20857       
20858 Roo.namespace('Roo.bootstrap.htmleditor');
20859 /**
20860  * @class Roo.bootstrap.HtmlEditorToolbar1
20861  * Basic Toolbar
20862  * 
20863  * Usage:
20864  *
20865  new Roo.bootstrap.HtmlEditor({
20866     ....
20867     toolbars : [
20868         new Roo.bootstrap.HtmlEditorToolbar1({
20869             disable : { fonts: 1 , format: 1, ..., ... , ...],
20870             btns : [ .... ]
20871         })
20872     }
20873      
20874  * 
20875  * @cfg {Object} disable List of elements to disable..
20876  * @cfg {Array} btns List of additional buttons.
20877  * 
20878  * 
20879  * NEEDS Extra CSS? 
20880  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20881  */
20882  
20883 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20884 {
20885     
20886     Roo.apply(this, config);
20887     
20888     // default disabled, based on 'good practice'..
20889     this.disable = this.disable || {};
20890     Roo.applyIf(this.disable, {
20891         fontSize : true,
20892         colors : true,
20893         specialElements : true
20894     });
20895     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20896     
20897     this.editor = config.editor;
20898     this.editorcore = config.editor.editorcore;
20899     
20900     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20901     
20902     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20903     // dont call parent... till later.
20904 }
20905 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20906      
20907     bar : true,
20908     
20909     editor : false,
20910     editorcore : false,
20911     
20912     
20913     formats : [
20914         "p" ,  
20915         "h1","h2","h3","h4","h5","h6", 
20916         "pre", "code", 
20917         "abbr", "acronym", "address", "cite", "samp", "var",
20918         'div','span'
20919     ],
20920     
20921     onRender : function(ct, position)
20922     {
20923        // Roo.log("Call onRender: " + this.xtype);
20924         
20925        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20926        Roo.log(this.el);
20927        this.el.dom.style.marginBottom = '0';
20928        var _this = this;
20929        var editorcore = this.editorcore;
20930        var editor= this.editor;
20931        
20932        var children = [];
20933        var btn = function(id,cmd , toggle, handler){
20934        
20935             var  event = toggle ? 'toggle' : 'click';
20936        
20937             var a = {
20938                 size : 'sm',
20939                 xtype: 'Button',
20940                 xns: Roo.bootstrap,
20941                 glyphicon : id,
20942                 cmd : id || cmd,
20943                 enableToggle:toggle !== false,
20944                 //html : 'submit'
20945                 pressed : toggle ? false : null,
20946                 listeners : {}
20947             };
20948             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20949                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20950             };
20951             children.push(a);
20952             return a;
20953        }
20954         
20955         var style = {
20956                 xtype: 'Button',
20957                 size : 'sm',
20958                 xns: Roo.bootstrap,
20959                 glyphicon : 'font',
20960                 //html : 'submit'
20961                 menu : {
20962                     xtype: 'Menu',
20963                     xns: Roo.bootstrap,
20964                     items:  []
20965                 }
20966         };
20967         Roo.each(this.formats, function(f) {
20968             style.menu.items.push({
20969                 xtype :'MenuItem',
20970                 xns: Roo.bootstrap,
20971                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20972                 tagname : f,
20973                 listeners : {
20974                     click : function()
20975                     {
20976                         editorcore.insertTag(this.tagname);
20977                         editor.focus();
20978                     }
20979                 }
20980                 
20981             });
20982         });
20983          children.push(style);   
20984             
20985             
20986         btn('bold',false,true);
20987         btn('italic',false,true);
20988         btn('align-left', 'justifyleft',true);
20989         btn('align-center', 'justifycenter',true);
20990         btn('align-right' , 'justifyright',true);
20991         btn('link', false, false, function(btn) {
20992             //Roo.log("create link?");
20993             var url = prompt(this.createLinkText, this.defaultLinkValue);
20994             if(url && url != 'http:/'+'/'){
20995                 this.editorcore.relayCmd('createlink', url);
20996             }
20997         }),
20998         btn('list','insertunorderedlist',true);
20999         btn('pencil', false,true, function(btn){
21000                 Roo.log(this);
21001                 
21002                 this.toggleSourceEdit(btn.pressed);
21003         });
21004         /*
21005         var cog = {
21006                 xtype: 'Button',
21007                 size : 'sm',
21008                 xns: Roo.bootstrap,
21009                 glyphicon : 'cog',
21010                 //html : 'submit'
21011                 menu : {
21012                     xtype: 'Menu',
21013                     xns: Roo.bootstrap,
21014                     items:  []
21015                 }
21016         };
21017         
21018         cog.menu.items.push({
21019             xtype :'MenuItem',
21020             xns: Roo.bootstrap,
21021             html : Clean styles,
21022             tagname : f,
21023             listeners : {
21024                 click : function()
21025                 {
21026                     editorcore.insertTag(this.tagname);
21027                     editor.focus();
21028                 }
21029             }
21030             
21031         });
21032        */
21033         
21034          
21035        this.xtype = 'NavSimplebar';
21036         
21037         for(var i=0;i< children.length;i++) {
21038             
21039             this.buttons.add(this.addxtypeChild(children[i]));
21040             
21041         }
21042         
21043         editor.on('editorevent', this.updateToolbar, this);
21044     },
21045     onBtnClick : function(id)
21046     {
21047        this.editorcore.relayCmd(id);
21048        this.editorcore.focus();
21049     },
21050     
21051     /**
21052      * Protected method that will not generally be called directly. It triggers
21053      * a toolbar update by reading the markup state of the current selection in the editor.
21054      */
21055     updateToolbar: function(){
21056
21057         if(!this.editorcore.activated){
21058             this.editor.onFirstFocus(); // is this neeed?
21059             return;
21060         }
21061
21062         var btns = this.buttons; 
21063         var doc = this.editorcore.doc;
21064         btns.get('bold').setActive(doc.queryCommandState('bold'));
21065         btns.get('italic').setActive(doc.queryCommandState('italic'));
21066         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21067         
21068         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21069         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21070         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21071         
21072         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21073         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21074          /*
21075         
21076         var ans = this.editorcore.getAllAncestors();
21077         if (this.formatCombo) {
21078             
21079             
21080             var store = this.formatCombo.store;
21081             this.formatCombo.setValue("");
21082             for (var i =0; i < ans.length;i++) {
21083                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21084                     // select it..
21085                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21086                     break;
21087                 }
21088             }
21089         }
21090         
21091         
21092         
21093         // hides menus... - so this cant be on a menu...
21094         Roo.bootstrap.MenuMgr.hideAll();
21095         */
21096         Roo.bootstrap.MenuMgr.hideAll();
21097         //this.editorsyncValue();
21098     },
21099     onFirstFocus: function() {
21100         this.buttons.each(function(item){
21101            item.enable();
21102         });
21103     },
21104     toggleSourceEdit : function(sourceEditMode){
21105         
21106           
21107         if(sourceEditMode){
21108             Roo.log("disabling buttons");
21109            this.buttons.each( function(item){
21110                 if(item.cmd != 'pencil'){
21111                     item.disable();
21112                 }
21113             });
21114           
21115         }else{
21116             Roo.log("enabling buttons");
21117             if(this.editorcore.initialized){
21118                 this.buttons.each( function(item){
21119                     item.enable();
21120                 });
21121             }
21122             
21123         }
21124         Roo.log("calling toggole on editor");
21125         // tell the editor that it's been pressed..
21126         this.editor.toggleSourceEdit(sourceEditMode);
21127        
21128     }
21129 });
21130
21131
21132
21133
21134
21135 /**
21136  * @class Roo.bootstrap.Table.AbstractSelectionModel
21137  * @extends Roo.util.Observable
21138  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21139  * implemented by descendant classes.  This class should not be directly instantiated.
21140  * @constructor
21141  */
21142 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21143     this.locked = false;
21144     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21145 };
21146
21147
21148 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21149     /** @ignore Called by the grid automatically. Do not call directly. */
21150     init : function(grid){
21151         this.grid = grid;
21152         this.initEvents();
21153     },
21154
21155     /**
21156      * Locks the selections.
21157      */
21158     lock : function(){
21159         this.locked = true;
21160     },
21161
21162     /**
21163      * Unlocks the selections.
21164      */
21165     unlock : function(){
21166         this.locked = false;
21167     },
21168
21169     /**
21170      * Returns true if the selections are locked.
21171      * @return {Boolean}
21172      */
21173     isLocked : function(){
21174         return this.locked;
21175     }
21176 });
21177 /**
21178  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21179  * @class Roo.bootstrap.Table.RowSelectionModel
21180  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21181  * It supports multiple selections and keyboard selection/navigation. 
21182  * @constructor
21183  * @param {Object} config
21184  */
21185
21186 Roo.bootstrap.Table.RowSelectionModel = function(config){
21187     Roo.apply(this, config);
21188     this.selections = new Roo.util.MixedCollection(false, function(o){
21189         return o.id;
21190     });
21191
21192     this.last = false;
21193     this.lastActive = false;
21194
21195     this.addEvents({
21196         /**
21197              * @event selectionchange
21198              * Fires when the selection changes
21199              * @param {SelectionModel} this
21200              */
21201             "selectionchange" : true,
21202         /**
21203              * @event afterselectionchange
21204              * Fires after the selection changes (eg. by key press or clicking)
21205              * @param {SelectionModel} this
21206              */
21207             "afterselectionchange" : true,
21208         /**
21209              * @event beforerowselect
21210              * Fires when a row is selected being selected, return false to cancel.
21211              * @param {SelectionModel} this
21212              * @param {Number} rowIndex The selected index
21213              * @param {Boolean} keepExisting False if other selections will be cleared
21214              */
21215             "beforerowselect" : true,
21216         /**
21217              * @event rowselect
21218              * Fires when a row is selected.
21219              * @param {SelectionModel} this
21220              * @param {Number} rowIndex The selected index
21221              * @param {Roo.data.Record} r The record
21222              */
21223             "rowselect" : true,
21224         /**
21225              * @event rowdeselect
21226              * Fires when a row is deselected.
21227              * @param {SelectionModel} this
21228              * @param {Number} rowIndex The selected index
21229              */
21230         "rowdeselect" : true
21231     });
21232     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21233     this.locked = false;
21234 };
21235
21236 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21237     /**
21238      * @cfg {Boolean} singleSelect
21239      * True to allow selection of only one row at a time (defaults to false)
21240      */
21241     singleSelect : false,
21242
21243     // private
21244     initEvents : function(){
21245
21246         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21247             this.grid.on("mousedown", this.handleMouseDown, this);
21248         }else{ // allow click to work like normal
21249             this.grid.on("rowclick", this.handleDragableRowClick, this);
21250         }
21251
21252         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21253             "up" : function(e){
21254                 if(!e.shiftKey){
21255                     this.selectPrevious(e.shiftKey);
21256                 }else if(this.last !== false && this.lastActive !== false){
21257                     var last = this.last;
21258                     this.selectRange(this.last,  this.lastActive-1);
21259                     this.grid.getView().focusRow(this.lastActive);
21260                     if(last !== false){
21261                         this.last = last;
21262                     }
21263                 }else{
21264                     this.selectFirstRow();
21265                 }
21266                 this.fireEvent("afterselectionchange", this);
21267             },
21268             "down" : function(e){
21269                 if(!e.shiftKey){
21270                     this.selectNext(e.shiftKey);
21271                 }else if(this.last !== false && this.lastActive !== false){
21272                     var last = this.last;
21273                     this.selectRange(this.last,  this.lastActive+1);
21274                     this.grid.getView().focusRow(this.lastActive);
21275                     if(last !== false){
21276                         this.last = last;
21277                     }
21278                 }else{
21279                     this.selectFirstRow();
21280                 }
21281                 this.fireEvent("afterselectionchange", this);
21282             },
21283             scope: this
21284         });
21285
21286         var view = this.grid.view;
21287         view.on("refresh", this.onRefresh, this);
21288         view.on("rowupdated", this.onRowUpdated, this);
21289         view.on("rowremoved", this.onRemove, this);
21290     },
21291
21292     // private
21293     onRefresh : function(){
21294         var ds = this.grid.dataSource, i, v = this.grid.view;
21295         var s = this.selections;
21296         s.each(function(r){
21297             if((i = ds.indexOfId(r.id)) != -1){
21298                 v.onRowSelect(i);
21299             }else{
21300                 s.remove(r);
21301             }
21302         });
21303     },
21304
21305     // private
21306     onRemove : function(v, index, r){
21307         this.selections.remove(r);
21308     },
21309
21310     // private
21311     onRowUpdated : function(v, index, r){
21312         if(this.isSelected(r)){
21313             v.onRowSelect(index);
21314         }
21315     },
21316
21317     /**
21318      * Select records.
21319      * @param {Array} records The records to select
21320      * @param {Boolean} keepExisting (optional) True to keep existing selections
21321      */
21322     selectRecords : function(records, keepExisting){
21323         if(!keepExisting){
21324             this.clearSelections();
21325         }
21326         var ds = this.grid.dataSource;
21327         for(var i = 0, len = records.length; i < len; i++){
21328             this.selectRow(ds.indexOf(records[i]), true);
21329         }
21330     },
21331
21332     /**
21333      * Gets the number of selected rows.
21334      * @return {Number}
21335      */
21336     getCount : function(){
21337         return this.selections.length;
21338     },
21339
21340     /**
21341      * Selects the first row in the grid.
21342      */
21343     selectFirstRow : function(){
21344         this.selectRow(0);
21345     },
21346
21347     /**
21348      * Select the last row.
21349      * @param {Boolean} keepExisting (optional) True to keep existing selections
21350      */
21351     selectLastRow : function(keepExisting){
21352         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21353     },
21354
21355     /**
21356      * Selects the row immediately following the last selected row.
21357      * @param {Boolean} keepExisting (optional) True to keep existing selections
21358      */
21359     selectNext : function(keepExisting){
21360         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21361             this.selectRow(this.last+1, keepExisting);
21362             this.grid.getView().focusRow(this.last);
21363         }
21364     },
21365
21366     /**
21367      * Selects the row that precedes the last selected row.
21368      * @param {Boolean} keepExisting (optional) True to keep existing selections
21369      */
21370     selectPrevious : function(keepExisting){
21371         if(this.last){
21372             this.selectRow(this.last-1, keepExisting);
21373             this.grid.getView().focusRow(this.last);
21374         }
21375     },
21376
21377     /**
21378      * Returns the selected records
21379      * @return {Array} Array of selected records
21380      */
21381     getSelections : function(){
21382         return [].concat(this.selections.items);
21383     },
21384
21385     /**
21386      * Returns the first selected record.
21387      * @return {Record}
21388      */
21389     getSelected : function(){
21390         return this.selections.itemAt(0);
21391     },
21392
21393
21394     /**
21395      * Clears all selections.
21396      */
21397     clearSelections : function(fast){
21398         if(this.locked) return;
21399         if(fast !== true){
21400             var ds = this.grid.dataSource;
21401             var s = this.selections;
21402             s.each(function(r){
21403                 this.deselectRow(ds.indexOfId(r.id));
21404             }, this);
21405             s.clear();
21406         }else{
21407             this.selections.clear();
21408         }
21409         this.last = false;
21410     },
21411
21412
21413     /**
21414      * Selects all rows.
21415      */
21416     selectAll : function(){
21417         if(this.locked) return;
21418         this.selections.clear();
21419         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21420             this.selectRow(i, true);
21421         }
21422     },
21423
21424     /**
21425      * Returns True if there is a selection.
21426      * @return {Boolean}
21427      */
21428     hasSelection : function(){
21429         return this.selections.length > 0;
21430     },
21431
21432     /**
21433      * Returns True if the specified row is selected.
21434      * @param {Number/Record} record The record or index of the record to check
21435      * @return {Boolean}
21436      */
21437     isSelected : function(index){
21438         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21439         return (r && this.selections.key(r.id) ? true : false);
21440     },
21441
21442     /**
21443      * Returns True if the specified record id is selected.
21444      * @param {String} id The id of record to check
21445      * @return {Boolean}
21446      */
21447     isIdSelected : function(id){
21448         return (this.selections.key(id) ? true : false);
21449     },
21450
21451     // private
21452     handleMouseDown : function(e, t){
21453         var view = this.grid.getView(), rowIndex;
21454         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21455             return;
21456         };
21457         if(e.shiftKey && this.last !== false){
21458             var last = this.last;
21459             this.selectRange(last, rowIndex, e.ctrlKey);
21460             this.last = last; // reset the last
21461             view.focusRow(rowIndex);
21462         }else{
21463             var isSelected = this.isSelected(rowIndex);
21464             if(e.button !== 0 && isSelected){
21465                 view.focusRow(rowIndex);
21466             }else if(e.ctrlKey && isSelected){
21467                 this.deselectRow(rowIndex);
21468             }else if(!isSelected){
21469                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21470                 view.focusRow(rowIndex);
21471             }
21472         }
21473         this.fireEvent("afterselectionchange", this);
21474     },
21475     // private
21476     handleDragableRowClick :  function(grid, rowIndex, e) 
21477     {
21478         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21479             this.selectRow(rowIndex, false);
21480             grid.view.focusRow(rowIndex);
21481              this.fireEvent("afterselectionchange", this);
21482         }
21483     },
21484     
21485     /**
21486      * Selects multiple rows.
21487      * @param {Array} rows Array of the indexes of the row to select
21488      * @param {Boolean} keepExisting (optional) True to keep existing selections
21489      */
21490     selectRows : function(rows, keepExisting){
21491         if(!keepExisting){
21492             this.clearSelections();
21493         }
21494         for(var i = 0, len = rows.length; i < len; i++){
21495             this.selectRow(rows[i], true);
21496         }
21497     },
21498
21499     /**
21500      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21501      * @param {Number} startRow The index of the first row in the range
21502      * @param {Number} endRow The index of the last row in the range
21503      * @param {Boolean} keepExisting (optional) True to retain existing selections
21504      */
21505     selectRange : function(startRow, endRow, keepExisting){
21506         if(this.locked) return;
21507         if(!keepExisting){
21508             this.clearSelections();
21509         }
21510         if(startRow <= endRow){
21511             for(var i = startRow; i <= endRow; i++){
21512                 this.selectRow(i, true);
21513             }
21514         }else{
21515             for(var i = startRow; i >= endRow; i--){
21516                 this.selectRow(i, true);
21517             }
21518         }
21519     },
21520
21521     /**
21522      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21523      * @param {Number} startRow The index of the first row in the range
21524      * @param {Number} endRow The index of the last row in the range
21525      */
21526     deselectRange : function(startRow, endRow, preventViewNotify){
21527         if(this.locked) return;
21528         for(var i = startRow; i <= endRow; i++){
21529             this.deselectRow(i, preventViewNotify);
21530         }
21531     },
21532
21533     /**
21534      * Selects a row.
21535      * @param {Number} row The index of the row to select
21536      * @param {Boolean} keepExisting (optional) True to keep existing selections
21537      */
21538     selectRow : function(index, keepExisting, preventViewNotify){
21539         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21540         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21541             if(!keepExisting || this.singleSelect){
21542                 this.clearSelections();
21543             }
21544             var r = this.grid.dataSource.getAt(index);
21545             this.selections.add(r);
21546             this.last = this.lastActive = index;
21547             if(!preventViewNotify){
21548                 this.grid.getView().onRowSelect(index);
21549             }
21550             this.fireEvent("rowselect", this, index, r);
21551             this.fireEvent("selectionchange", this);
21552         }
21553     },
21554
21555     /**
21556      * Deselects a row.
21557      * @param {Number} row The index of the row to deselect
21558      */
21559     deselectRow : function(index, preventViewNotify){
21560         if(this.locked) return;
21561         if(this.last == index){
21562             this.last = false;
21563         }
21564         if(this.lastActive == index){
21565             this.lastActive = false;
21566         }
21567         var r = this.grid.dataSource.getAt(index);
21568         this.selections.remove(r);
21569         if(!preventViewNotify){
21570             this.grid.getView().onRowDeselect(index);
21571         }
21572         this.fireEvent("rowdeselect", this, index);
21573         this.fireEvent("selectionchange", this);
21574     },
21575
21576     // private
21577     restoreLast : function(){
21578         if(this._last){
21579             this.last = this._last;
21580         }
21581     },
21582
21583     // private
21584     acceptsNav : function(row, col, cm){
21585         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21586     },
21587
21588     // private
21589     onEditorKey : function(field, e){
21590         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21591         if(k == e.TAB){
21592             e.stopEvent();
21593             ed.completeEdit();
21594             if(e.shiftKey){
21595                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21596             }else{
21597                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21598             }
21599         }else if(k == e.ENTER && !e.ctrlKey){
21600             e.stopEvent();
21601             ed.completeEdit();
21602             if(e.shiftKey){
21603                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21604             }else{
21605                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21606             }
21607         }else if(k == e.ESC){
21608             ed.cancelEdit();
21609         }
21610         if(newCell){
21611             g.startEditing(newCell[0], newCell[1]);
21612         }
21613     }
21614 });/*
21615  * Based on:
21616  * Ext JS Library 1.1.1
21617  * Copyright(c) 2006-2007, Ext JS, LLC.
21618  *
21619  * Originally Released Under LGPL - original licence link has changed is not relivant.
21620  *
21621  * Fork - LGPL
21622  * <script type="text/javascript">
21623  */
21624  
21625 /**
21626  * @class Roo.bootstrap.PagingToolbar
21627  * @extends Roo.Row
21628  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21629  * @constructor
21630  * Create a new PagingToolbar
21631  * @param {Object} config The config object
21632  */
21633 Roo.bootstrap.PagingToolbar = function(config)
21634 {
21635     // old args format still supported... - xtype is prefered..
21636         // created from xtype...
21637     var ds = config.dataSource;
21638     this.toolbarItems = [];
21639     if (config.items) {
21640         this.toolbarItems = config.items;
21641 //        config.items = [];
21642     }
21643     
21644     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21645     this.ds = ds;
21646     this.cursor = 0;
21647     if (ds) { 
21648         this.bind(ds);
21649     }
21650     
21651     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21652     
21653 };
21654
21655 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21656     /**
21657      * @cfg {Roo.data.Store} dataSource
21658      * The underlying data store providing the paged data
21659      */
21660     /**
21661      * @cfg {String/HTMLElement/Element} container
21662      * container The id or element that will contain the toolbar
21663      */
21664     /**
21665      * @cfg {Boolean} displayInfo
21666      * True to display the displayMsg (defaults to false)
21667      */
21668     /**
21669      * @cfg {Number} pageSize
21670      * The number of records to display per page (defaults to 20)
21671      */
21672     pageSize: 20,
21673     /**
21674      * @cfg {String} displayMsg
21675      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21676      */
21677     displayMsg : 'Displaying {0} - {1} of {2}',
21678     /**
21679      * @cfg {String} emptyMsg
21680      * The message to display when no records are found (defaults to "No data to display")
21681      */
21682     emptyMsg : 'No data to display',
21683     /**
21684      * Customizable piece of the default paging text (defaults to "Page")
21685      * @type String
21686      */
21687     beforePageText : "Page",
21688     /**
21689      * Customizable piece of the default paging text (defaults to "of %0")
21690      * @type String
21691      */
21692     afterPageText : "of {0}",
21693     /**
21694      * Customizable piece of the default paging text (defaults to "First Page")
21695      * @type String
21696      */
21697     firstText : "First Page",
21698     /**
21699      * Customizable piece of the default paging text (defaults to "Previous Page")
21700      * @type String
21701      */
21702     prevText : "Previous Page",
21703     /**
21704      * Customizable piece of the default paging text (defaults to "Next Page")
21705      * @type String
21706      */
21707     nextText : "Next Page",
21708     /**
21709      * Customizable piece of the default paging text (defaults to "Last Page")
21710      * @type String
21711      */
21712     lastText : "Last Page",
21713     /**
21714      * Customizable piece of the default paging text (defaults to "Refresh")
21715      * @type String
21716      */
21717     refreshText : "Refresh",
21718
21719     buttons : false,
21720     // private
21721     onRender : function(ct, position) 
21722     {
21723         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21724         this.navgroup.parentId = this.id;
21725         this.navgroup.onRender(this.el, null);
21726         // add the buttons to the navgroup
21727         
21728         if(this.displayInfo){
21729             Roo.log(this.el.select('ul.navbar-nav',true).first());
21730             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21731             this.displayEl = this.el.select('.x-paging-info', true).first();
21732 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21733 //            this.displayEl = navel.el.select('span',true).first();
21734         }
21735         
21736         var _this = this;
21737         
21738         if(this.buttons){
21739             Roo.each(_this.buttons, function(e){
21740                Roo.factory(e).onRender(_this.el, null);
21741             });
21742         }
21743             
21744         Roo.each(_this.toolbarItems, function(e) {
21745             _this.navgroup.addItem(e);
21746         });
21747         
21748         
21749         this.first = this.navgroup.addItem({
21750             tooltip: this.firstText,
21751             cls: "prev",
21752             icon : 'fa fa-backward',
21753             disabled: true,
21754             preventDefault: true,
21755             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21756         });
21757         
21758         this.prev =  this.navgroup.addItem({
21759             tooltip: this.prevText,
21760             cls: "prev",
21761             icon : 'fa fa-step-backward',
21762             disabled: true,
21763             preventDefault: true,
21764             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21765         });
21766     //this.addSeparator();
21767         
21768         
21769         var field = this.navgroup.addItem( {
21770             tagtype : 'span',
21771             cls : 'x-paging-position',
21772             
21773             html : this.beforePageText  +
21774                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21775                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21776          } ); //?? escaped?
21777         
21778         this.field = field.el.select('input', true).first();
21779         this.field.on("keydown", this.onPagingKeydown, this);
21780         this.field.on("focus", function(){this.dom.select();});
21781     
21782     
21783         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21784         //this.field.setHeight(18);
21785         //this.addSeparator();
21786         this.next = this.navgroup.addItem({
21787             tooltip: this.nextText,
21788             cls: "next",
21789             html : ' <i class="fa fa-step-forward">',
21790             disabled: true,
21791             preventDefault: true,
21792             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21793         });
21794         this.last = this.navgroup.addItem({
21795             tooltip: this.lastText,
21796             icon : 'fa fa-forward',
21797             cls: "next",
21798             disabled: true,
21799             preventDefault: true,
21800             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21801         });
21802     //this.addSeparator();
21803         this.loading = this.navgroup.addItem({
21804             tooltip: this.refreshText,
21805             icon: 'fa fa-refresh',
21806             preventDefault: true,
21807             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21808         });
21809
21810     },
21811
21812     // private
21813     updateInfo : function(){
21814         if(this.displayEl){
21815             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21816             var msg = count == 0 ?
21817                 this.emptyMsg :
21818                 String.format(
21819                     this.displayMsg,
21820                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21821                 );
21822             this.displayEl.update(msg);
21823         }
21824     },
21825
21826     // private
21827     onLoad : function(ds, r, o){
21828        this.cursor = o.params ? o.params.start : 0;
21829        var d = this.getPageData(),
21830             ap = d.activePage,
21831             ps = d.pages;
21832         
21833        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21834        this.field.dom.value = ap;
21835        this.first.setDisabled(ap == 1);
21836        this.prev.setDisabled(ap == 1);
21837        this.next.setDisabled(ap == ps);
21838        this.last.setDisabled(ap == ps);
21839        this.loading.enable();
21840        this.updateInfo();
21841     },
21842
21843     // private
21844     getPageData : function(){
21845         var total = this.ds.getTotalCount();
21846         return {
21847             total : total,
21848             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21849             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21850         };
21851     },
21852
21853     // private
21854     onLoadError : function(){
21855         this.loading.enable();
21856     },
21857
21858     // private
21859     onPagingKeydown : function(e){
21860         var k = e.getKey();
21861         var d = this.getPageData();
21862         if(k == e.RETURN){
21863             var v = this.field.dom.value, pageNum;
21864             if(!v || isNaN(pageNum = parseInt(v, 10))){
21865                 this.field.dom.value = d.activePage;
21866                 return;
21867             }
21868             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21869             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21870             e.stopEvent();
21871         }
21872         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))
21873         {
21874           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21875           this.field.dom.value = pageNum;
21876           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21877           e.stopEvent();
21878         }
21879         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21880         {
21881           var v = this.field.dom.value, pageNum; 
21882           var increment = (e.shiftKey) ? 10 : 1;
21883           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21884             increment *= -1;
21885           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21886             this.field.dom.value = d.activePage;
21887             return;
21888           }
21889           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21890           {
21891             this.field.dom.value = parseInt(v, 10) + increment;
21892             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21893             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21894           }
21895           e.stopEvent();
21896         }
21897     },
21898
21899     // private
21900     beforeLoad : function(){
21901         if(this.loading){
21902             this.loading.disable();
21903         }
21904     },
21905
21906     // private
21907     onClick : function(which){
21908         
21909         var ds = this.ds;
21910         if (!ds) {
21911             return;
21912         }
21913         
21914         switch(which){
21915             case "first":
21916                 ds.load({params:{start: 0, limit: this.pageSize}});
21917             break;
21918             case "prev":
21919                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21920             break;
21921             case "next":
21922                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21923             break;
21924             case "last":
21925                 var total = ds.getTotalCount();
21926                 var extra = total % this.pageSize;
21927                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21928                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21929             break;
21930             case "refresh":
21931                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21932             break;
21933         }
21934     },
21935
21936     /**
21937      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21938      * @param {Roo.data.Store} store The data store to unbind
21939      */
21940     unbind : function(ds){
21941         ds.un("beforeload", this.beforeLoad, this);
21942         ds.un("load", this.onLoad, this);
21943         ds.un("loadexception", this.onLoadError, this);
21944         ds.un("remove", this.updateInfo, this);
21945         ds.un("add", this.updateInfo, this);
21946         this.ds = undefined;
21947     },
21948
21949     /**
21950      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21951      * @param {Roo.data.Store} store The data store to bind
21952      */
21953     bind : function(ds){
21954         ds.on("beforeload", this.beforeLoad, this);
21955         ds.on("load", this.onLoad, this);
21956         ds.on("loadexception", this.onLoadError, this);
21957         ds.on("remove", this.updateInfo, this);
21958         ds.on("add", this.updateInfo, this);
21959         this.ds = ds;
21960     }
21961 });/*
21962  * - LGPL
21963  *
21964  * element
21965  * 
21966  */
21967
21968 /**
21969  * @class Roo.bootstrap.MessageBar
21970  * @extends Roo.bootstrap.Component
21971  * Bootstrap MessageBar class
21972  * @cfg {String} html contents of the MessageBar
21973  * @cfg {String} weight (info | success | warning | danger) default info
21974  * @cfg {String} beforeClass insert the bar before the given class
21975  * @cfg {Boolean} closable (true | false) default false
21976  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21977  * 
21978  * @constructor
21979  * Create a new Element
21980  * @param {Object} config The config object
21981  */
21982
21983 Roo.bootstrap.MessageBar = function(config){
21984     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21985 };
21986
21987 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21988     
21989     html: '',
21990     weight: 'info',
21991     closable: false,
21992     fixed: false,
21993     beforeClass: 'bootstrap-sticky-wrap',
21994     
21995     getAutoCreate : function(){
21996         
21997         var cfg = {
21998             tag: 'div',
21999             cls: 'alert alert-dismissable alert-' + this.weight,
22000             cn: [
22001                 {
22002                     tag: 'span',
22003                     cls: 'message',
22004                     html: this.html || ''
22005                 }
22006             ]
22007         }
22008         
22009         if(this.fixed){
22010             cfg.cls += ' alert-messages-fixed';
22011         }
22012         
22013         if(this.closable){
22014             cfg.cn.push({
22015                 tag: 'button',
22016                 cls: 'close',
22017                 html: 'x'
22018             });
22019         }
22020         
22021         return cfg;
22022     },
22023     
22024     onRender : function(ct, position)
22025     {
22026         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22027         
22028         if(!this.el){
22029             var cfg = Roo.apply({},  this.getAutoCreate());
22030             cfg.id = Roo.id();
22031             
22032             if (this.cls) {
22033                 cfg.cls += ' ' + this.cls;
22034             }
22035             if (this.style) {
22036                 cfg.style = this.style;
22037             }
22038             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22039             
22040             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22041         }
22042         
22043         this.el.select('>button.close').on('click', this.hide, this);
22044         
22045     },
22046     
22047     show : function()
22048     {
22049         if (!this.rendered) {
22050             this.render();
22051         }
22052         
22053         this.el.show();
22054         
22055         this.fireEvent('show', this);
22056         
22057     },
22058     
22059     hide : function()
22060     {
22061         if (!this.rendered) {
22062             this.render();
22063         }
22064         
22065         this.el.hide();
22066         
22067         this.fireEvent('hide', this);
22068     },
22069     
22070     update : function()
22071     {
22072 //        var e = this.el.dom.firstChild;
22073 //        
22074 //        if(this.closable){
22075 //            e = e.nextSibling;
22076 //        }
22077 //        
22078 //        e.data = this.html || '';
22079
22080         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22081     }
22082    
22083 });
22084
22085  
22086
22087      /*
22088  * - LGPL
22089  *
22090  * Graph
22091  * 
22092  */
22093
22094
22095 /**
22096  * @class Roo.bootstrap.Graph
22097  * @extends Roo.bootstrap.Component
22098  * Bootstrap Graph class
22099 > Prameters
22100  -sm {number} sm 4
22101  -md {number} md 5
22102  @cfg {String} graphtype  bar | vbar | pie
22103  @cfg {number} g_x coodinator | centre x (pie)
22104  @cfg {number} g_y coodinator | centre y (pie)
22105  @cfg {number} g_r radius (pie)
22106  @cfg {number} g_height height of the chart (respected by all elements in the set)
22107  @cfg {number} g_width width of the chart (respected by all elements in the set)
22108  @cfg {Object} title The title of the chart
22109     
22110  -{Array}  values
22111  -opts (object) options for the chart 
22112      o {
22113      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22114      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22115      o vgutter (number)
22116      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.
22117      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22118      o to
22119      o stretch (boolean)
22120      o }
22121  -opts (object) options for the pie
22122      o{
22123      o cut
22124      o startAngle (number)
22125      o endAngle (number)
22126      } 
22127  *
22128  * @constructor
22129  * Create a new Input
22130  * @param {Object} config The config object
22131  */
22132
22133 Roo.bootstrap.Graph = function(config){
22134     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22135     
22136     this.addEvents({
22137         // img events
22138         /**
22139          * @event click
22140          * The img click event for the img.
22141          * @param {Roo.EventObject} e
22142          */
22143         "click" : true
22144     });
22145 };
22146
22147 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22148     
22149     sm: 4,
22150     md: 5,
22151     graphtype: 'bar',
22152     g_height: 250,
22153     g_width: 400,
22154     g_x: 50,
22155     g_y: 50,
22156     g_r: 30,
22157     opts:{
22158         //g_colors: this.colors,
22159         g_type: 'soft',
22160         g_gutter: '20%'
22161
22162     },
22163     title : false,
22164
22165     getAutoCreate : function(){
22166         
22167         var cfg = {
22168             tag: 'div',
22169             html : null
22170         }
22171         
22172         
22173         return  cfg;
22174     },
22175
22176     onRender : function(ct,position){
22177         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22178         this.raphael = Raphael(this.el.dom);
22179         
22180                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22181                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22182                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22183                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22184                 /*
22185                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22186                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22187                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22188                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22189                 
22190                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22191                 r.barchart(330, 10, 300, 220, data1);
22192                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22193                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22194                 */
22195                 
22196                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22197                 // r.barchart(30, 30, 560, 250,  xdata, {
22198                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22199                 //     axis : "0 0 1 1",
22200                 //     axisxlabels :  xdata
22201                 //     //yvalues : cols,
22202                    
22203                 // });
22204 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22205 //        
22206 //        this.load(null,xdata,{
22207 //                axis : "0 0 1 1",
22208 //                axisxlabels :  xdata
22209 //                });
22210
22211     },
22212
22213     load : function(graphtype,xdata,opts){
22214         this.raphael.clear();
22215         if(!graphtype) {
22216             graphtype = this.graphtype;
22217         }
22218         if(!opts){
22219             opts = this.opts;
22220         }
22221         var r = this.raphael,
22222             fin = function () {
22223                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22224             },
22225             fout = function () {
22226                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22227             },
22228             pfin = function() {
22229                 this.sector.stop();
22230                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22231
22232                 if (this.label) {
22233                     this.label[0].stop();
22234                     this.label[0].attr({ r: 7.5 });
22235                     this.label[1].attr({ "font-weight": 800 });
22236                 }
22237             },
22238             pfout = function() {
22239                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22240
22241                 if (this.label) {
22242                     this.label[0].animate({ r: 5 }, 500, "bounce");
22243                     this.label[1].attr({ "font-weight": 400 });
22244                 }
22245             };
22246
22247         switch(graphtype){
22248             case 'bar':
22249                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22250                 break;
22251             case 'hbar':
22252                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22253                 break;
22254             case 'pie':
22255 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22256 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22257 //            
22258                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22259                 
22260                 break;
22261
22262         }
22263         
22264         if(this.title){
22265             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22266         }
22267         
22268     },
22269     
22270     setTitle: function(o)
22271     {
22272         this.title = o;
22273     },
22274     
22275     initEvents: function() {
22276         
22277         if(!this.href){
22278             this.el.on('click', this.onClick, this);
22279         }
22280     },
22281     
22282     onClick : function(e)
22283     {
22284         Roo.log('img onclick');
22285         this.fireEvent('click', this, e);
22286     }
22287    
22288 });
22289
22290  
22291 /*
22292  * - LGPL
22293  *
22294  * numberBox
22295  * 
22296  */
22297 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22298
22299 /**
22300  * @class Roo.bootstrap.dash.NumberBox
22301  * @extends Roo.bootstrap.Component
22302  * Bootstrap NumberBox class
22303  * @cfg {String} headline Box headline
22304  * @cfg {String} content Box content
22305  * @cfg {String} icon Box icon
22306  * @cfg {String} footer Footer text
22307  * @cfg {String} fhref Footer href
22308  * 
22309  * @constructor
22310  * Create a new NumberBox
22311  * @param {Object} config The config object
22312  */
22313
22314
22315 Roo.bootstrap.dash.NumberBox = function(config){
22316     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22317     
22318 };
22319
22320 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22321     
22322     headline : '',
22323     content : '',
22324     icon : '',
22325     footer : '',
22326     fhref : '',
22327     ficon : '',
22328     
22329     getAutoCreate : function(){
22330         
22331         var cfg = {
22332             tag : 'div',
22333             cls : 'small-box ',
22334             cn : [
22335                 {
22336                     tag : 'div',
22337                     cls : 'inner',
22338                     cn :[
22339                         {
22340                             tag : 'h3',
22341                             cls : 'roo-headline',
22342                             html : this.headline
22343                         },
22344                         {
22345                             tag : 'p',
22346                             cls : 'roo-content',
22347                             html : this.content
22348                         }
22349                     ]
22350                 }
22351             ]
22352         }
22353         
22354         if(this.icon){
22355             cfg.cn.push({
22356                 tag : 'div',
22357                 cls : 'icon',
22358                 cn :[
22359                     {
22360                         tag : 'i',
22361                         cls : 'ion ' + this.icon
22362                     }
22363                 ]
22364             });
22365         }
22366         
22367         if(this.footer){
22368             var footer = {
22369                 tag : 'a',
22370                 cls : 'small-box-footer',
22371                 href : this.fhref || '#',
22372                 html : this.footer
22373             };
22374             
22375             cfg.cn.push(footer);
22376             
22377         }
22378         
22379         return  cfg;
22380     },
22381
22382     onRender : function(ct,position){
22383         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22384
22385
22386        
22387                 
22388     },
22389
22390     setHeadline: function (value)
22391     {
22392         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22393     },
22394     
22395     setFooter: function (value, href)
22396     {
22397         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22398         
22399         if(href){
22400             this.el.select('a.small-box-footer',true).first().attr('href', href);
22401         }
22402         
22403     },
22404
22405     setContent: function (value)
22406     {
22407         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22408     },
22409
22410     initEvents: function() 
22411     {   
22412         
22413     }
22414     
22415 });
22416
22417  
22418 /*
22419  * - LGPL
22420  *
22421  * TabBox
22422  * 
22423  */
22424 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22425
22426 /**
22427  * @class Roo.bootstrap.dash.TabBox
22428  * @extends Roo.bootstrap.Component
22429  * Bootstrap TabBox class
22430  * @cfg {String} title Title of the TabBox
22431  * @cfg {String} icon Icon of the TabBox
22432  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22433  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22434  * 
22435  * @constructor
22436  * Create a new TabBox
22437  * @param {Object} config The config object
22438  */
22439
22440
22441 Roo.bootstrap.dash.TabBox = function(config){
22442     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22443     this.addEvents({
22444         // raw events
22445         /**
22446          * @event addpane
22447          * When a pane is added
22448          * @param {Roo.bootstrap.dash.TabPane} pane
22449          */
22450         "addpane" : true,
22451         /**
22452          * @event activatepane
22453          * When a pane is activated
22454          * @param {Roo.bootstrap.dash.TabPane} pane
22455          */
22456         "activatepane" : true
22457         
22458          
22459     });
22460     
22461     this.panes = [];
22462 };
22463
22464 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22465
22466     title : '',
22467     icon : false,
22468     showtabs : true,
22469     tabScrollable : false,
22470     
22471     getChildContainer : function()
22472     {
22473         return this.el.select('.tab-content', true).first();
22474     },
22475     
22476     getAutoCreate : function(){
22477         
22478         var header = {
22479             tag: 'li',
22480             cls: 'pull-left header',
22481             html: this.title,
22482             cn : []
22483         };
22484         
22485         if(this.icon){
22486             header.cn.push({
22487                 tag: 'i',
22488                 cls: 'fa ' + this.icon
22489             });
22490         }
22491         
22492         var h = {
22493             tag: 'ul',
22494             cls: 'nav nav-tabs pull-right',
22495             cn: [
22496                 header
22497             ]
22498         };
22499         
22500         if(this.tabScrollable){
22501             h = {
22502                 tag: 'div',
22503                 cls: 'tab-header',
22504                 cn: [
22505                     {
22506                         tag: 'ul',
22507                         cls: 'nav nav-tabs pull-right',
22508                         cn: [
22509                             header
22510                         ]
22511                     }
22512                 ]
22513             }
22514         }
22515         
22516         var cfg = {
22517             tag: 'div',
22518             cls: 'nav-tabs-custom',
22519             cn: [
22520                 h,
22521                 {
22522                     tag: 'div',
22523                     cls: 'tab-content no-padding',
22524                     cn: []
22525                 }
22526             ]
22527         }
22528
22529         return  cfg;
22530     },
22531     initEvents : function()
22532     {
22533         //Roo.log('add add pane handler');
22534         this.on('addpane', this.onAddPane, this);
22535     },
22536      /**
22537      * Updates the box title
22538      * @param {String} html to set the title to.
22539      */
22540     setTitle : function(value)
22541     {
22542         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22543     },
22544     onAddPane : function(pane)
22545     {
22546         this.panes.push(pane);
22547         //Roo.log('addpane');
22548         //Roo.log(pane);
22549         // tabs are rendere left to right..
22550         if(!this.showtabs){
22551             return;
22552         }
22553         
22554         var ctr = this.el.select('.nav-tabs', true).first();
22555          
22556          
22557         var existing = ctr.select('.nav-tab',true);
22558         var qty = existing.getCount();;
22559         
22560         
22561         var tab = ctr.createChild({
22562             tag : 'li',
22563             cls : 'nav-tab' + (qty ? '' : ' active'),
22564             cn : [
22565                 {
22566                     tag : 'a',
22567                     href:'#',
22568                     html : pane.title
22569                 }
22570             ]
22571         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22572         pane.tab = tab;
22573         
22574         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22575         if (!qty) {
22576             pane.el.addClass('active');
22577         }
22578         
22579                 
22580     },
22581     onTabClick : function(ev,un,ob,pane)
22582     {
22583         //Roo.log('tab - prev default');
22584         ev.preventDefault();
22585         
22586         
22587         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22588         pane.tab.addClass('active');
22589         //Roo.log(pane.title);
22590         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22591         // technically we should have a deactivate event.. but maybe add later.
22592         // and it should not de-activate the selected tab...
22593         this.fireEvent('activatepane', pane);
22594         pane.el.addClass('active');
22595         pane.fireEvent('activate');
22596         
22597         
22598     },
22599     
22600     getActivePane : function()
22601     {
22602         var r = false;
22603         Roo.each(this.panes, function(p) {
22604             if(p.el.hasClass('active')){
22605                 r = p;
22606                 return false;
22607             }
22608             
22609             return;
22610         });
22611         
22612         return r;
22613     }
22614     
22615     
22616 });
22617
22618  
22619 /*
22620  * - LGPL
22621  *
22622  * Tab pane
22623  * 
22624  */
22625 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22626 /**
22627  * @class Roo.bootstrap.TabPane
22628  * @extends Roo.bootstrap.Component
22629  * Bootstrap TabPane class
22630  * @cfg {Boolean} active (false | true) Default false
22631  * @cfg {String} title title of panel
22632
22633  * 
22634  * @constructor
22635  * Create a new TabPane
22636  * @param {Object} config The config object
22637  */
22638
22639 Roo.bootstrap.dash.TabPane = function(config){
22640     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22641     
22642     this.addEvents({
22643         // raw events
22644         /**
22645          * @event activate
22646          * When a pane is activated
22647          * @param {Roo.bootstrap.dash.TabPane} pane
22648          */
22649         "activate" : true
22650          
22651     });
22652 };
22653
22654 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22655     
22656     active : false,
22657     title : '',
22658     
22659     // the tabBox that this is attached to.
22660     tab : false,
22661      
22662     getAutoCreate : function() 
22663     {
22664         var cfg = {
22665             tag: 'div',
22666             cls: 'tab-pane'
22667         }
22668         
22669         if(this.active){
22670             cfg.cls += ' active';
22671         }
22672         
22673         return cfg;
22674     },
22675     initEvents  : function()
22676     {
22677         //Roo.log('trigger add pane handler');
22678         this.parent().fireEvent('addpane', this)
22679     },
22680     
22681      /**
22682      * Updates the tab title 
22683      * @param {String} html to set the title to.
22684      */
22685     setTitle: function(str)
22686     {
22687         if (!this.tab) {
22688             return;
22689         }
22690         this.title = str;
22691         this.tab.select('a', true).first().dom.innerHTML = str;
22692         
22693     }
22694     
22695     
22696     
22697 });
22698
22699  
22700
22701
22702  /*
22703  * - LGPL
22704  *
22705  * menu
22706  * 
22707  */
22708 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22709
22710 /**
22711  * @class Roo.bootstrap.menu.Menu
22712  * @extends Roo.bootstrap.Component
22713  * Bootstrap Menu class - container for Menu
22714  * @cfg {String} html Text of the menu
22715  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22716  * @cfg {String} icon Font awesome icon
22717  * @cfg {String} pos Menu align to (top | bottom) default bottom
22718  * 
22719  * 
22720  * @constructor
22721  * Create a new Menu
22722  * @param {Object} config The config object
22723  */
22724
22725
22726 Roo.bootstrap.menu.Menu = function(config){
22727     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22728     
22729     this.addEvents({
22730         /**
22731          * @event beforeshow
22732          * Fires before this menu is displayed
22733          * @param {Roo.bootstrap.menu.Menu} this
22734          */
22735         beforeshow : true,
22736         /**
22737          * @event beforehide
22738          * Fires before this menu is hidden
22739          * @param {Roo.bootstrap.menu.Menu} this
22740          */
22741         beforehide : true,
22742         /**
22743          * @event show
22744          * Fires after this menu is displayed
22745          * @param {Roo.bootstrap.menu.Menu} this
22746          */
22747         show : true,
22748         /**
22749          * @event hide
22750          * Fires after this menu is hidden
22751          * @param {Roo.bootstrap.menu.Menu} this
22752          */
22753         hide : true,
22754         /**
22755          * @event click
22756          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22757          * @param {Roo.bootstrap.menu.Menu} this
22758          * @param {Roo.EventObject} e
22759          */
22760         click : true
22761     });
22762     
22763 };
22764
22765 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22766     
22767     submenu : false,
22768     html : '',
22769     weight : 'default',
22770     icon : false,
22771     pos : 'bottom',
22772     
22773     
22774     getChildContainer : function() {
22775         if(this.isSubMenu){
22776             return this.el;
22777         }
22778         
22779         return this.el.select('ul.dropdown-menu', true).first();  
22780     },
22781     
22782     getAutoCreate : function()
22783     {
22784         var text = [
22785             {
22786                 tag : 'span',
22787                 cls : 'roo-menu-text',
22788                 html : this.html
22789             }
22790         ];
22791         
22792         if(this.icon){
22793             text.unshift({
22794                 tag : 'i',
22795                 cls : 'fa ' + this.icon
22796             })
22797         }
22798         
22799         
22800         var cfg = {
22801             tag : 'div',
22802             cls : 'btn-group',
22803             cn : [
22804                 {
22805                     tag : 'button',
22806                     cls : 'dropdown-button btn btn-' + this.weight,
22807                     cn : text
22808                 },
22809                 {
22810                     tag : 'button',
22811                     cls : 'dropdown-toggle btn btn-' + this.weight,
22812                     cn : [
22813                         {
22814                             tag : 'span',
22815                             cls : 'caret'
22816                         }
22817                     ]
22818                 },
22819                 {
22820                     tag : 'ul',
22821                     cls : 'dropdown-menu'
22822                 }
22823             ]
22824             
22825         };
22826         
22827         if(this.pos == 'top'){
22828             cfg.cls += ' dropup';
22829         }
22830         
22831         if(this.isSubMenu){
22832             cfg = {
22833                 tag : 'ul',
22834                 cls : 'dropdown-menu'
22835             }
22836         }
22837         
22838         return cfg;
22839     },
22840     
22841     onRender : function(ct, position)
22842     {
22843         this.isSubMenu = ct.hasClass('dropdown-submenu');
22844         
22845         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22846     },
22847     
22848     initEvents : function() 
22849     {
22850         if(this.isSubMenu){
22851             return;
22852         }
22853         
22854         this.hidden = true;
22855         
22856         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22857         this.triggerEl.on('click', this.onTriggerPress, this);
22858         
22859         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22860         this.buttonEl.on('click', this.onClick, this);
22861         
22862     },
22863     
22864     list : function()
22865     {
22866         if(this.isSubMenu){
22867             return this.el;
22868         }
22869         
22870         return this.el.select('ul.dropdown-menu', true).first();
22871     },
22872     
22873     onClick : function(e)
22874     {
22875         this.fireEvent("click", this, e);
22876     },
22877     
22878     onTriggerPress  : function(e)
22879     {   
22880         if (this.isVisible()) {
22881             this.hide();
22882         } else {
22883             this.show();
22884         }
22885     },
22886     
22887     isVisible : function(){
22888         return !this.hidden;
22889     },
22890     
22891     show : function()
22892     {
22893         this.fireEvent("beforeshow", this);
22894         
22895         this.hidden = false;
22896         this.el.addClass('open');
22897         
22898         Roo.get(document).on("mouseup", this.onMouseUp, this);
22899         
22900         this.fireEvent("show", this);
22901         
22902         
22903     },
22904     
22905     hide : function()
22906     {
22907         this.fireEvent("beforehide", this);
22908         
22909         this.hidden = true;
22910         this.el.removeClass('open');
22911         
22912         Roo.get(document).un("mouseup", this.onMouseUp);
22913         
22914         this.fireEvent("hide", this);
22915     },
22916     
22917     onMouseUp : function()
22918     {
22919         this.hide();
22920     }
22921     
22922 });
22923
22924  
22925  /*
22926  * - LGPL
22927  *
22928  * menu item
22929  * 
22930  */
22931 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22932
22933 /**
22934  * @class Roo.bootstrap.menu.Item
22935  * @extends Roo.bootstrap.Component
22936  * Bootstrap MenuItem class
22937  * @cfg {Boolean} submenu (true | false) default false
22938  * @cfg {String} html text of the item
22939  * @cfg {String} href the link
22940  * @cfg {Boolean} disable (true | false) default false
22941  * @cfg {Boolean} preventDefault (true | false) default true
22942  * @cfg {String} icon Font awesome icon
22943  * @cfg {String} pos Submenu align to (left | right) default right 
22944  * 
22945  * 
22946  * @constructor
22947  * Create a new Item
22948  * @param {Object} config The config object
22949  */
22950
22951
22952 Roo.bootstrap.menu.Item = function(config){
22953     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22954     this.addEvents({
22955         /**
22956          * @event mouseover
22957          * Fires when the mouse is hovering over this menu
22958          * @param {Roo.bootstrap.menu.Item} this
22959          * @param {Roo.EventObject} e
22960          */
22961         mouseover : true,
22962         /**
22963          * @event mouseout
22964          * Fires when the mouse exits this menu
22965          * @param {Roo.bootstrap.menu.Item} this
22966          * @param {Roo.EventObject} e
22967          */
22968         mouseout : true,
22969         // raw events
22970         /**
22971          * @event click
22972          * The raw click event for the entire grid.
22973          * @param {Roo.EventObject} e
22974          */
22975         click : true
22976     });
22977 };
22978
22979 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22980     
22981     submenu : false,
22982     href : '',
22983     html : '',
22984     preventDefault: true,
22985     disable : false,
22986     icon : false,
22987     pos : 'right',
22988     
22989     getAutoCreate : function()
22990     {
22991         var text = [
22992             {
22993                 tag : 'span',
22994                 cls : 'roo-menu-item-text',
22995                 html : this.html
22996             }
22997         ];
22998         
22999         if(this.icon){
23000             text.unshift({
23001                 tag : 'i',
23002                 cls : 'fa ' + this.icon
23003             })
23004         }
23005         
23006         var cfg = {
23007             tag : 'li',
23008             cn : [
23009                 {
23010                     tag : 'a',
23011                     href : this.href || '#',
23012                     cn : text
23013                 }
23014             ]
23015         };
23016         
23017         if(this.disable){
23018             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23019         }
23020         
23021         if(this.submenu){
23022             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23023             
23024             if(this.pos == 'left'){
23025                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23026             }
23027         }
23028         
23029         return cfg;
23030     },
23031     
23032     initEvents : function() 
23033     {
23034         this.el.on('mouseover', this.onMouseOver, this);
23035         this.el.on('mouseout', this.onMouseOut, this);
23036         
23037         this.el.select('a', true).first().on('click', this.onClick, this);
23038         
23039     },
23040     
23041     onClick : function(e)
23042     {
23043         if(this.preventDefault){
23044             e.preventDefault();
23045         }
23046         
23047         this.fireEvent("click", this, e);
23048     },
23049     
23050     onMouseOver : function(e)
23051     {
23052         if(this.submenu && this.pos == 'left'){
23053             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23054         }
23055         
23056         this.fireEvent("mouseover", this, e);
23057     },
23058     
23059     onMouseOut : function(e)
23060     {
23061         this.fireEvent("mouseout", this, e);
23062     }
23063 });
23064
23065  
23066
23067  /*
23068  * - LGPL
23069  *
23070  * menu separator
23071  * 
23072  */
23073 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23074
23075 /**
23076  * @class Roo.bootstrap.menu.Separator
23077  * @extends Roo.bootstrap.Component
23078  * Bootstrap Separator class
23079  * 
23080  * @constructor
23081  * Create a new Separator
23082  * @param {Object} config The config object
23083  */
23084
23085
23086 Roo.bootstrap.menu.Separator = function(config){
23087     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23088 };
23089
23090 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23091     
23092     getAutoCreate : function(){
23093         var cfg = {
23094             tag : 'li',
23095             cls: 'divider'
23096         };
23097         
23098         return cfg;
23099     }
23100    
23101 });
23102
23103  
23104
23105  /*
23106  * - LGPL
23107  *
23108  * Tooltip
23109  * 
23110  */
23111
23112 /**
23113  * @class Roo.bootstrap.Tooltip
23114  * Bootstrap Tooltip class
23115  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23116  * to determine which dom element triggers the tooltip.
23117  * 
23118  * It needs to add support for additional attributes like tooltip-position
23119  * 
23120  * @constructor
23121  * Create a new Toolti
23122  * @param {Object} config The config object
23123  */
23124
23125 Roo.bootstrap.Tooltip = function(config){
23126     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23127 };
23128
23129 Roo.apply(Roo.bootstrap.Tooltip, {
23130     /**
23131      * @function init initialize tooltip monitoring.
23132      * @static
23133      */
23134     currentEl : false,
23135     currentTip : false,
23136     currentRegion : false,
23137     
23138     //  init : delay?
23139     
23140     init : function()
23141     {
23142         Roo.get(document).on('mouseover', this.enter ,this);
23143         Roo.get(document).on('mouseout', this.leave, this);
23144          
23145         
23146         this.currentTip = new Roo.bootstrap.Tooltip();
23147     },
23148     
23149     enter : function(ev)
23150     {
23151         var dom = ev.getTarget();
23152         
23153         //Roo.log(['enter',dom]);
23154         var el = Roo.fly(dom);
23155         if (this.currentEl) {
23156             //Roo.log(dom);
23157             //Roo.log(this.currentEl);
23158             //Roo.log(this.currentEl.contains(dom));
23159             if (this.currentEl == el) {
23160                 return;
23161             }
23162             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23163                 return;
23164             }
23165
23166         }
23167         
23168         
23169         
23170         if (this.currentTip.el) {
23171             this.currentTip.el.hide(); // force hiding...
23172         }    
23173         //Roo.log(ev);
23174         var bindEl = el;
23175         
23176         // you can not look for children, as if el is the body.. then everythign is the child..
23177         if (!el.attr('tooltip')) { //
23178             if (!el.select("[tooltip]").elements.length) {
23179                 return;
23180             }
23181             // is the mouse over this child...?
23182             bindEl = el.select("[tooltip]").first();
23183             var xy = ev.getXY();
23184             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23185                 //Roo.log("not in region.");
23186                 return;
23187             }
23188             //Roo.log("child element over..");
23189             
23190         }
23191         this.currentEl = bindEl;
23192         this.currentTip.bind(bindEl);
23193         this.currentRegion = Roo.lib.Region.getRegion(dom);
23194         this.currentTip.enter();
23195         
23196     },
23197     leave : function(ev)
23198     {
23199         var dom = ev.getTarget();
23200         //Roo.log(['leave',dom]);
23201         if (!this.currentEl) {
23202             return;
23203         }
23204         
23205         
23206         if (dom != this.currentEl.dom) {
23207             return;
23208         }
23209         var xy = ev.getXY();
23210         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23211             return;
23212         }
23213         // only activate leave if mouse cursor is outside... bounding box..
23214         
23215         
23216         
23217         
23218         if (this.currentTip) {
23219             this.currentTip.leave();
23220         }
23221         //Roo.log('clear currentEl');
23222         this.currentEl = false;
23223         
23224         
23225     },
23226     alignment : {
23227         'left' : ['r-l', [-2,0], 'right'],
23228         'right' : ['l-r', [2,0], 'left'],
23229         'bottom' : ['t-b', [0,2], 'top'],
23230         'top' : [ 'b-t', [0,-2], 'bottom']
23231     }
23232     
23233 });
23234
23235
23236 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23237     
23238     
23239     bindEl : false,
23240     
23241     delay : null, // can be { show : 300 , hide: 500}
23242     
23243     timeout : null,
23244     
23245     hoverState : null, //???
23246     
23247     placement : 'bottom', 
23248     
23249     getAutoCreate : function(){
23250     
23251         var cfg = {
23252            cls : 'tooltip',
23253            role : 'tooltip',
23254            cn : [
23255                 {
23256                     cls : 'tooltip-arrow'
23257                 },
23258                 {
23259                     cls : 'tooltip-inner'
23260                 }
23261            ]
23262         };
23263         
23264         return cfg;
23265     },
23266     bind : function(el)
23267     {
23268         this.bindEl = el;
23269     },
23270       
23271     
23272     enter : function () {
23273        
23274         if (this.timeout != null) {
23275             clearTimeout(this.timeout);
23276         }
23277         
23278         this.hoverState = 'in';
23279          //Roo.log("enter - show");
23280         if (!this.delay || !this.delay.show) {
23281             this.show();
23282             return;
23283         }
23284         var _t = this;
23285         this.timeout = setTimeout(function () {
23286             if (_t.hoverState == 'in') {
23287                 _t.show();
23288             }
23289         }, this.delay.show);
23290     },
23291     leave : function()
23292     {
23293         clearTimeout(this.timeout);
23294     
23295         this.hoverState = 'out';
23296          if (!this.delay || !this.delay.hide) {
23297             this.hide();
23298             return;
23299         }
23300        
23301         var _t = this;
23302         this.timeout = setTimeout(function () {
23303             //Roo.log("leave - timeout");
23304             
23305             if (_t.hoverState == 'out') {
23306                 _t.hide();
23307                 Roo.bootstrap.Tooltip.currentEl = false;
23308             }
23309         }, delay);
23310     },
23311     
23312     show : function ()
23313     {
23314         if (!this.el) {
23315             this.render(document.body);
23316         }
23317         // set content.
23318         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23319         
23320         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23321         
23322         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23323         
23324         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23325         
23326         var placement = typeof this.placement == 'function' ?
23327             this.placement.call(this, this.el, on_el) :
23328             this.placement;
23329             
23330         var autoToken = /\s?auto?\s?/i;
23331         var autoPlace = autoToken.test(placement);
23332         if (autoPlace) {
23333             placement = placement.replace(autoToken, '') || 'top';
23334         }
23335         
23336         //this.el.detach()
23337         //this.el.setXY([0,0]);
23338         this.el.show();
23339         //this.el.dom.style.display='block';
23340         this.el.addClass(placement);
23341         
23342         //this.el.appendTo(on_el);
23343         
23344         var p = this.getPosition();
23345         var box = this.el.getBox();
23346         
23347         if (autoPlace) {
23348             // fixme..
23349         }
23350         var align = Roo.bootstrap.Tooltip.alignment[placement];
23351         this.el.alignTo(this.bindEl, align[0],align[1]);
23352         //var arrow = this.el.select('.arrow',true).first();
23353         //arrow.set(align[2], 
23354         
23355         this.el.addClass('in fade');
23356         this.hoverState = null;
23357         
23358         if (this.el.hasClass('fade')) {
23359             // fade it?
23360         }
23361         
23362     },
23363     hide : function()
23364     {
23365          
23366         if (!this.el) {
23367             return;
23368         }
23369         //this.el.setXY([0,0]);
23370         this.el.removeClass('in');
23371         //this.el.hide();
23372         
23373     }
23374     
23375 });
23376  
23377
23378  /*
23379  * - LGPL
23380  *
23381  * Location Picker
23382  * 
23383  */
23384
23385 /**
23386  * @class Roo.bootstrap.LocationPicker
23387  * @extends Roo.bootstrap.Component
23388  * Bootstrap LocationPicker class
23389  * @cfg {Number} latitude Position when init default 0
23390  * @cfg {Number} longitude Position when init default 0
23391  * @cfg {Number} zoom default 15
23392  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23393  * @cfg {Boolean} mapTypeControl default false
23394  * @cfg {Boolean} disableDoubleClickZoom default false
23395  * @cfg {Boolean} scrollwheel default true
23396  * @cfg {Boolean} streetViewControl default false
23397  * @cfg {Number} radius default 0
23398  * @cfg {String} locationName
23399  * @cfg {Boolean} draggable default true
23400  * @cfg {Boolean} enableAutocomplete default false
23401  * @cfg {Boolean} enableReverseGeocode default true
23402  * @cfg {String} markerTitle
23403  * 
23404  * @constructor
23405  * Create a new LocationPicker
23406  * @param {Object} config The config object
23407  */
23408
23409
23410 Roo.bootstrap.LocationPicker = function(config){
23411     
23412     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23413     
23414     this.addEvents({
23415         /**
23416          * @event initial
23417          * Fires when the picker initialized.
23418          * @param {Roo.bootstrap.LocationPicker} this
23419          * @param {Google Location} location
23420          */
23421         initial : true,
23422         /**
23423          * @event positionchanged
23424          * Fires when the picker position changed.
23425          * @param {Roo.bootstrap.LocationPicker} this
23426          * @param {Google Location} location
23427          */
23428         positionchanged : true,
23429         /**
23430          * @event resize
23431          * Fires when the map resize.
23432          * @param {Roo.bootstrap.LocationPicker} this
23433          */
23434         resize : true,
23435         /**
23436          * @event show
23437          * Fires when the map show.
23438          * @param {Roo.bootstrap.LocationPicker} this
23439          */
23440         show : true,
23441         /**
23442          * @event hide
23443          * Fires when the map hide.
23444          * @param {Roo.bootstrap.LocationPicker} this
23445          */
23446         hide : true,
23447         /**
23448          * @event mapClick
23449          * Fires when click the map.
23450          * @param {Roo.bootstrap.LocationPicker} this
23451          * @param {Map event} e
23452          */
23453         mapClick : true,
23454         /**
23455          * @event mapRightClick
23456          * Fires when right click the map.
23457          * @param {Roo.bootstrap.LocationPicker} this
23458          * @param {Map event} e
23459          */
23460         mapRightClick : true,
23461         /**
23462          * @event markerClick
23463          * Fires when click the marker.
23464          * @param {Roo.bootstrap.LocationPicker} this
23465          * @param {Map event} e
23466          */
23467         markerClick : true,
23468         /**
23469          * @event markerRightClick
23470          * Fires when right click the marker.
23471          * @param {Roo.bootstrap.LocationPicker} this
23472          * @param {Map event} e
23473          */
23474         markerRightClick : true,
23475         /**
23476          * @event OverlayViewDraw
23477          * Fires when OverlayView Draw
23478          * @param {Roo.bootstrap.LocationPicker} this
23479          */
23480         OverlayViewDraw : true,
23481         /**
23482          * @event OverlayViewOnAdd
23483          * Fires when OverlayView Draw
23484          * @param {Roo.bootstrap.LocationPicker} this
23485          */
23486         OverlayViewOnAdd : true,
23487         /**
23488          * @event OverlayViewOnRemove
23489          * Fires when OverlayView Draw
23490          * @param {Roo.bootstrap.LocationPicker} this
23491          */
23492         OverlayViewOnRemove : true,
23493         /**
23494          * @event OverlayViewShow
23495          * Fires when OverlayView Draw
23496          * @param {Roo.bootstrap.LocationPicker} this
23497          * @param {Pixel} cpx
23498          */
23499         OverlayViewShow : true,
23500         /**
23501          * @event OverlayViewHide
23502          * Fires when OverlayView Draw
23503          * @param {Roo.bootstrap.LocationPicker} this
23504          */
23505         OverlayViewHide : true
23506     });
23507         
23508 };
23509
23510 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23511     
23512     gMapContext: false,
23513     
23514     latitude: 0,
23515     longitude: 0,
23516     zoom: 15,
23517     mapTypeId: false,
23518     mapTypeControl: false,
23519     disableDoubleClickZoom: false,
23520     scrollwheel: true,
23521     streetViewControl: false,
23522     radius: 0,
23523     locationName: '',
23524     draggable: true,
23525     enableAutocomplete: false,
23526     enableReverseGeocode: true,
23527     markerTitle: '',
23528     
23529     getAutoCreate: function()
23530     {
23531
23532         var cfg = {
23533             tag: 'div',
23534             cls: 'roo-location-picker'
23535         };
23536         
23537         return cfg
23538     },
23539     
23540     initEvents: function(ct, position)
23541     {       
23542         if(!this.el.getWidth() || this.isApplied()){
23543             return;
23544         }
23545         
23546         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23547         
23548         this.initial();
23549     },
23550     
23551     initial: function()
23552     {
23553         if(!this.mapTypeId){
23554             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23555         }
23556         
23557         this.gMapContext = this.GMapContext();
23558         
23559         this.initOverlayView();
23560         
23561         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23562         
23563         var _this = this;
23564                 
23565         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23566             _this.setPosition(_this.gMapContext.marker.position);
23567         });
23568         
23569         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23570             _this.fireEvent('mapClick', this, event);
23571             
23572         });
23573
23574         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23575             _this.fireEvent('mapRightClick', this, event);
23576             
23577         });
23578         
23579         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23580             _this.fireEvent('markerClick', this, event);
23581             
23582         });
23583
23584         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23585             _this.fireEvent('markerRightClick', this, event);
23586             
23587         });
23588         
23589         this.setPosition(this.gMapContext.location);
23590         
23591         this.fireEvent('initial', this, this.gMapContext.location);
23592     },
23593     
23594     initOverlayView: function()
23595     {
23596         var _this = this;
23597         
23598         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23599             
23600             draw: function()
23601             {
23602                 _this.fireEvent('OverlayViewDraw', _this);
23603             },
23604             
23605             onAdd: function()
23606             {
23607                 _this.fireEvent('OverlayViewOnAdd', _this);
23608             },
23609             
23610             onRemove: function()
23611             {
23612                 _this.fireEvent('OverlayViewOnRemove', _this);
23613             },
23614             
23615             show: function(cpx)
23616             {
23617                 _this.fireEvent('OverlayViewShow', _this, cpx);
23618             },
23619             
23620             hide: function()
23621             {
23622                 _this.fireEvent('OverlayViewHide', _this);
23623             }
23624             
23625         });
23626     },
23627     
23628     fromLatLngToContainerPixel: function(event)
23629     {
23630         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23631     },
23632     
23633     isApplied: function() 
23634     {
23635         return this.getGmapContext() == false ? false : true;
23636     },
23637     
23638     getGmapContext: function() 
23639     {
23640         return this.gMapContext
23641     },
23642     
23643     GMapContext: function() 
23644     {
23645         var position = new google.maps.LatLng(this.latitude, this.longitude);
23646         
23647         var _map = new google.maps.Map(this.el.dom, {
23648             center: position,
23649             zoom: this.zoom,
23650             mapTypeId: this.mapTypeId,
23651             mapTypeControl: this.mapTypeControl,
23652             disableDoubleClickZoom: this.disableDoubleClickZoom,
23653             scrollwheel: this.scrollwheel,
23654             streetViewControl: this.streetViewControl,
23655             locationName: this.locationName,
23656             draggable: this.draggable,
23657             enableAutocomplete: this.enableAutocomplete,
23658             enableReverseGeocode: this.enableReverseGeocode
23659         });
23660         
23661         var _marker = new google.maps.Marker({
23662             position: position,
23663             map: _map,
23664             title: this.markerTitle,
23665             draggable: this.draggable
23666         });
23667         
23668         return {
23669             map: _map,
23670             marker: _marker,
23671             circle: null,
23672             location: position,
23673             radius: this.radius,
23674             locationName: this.locationName,
23675             addressComponents: {
23676                 formatted_address: null,
23677                 addressLine1: null,
23678                 addressLine2: null,
23679                 streetName: null,
23680                 streetNumber: null,
23681                 city: null,
23682                 district: null,
23683                 state: null,
23684                 stateOrProvince: null
23685             },
23686             settings: this,
23687             domContainer: this.el.dom,
23688             geodecoder: new google.maps.Geocoder()
23689         };
23690     },
23691     
23692     drawCircle: function(center, radius, options) 
23693     {
23694         if (this.gMapContext.circle != null) {
23695             this.gMapContext.circle.setMap(null);
23696         }
23697         if (radius > 0) {
23698             radius *= 1;
23699             options = Roo.apply({}, options, {
23700                 strokeColor: "#0000FF",
23701                 strokeOpacity: .35,
23702                 strokeWeight: 2,
23703                 fillColor: "#0000FF",
23704                 fillOpacity: .2
23705             });
23706             
23707             options.map = this.gMapContext.map;
23708             options.radius = radius;
23709             options.center = center;
23710             this.gMapContext.circle = new google.maps.Circle(options);
23711             return this.gMapContext.circle;
23712         }
23713         
23714         return null;
23715     },
23716     
23717     setPosition: function(location) 
23718     {
23719         this.gMapContext.location = location;
23720         this.gMapContext.marker.setPosition(location);
23721         this.gMapContext.map.panTo(location);
23722         this.drawCircle(location, this.gMapContext.radius, {});
23723         
23724         var _this = this;
23725         
23726         if (this.gMapContext.settings.enableReverseGeocode) {
23727             this.gMapContext.geodecoder.geocode({
23728                 latLng: this.gMapContext.location
23729             }, function(results, status) {
23730                 
23731                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23732                     _this.gMapContext.locationName = results[0].formatted_address;
23733                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23734                     
23735                     _this.fireEvent('positionchanged', this, location);
23736                 }
23737             });
23738             
23739             return;
23740         }
23741         
23742         this.fireEvent('positionchanged', this, location);
23743     },
23744     
23745     resize: function()
23746     {
23747         google.maps.event.trigger(this.gMapContext.map, "resize");
23748         
23749         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23750         
23751         this.fireEvent('resize', this);
23752     },
23753     
23754     setPositionByLatLng: function(latitude, longitude)
23755     {
23756         this.setPosition(new google.maps.LatLng(latitude, longitude));
23757     },
23758     
23759     getCurrentPosition: function() 
23760     {
23761         return {
23762             latitude: this.gMapContext.location.lat(),
23763             longitude: this.gMapContext.location.lng()
23764         };
23765     },
23766     
23767     getAddressName: function() 
23768     {
23769         return this.gMapContext.locationName;
23770     },
23771     
23772     getAddressComponents: function() 
23773     {
23774         return this.gMapContext.addressComponents;
23775     },
23776     
23777     address_component_from_google_geocode: function(address_components) 
23778     {
23779         var result = {};
23780         
23781         for (var i = 0; i < address_components.length; i++) {
23782             var component = address_components[i];
23783             if (component.types.indexOf("postal_code") >= 0) {
23784                 result.postalCode = component.short_name;
23785             } else if (component.types.indexOf("street_number") >= 0) {
23786                 result.streetNumber = component.short_name;
23787             } else if (component.types.indexOf("route") >= 0) {
23788                 result.streetName = component.short_name;
23789             } else if (component.types.indexOf("neighborhood") >= 0) {
23790                 result.city = component.short_name;
23791             } else if (component.types.indexOf("locality") >= 0) {
23792                 result.city = component.short_name;
23793             } else if (component.types.indexOf("sublocality") >= 0) {
23794                 result.district = component.short_name;
23795             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23796                 result.stateOrProvince = component.short_name;
23797             } else if (component.types.indexOf("country") >= 0) {
23798                 result.country = component.short_name;
23799             }
23800         }
23801         
23802         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23803         result.addressLine2 = "";
23804         return result;
23805     },
23806     
23807     setZoomLevel: function(zoom)
23808     {
23809         this.gMapContext.map.setZoom(zoom);
23810     },
23811     
23812     show: function()
23813     {
23814         if(!this.el){
23815             return;
23816         }
23817         
23818         this.el.show();
23819         
23820         this.resize();
23821         
23822         this.fireEvent('show', this);
23823     },
23824     
23825     hide: function()
23826     {
23827         if(!this.el){
23828             return;
23829         }
23830         
23831         this.el.hide();
23832         
23833         this.fireEvent('hide', this);
23834     }
23835     
23836 });
23837
23838 Roo.apply(Roo.bootstrap.LocationPicker, {
23839     
23840     OverlayView : function(map, options)
23841     {
23842         options = options || {};
23843         
23844         this.setMap(map);
23845     }
23846     
23847     
23848 });/*
23849  * - LGPL
23850  *
23851  * Alert
23852  * 
23853  */
23854
23855 /**
23856  * @class Roo.bootstrap.Alert
23857  * @extends Roo.bootstrap.Component
23858  * Bootstrap Alert class
23859  * @cfg {String} title The title of alert
23860  * @cfg {String} html The content of alert
23861  * @cfg {String} weight (  success | info | warning | danger )
23862  * @cfg {String} faicon font-awesomeicon
23863  * 
23864  * @constructor
23865  * Create a new alert
23866  * @param {Object} config The config object
23867  */
23868
23869
23870 Roo.bootstrap.Alert = function(config){
23871     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23872     
23873 };
23874
23875 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23876     
23877     title: '',
23878     html: '',
23879     weight: false,
23880     faicon: false,
23881     
23882     getAutoCreate : function()
23883     {
23884         
23885         var cfg = {
23886             tag : 'div',
23887             cls : 'alert',
23888             cn : [
23889                 {
23890                     tag : 'i',
23891                     cls : 'roo-alert-icon'
23892                     
23893                 },
23894                 {
23895                     tag : 'b',
23896                     cls : 'roo-alert-title',
23897                     html : this.title
23898                 },
23899                 {
23900                     tag : 'span',
23901                     cls : 'roo-alert-text',
23902                     html : this.html
23903                 }
23904             ]
23905         };
23906         
23907         if(this.faicon){
23908             cfg.cn[0].cls += ' fa ' + this.faicon;
23909         }
23910         
23911         if(this.weight){
23912             cfg.cls += ' alert-' + this.weight;
23913         }
23914         
23915         return cfg;
23916     },
23917     
23918     initEvents: function() 
23919     {
23920         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23921     },
23922     
23923     setTitle : function(str)
23924     {
23925         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23926     },
23927     
23928     setText : function(str)
23929     {
23930         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23931     },
23932     
23933     setWeight : function(weight)
23934     {
23935         if(this.weight){
23936             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23937         }
23938         
23939         this.weight = weight;
23940         
23941         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23942     },
23943     
23944     setIcon : function(icon)
23945     {
23946         if(this.faicon){
23947             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23948         }
23949         
23950         this.faicon = icon
23951         
23952         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23953     },
23954     
23955     hide: function() 
23956     {
23957         this.el.hide();   
23958     },
23959     
23960     show: function() 
23961     {  
23962         this.el.show();   
23963     }
23964     
23965 });
23966
23967  
23968 /*
23969 * Licence: LGPL
23970 */
23971
23972 /**
23973  * @class Roo.bootstrap.UploadCropbox
23974  * @extends Roo.bootstrap.Component
23975  * Bootstrap UploadCropbox class
23976  * @cfg {String} emptyText show when image has been loaded
23977  * @cfg {String} rotateNotify show when image too small to rotate
23978  * @cfg {Number} errorTimeout default 3000
23979  * @cfg {Number} minWidth default 300
23980  * @cfg {Number} minHeight default 300
23981  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23982  * 
23983  * @constructor
23984  * Create a new UploadCropbox
23985  * @param {Object} config The config object
23986  */
23987
23988 Roo.bootstrap.UploadCropbox = function(config){
23989     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23990     
23991     this.addEvents({
23992         /**
23993          * @event beforeselectfile
23994          * Fire before select file
23995          * @param {Roo.bootstrap.UploadCropbox} this
23996          */
23997         "beforeselectfile" : true,
23998         /**
23999          * @event initial
24000          * Fire after initEvent
24001          * @param {Roo.bootstrap.UploadCropbox} this
24002          */
24003         "initial" : true,
24004         /**
24005          * @event crop
24006          * Fire after initEvent
24007          * @param {Roo.bootstrap.UploadCropbox} this
24008          * @param {String} data
24009          */
24010         "crop" : true,
24011         /**
24012          * @event prepare
24013          * Fire when preparing the file data
24014          * @param {Roo.bootstrap.UploadCropbox} this
24015          * @param {Object} file
24016          */
24017         "prepare" : true,
24018         /**
24019          * @event exception
24020          * Fire when get exception
24021          * @param {Roo.bootstrap.UploadCropbox} this
24022          * @param {Object} options
24023          */
24024         "exception" : true,
24025         /**
24026          * @event beforeloadcanvas
24027          * Fire before load the canvas
24028          * @param {Roo.bootstrap.UploadCropbox} this
24029          * @param {String} src
24030          */
24031         "beforeloadcanvas" : true,
24032         /**
24033          * @event trash
24034          * Fire when trash image
24035          * @param {Roo.bootstrap.UploadCropbox} this
24036          */
24037         "trash" : true,
24038         /**
24039          * @event download
24040          * Fire when download the image
24041          * @param {Roo.bootstrap.UploadCropbox} this
24042          */
24043         "download" : true,
24044         /**
24045          * @event footerbuttonclick
24046          * Fire when footerbuttonclick
24047          * @param {Roo.bootstrap.UploadCropbox} this
24048          * @param {String} type
24049          */
24050         "footerbuttonclick" : true,
24051         /**
24052          * @event resize
24053          * Fire when resize
24054          * @param {Roo.bootstrap.UploadCropbox} this
24055          */
24056         "resize" : true
24057         
24058     });
24059     
24060     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24061 };
24062
24063 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24064     
24065     emptyText : 'Click to upload image',
24066     rotateNotify : 'Image is too small to rotate',
24067     errorTimeout : 3000,
24068     scale : 0,
24069     baseScale : 1,
24070     rotate : 0,
24071     dragable : false,
24072     pinching : false,
24073     mouseX : 0,
24074     mouseY : 0,
24075     cropData : false,
24076     minWidth : 300,
24077     minHeight : 300,
24078     file : false,
24079     exif : {},
24080     baseRotate : 1,
24081     cropType : 'image/jpeg',
24082     buttons : false,
24083     canvasLoaded : false,
24084     
24085     getAutoCreate : function()
24086     {
24087         var cfg = {
24088             tag : 'div',
24089             cls : 'roo-upload-cropbox',
24090             cn : [
24091                 {
24092                     tag : 'div',
24093                     cls : 'roo-upload-cropbox-body',
24094                     style : 'cursor:pointer',
24095                     cn : [
24096                         {
24097                             tag : 'div',
24098                             cls : 'roo-upload-cropbox-preview'
24099                         },
24100                         {
24101                             tag : 'div',
24102                             cls : 'roo-upload-cropbox-thumb'
24103                         },
24104                         {
24105                             tag : 'div',
24106                             cls : 'roo-upload-cropbox-empty-notify',
24107                             html : this.emptyText
24108                         },
24109                         {
24110                             tag : 'div',
24111                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24112                             html : this.rotateNotify
24113                         }
24114                     ]
24115                 },
24116                 {
24117                     tag : 'div',
24118                     cls : 'roo-upload-cropbox-footer',
24119                     cn : {
24120                         tag : 'div',
24121                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24122                         cn : []
24123                     }
24124                 }
24125             ]
24126         };
24127         
24128         return cfg;
24129     },
24130     
24131     onRender : function(ct, position)
24132     {
24133         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24134         
24135         if (this.buttons.length) {
24136             
24137             Roo.each(this.buttons, function(bb) {
24138                 
24139                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24140                 
24141                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24142                 
24143             }, this);
24144         }
24145     },
24146     
24147     initEvents : function()
24148     {
24149         this.urlAPI = (window.createObjectURL && window) || 
24150                                 (window.URL && URL.revokeObjectURL && URL) || 
24151                                 (window.webkitURL && webkitURL);
24152                         
24153         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24154         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24155         
24156         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24157         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24158         
24159         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24160         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24161         this.thumbEl.hide();
24162         
24163         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24164         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24165         
24166         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24167         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24168         this.errorEl.hide();
24169         
24170         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24171         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24172         this.footerEl.hide();
24173         
24174         this.setThumbBoxSize();
24175         
24176         this.bind();
24177         
24178         this.resize();
24179         
24180         this.fireEvent('initial', this);
24181     },
24182
24183     bind : function()
24184     {
24185         var _this = this;
24186         
24187         window.addEventListener("resize", function() { _this.resize(); } );
24188         
24189         this.bodyEl.on('click', this.beforeSelectFile, this);
24190         
24191         if(Roo.isTouch){
24192             this.bodyEl.on('touchstart', this.onTouchStart, this);
24193             this.bodyEl.on('touchmove', this.onTouchMove, this);
24194             this.bodyEl.on('touchend', this.onTouchEnd, this);
24195         }
24196         
24197         if(!Roo.isTouch){
24198             this.bodyEl.on('mousedown', this.onMouseDown, this);
24199             this.bodyEl.on('mousemove', this.onMouseMove, this);
24200             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24201             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24202             Roo.get(document).on('mouseup', this.onMouseUp, this);
24203         }
24204     },
24205     
24206     reset : function()
24207     {    
24208         this.scale = 0;
24209         this.baseScale = 1;
24210         this.rotate = 0;
24211         this.baseRotate = 1;
24212         this.dragable = false;
24213         this.pinching = false;
24214         this.mouseX = 0;
24215         this.mouseY = 0;
24216         this.cropData = false;
24217         this.notifyEl.dom.innerHTML = this.emptyText;
24218         
24219     },
24220     
24221     resize : function()
24222     {
24223         if(this.fireEvent('resize', this) != false){
24224             this.setThumbBoxPosition();
24225             this.setCanvasPosition();
24226         }
24227     },
24228     
24229     onFooterButtonClick : function(e, el, o, type)
24230     {
24231         switch (type) {
24232             case 'rotate-left' :
24233                 this.onRotateLeft(e);
24234                 break;
24235             case 'rotate-right' :
24236                 this.onRotateRight(e);
24237                 break;
24238             case 'picture' :
24239                 this.beforeSelectFile(e);
24240                 break;
24241             case 'trash' :
24242                 this.trash(e);
24243                 break;
24244             case 'crop' :
24245                 this.crop(e);
24246                 break;
24247             case 'download' :
24248                 this.download(e);
24249                 break;
24250             default :
24251                 break;
24252         }
24253         
24254         this.fireEvent('footerbuttonclick', this, type);
24255     },
24256     
24257     beforeSelectFile : function(e)
24258     {
24259         this.fireEvent('beforeselectfile', this);
24260     },
24261     
24262     trash : function(e)
24263     {
24264         this.fireEvent('trash', this);
24265     },
24266     
24267     download : function(e)
24268     {
24269         this.fireEvent('download', this);
24270     },
24271     
24272     loadCanvas : function(src)
24273     {   
24274         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24275             
24276             this.reset();
24277             
24278             this.imageEl = document.createElement('img');
24279             
24280             var _this = this;
24281             
24282             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24283             
24284             this.imageEl.src = src;
24285         }
24286     },
24287     
24288     onLoadCanvas : function()
24289     {   
24290         this.bodyEl.un('click', this.beforeSelectFile, this);
24291         
24292         this.notifyEl.hide();
24293         this.thumbEl.show();
24294         this.footerEl.show();
24295         
24296         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24297         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24298         
24299         this.setThumbBoxPosition();
24300         this.baseRotateLevel();
24301         this.baseScaleLevel();
24302         
24303         this.draw();
24304         
24305         this.resize();
24306         
24307         this.canvasLoaded = true;
24308         
24309     },
24310     
24311     setCanvasPosition : function()
24312     {   
24313         if(!this.canvasEl){
24314             return;
24315         }
24316         
24317         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24318         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24319         
24320         this.previewEl.setLeft(pw);
24321         this.previewEl.setTop(ph);
24322         
24323     },
24324     
24325     onMouseDown : function(e)
24326     {   
24327         e.stopEvent();
24328         
24329         this.dragable = true;
24330         this.pinching = false;
24331         
24332         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24333         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24334         
24335     },
24336     
24337     onMouseMove : function(e)
24338     {   
24339         e.stopEvent();
24340         
24341         if(!this.canvasLoaded){
24342             return;
24343         }
24344         
24345         if (!this.dragable){
24346             return;
24347         }
24348         
24349         var minX = Math.ceil(this.thumbEl.getLeft(true));
24350         var minY = Math.ceil(this.thumbEl.getTop(true));
24351         
24352         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24353         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24354         
24355         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24356         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24357         
24358         x = x - this.mouseX;
24359         y = y - this.mouseY;
24360         
24361         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24362         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24363         
24364         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24365         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24366         
24367         this.previewEl.setLeft(bgX);
24368         this.previewEl.setTop(bgY);
24369         
24370         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24371         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24372     },
24373     
24374     onMouseUp : function(e)
24375     {   
24376         e.stopEvent();
24377         
24378         this.dragable = false;
24379     },
24380     
24381     onMouseWheel : function(e)
24382     {   
24383         e.stopEvent();
24384         
24385         this.startScale = this.scale;
24386         
24387         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24388         
24389         if(!this.zoomable()){
24390             this.scale = this.startScale;
24391             return;
24392         }
24393         
24394         this.draw();
24395         
24396         return;
24397     },
24398     
24399     zoomable : function()
24400     {
24401         var minScale = this.thumbEl.getWidth() / this.minWidth;
24402         
24403         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24404         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24405         
24406         if(
24407                 (this.rotate == 0 || this.rotate == 180) && 
24408                 (
24409                     width / minScale < this.minWidth || 
24410                     width / minScale > this.imageEl.OriginWidth || 
24411                     height / minScale < this.minHeight || 
24412                     height / minScale > this.imageEl.OriginHeight
24413                 )
24414         ){
24415             return false;
24416         }
24417         
24418         if(
24419                 (this.rotate == 90 || this.rotate == 270) && 
24420                 (
24421                     width / minScale < this.minHeight || 
24422                     width / minScale > this.imageEl.OriginWidth || 
24423                     height / minScale < this.minWidth || 
24424                     height / minScale > this.imageEl.OriginHeight
24425                 )
24426         ){
24427             return false;
24428         }
24429         
24430         return true;
24431         
24432     },
24433     
24434     onRotateLeft : function(e)
24435     {   
24436         var minScale = this.thumbEl.getWidth() / this.minWidth;
24437         
24438         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24439             
24440             var bw = this.canvasEl.width / this.getScaleLevel();
24441             var bh = this.canvasEl.height / this.getScaleLevel();
24442             
24443             this.startScale = this.scale;
24444             
24445             while (this.getScaleLevel() < minScale){
24446             
24447                 this.scale = this.scale + 1;
24448                 
24449                 if(!this.zoomable()){
24450                     break;
24451                 }
24452                 
24453                 if(
24454                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24455                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24456                 ){
24457                     continue;
24458                 }
24459                 
24460                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24461
24462                 this.draw();
24463                 
24464                 return;
24465             }
24466             
24467             this.scale = this.startScale;
24468             
24469             this.onRotateFail();
24470             
24471             return false;
24472         }
24473         
24474         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24475
24476         this.draw();
24477         
24478     },
24479     
24480     onRotateRight : function(e)
24481     {
24482         var minScale = this.thumbEl.getWidth() / this.minWidth;
24483         
24484         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24485             
24486             var bw = this.canvasEl.width / this.getScaleLevel();
24487             var bh = this.canvasEl.height / this.getScaleLevel();
24488             
24489             this.startScale = this.scale;
24490             
24491             while (this.getScaleLevel() < minScale){
24492             
24493                 this.scale = this.scale + 1;
24494                 
24495                 if(!this.zoomable()){
24496                     break;
24497                 }
24498                 
24499                 if(
24500                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24501                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24502                 ){
24503                     continue;
24504                 }
24505                 
24506                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24507
24508                 this.draw();
24509                 
24510                 return;
24511             }
24512             
24513             this.scale = this.startScale;
24514             
24515             this.onRotateFail();
24516             
24517             return false;
24518         }
24519         
24520         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24521
24522         this.draw();
24523     },
24524     
24525     onRotateFail : function()
24526     {
24527         this.errorEl.show(true);
24528         
24529         var _this = this;
24530         
24531         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24532     },
24533     
24534     draw : function()
24535     {
24536         this.previewEl.dom.innerHTML = '';
24537         
24538         var canvasEl = document.createElement("canvas");
24539         
24540         var contextEl = canvasEl.getContext("2d");
24541         
24542         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24543         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24544         var center = this.imageEl.OriginWidth / 2;
24545         
24546         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24547             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24548             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24549             center = this.imageEl.OriginHeight / 2;
24550         }
24551         
24552         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24553         
24554         contextEl.translate(center, center);
24555         contextEl.rotate(this.rotate * Math.PI / 180);
24556
24557         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24558         
24559         this.canvasEl = document.createElement("canvas");
24560         
24561         this.contextEl = this.canvasEl.getContext("2d");
24562         
24563         switch (this.rotate) {
24564             case 0 :
24565                 
24566                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24567                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24568                 
24569                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24570                 
24571                 break;
24572             case 90 : 
24573                 
24574                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24575                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24576                 
24577                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24578                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24579                     break;
24580                 }
24581                 
24582                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24583                 
24584                 break;
24585             case 180 :
24586                 
24587                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24588                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24589                 
24590                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24591                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24592                     break;
24593                 }
24594                 
24595                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24596                 
24597                 break;
24598             case 270 :
24599                 
24600                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24601                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24602         
24603                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24604                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24605                     break;
24606                 }
24607                 
24608                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24609                 
24610                 break;
24611             default : 
24612                 break;
24613         }
24614         
24615         this.previewEl.appendChild(this.canvasEl);
24616         
24617         this.setCanvasPosition();
24618     },
24619     
24620     crop : function()
24621     {
24622         if(!this.canvasLoaded){
24623             return;
24624         }
24625         var canvas = document.createElement("canvas");
24626         
24627         var context = canvas.getContext("2d");
24628         
24629         canvas.width = this.minWidth;
24630         canvas.height = this.minHeight;
24631         
24632         var cropWidth = this.thumbEl.getWidth();
24633         var cropHeight = this.thumbEl.getHeight();
24634         
24635         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24636         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24637         
24638         if(this.canvasEl.width - cropWidth < x){
24639             x = this.canvasEl.width - cropWidth;
24640         }
24641         
24642         if(this.canvasEl.height - cropHeight < y){
24643             y = this.canvasEl.height - cropHeight;
24644         }
24645         
24646         x = x < 0 ? 0 : x;
24647         y = y < 0 ? 0 : y;
24648         
24649         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24650         
24651         this.cropData = canvas.toDataURL(this.cropType);
24652         
24653         this.fireEvent('crop', this, this.cropData);
24654         
24655     },
24656     
24657     setThumbBoxSize : function()
24658     {
24659         var height = 300;
24660         var width = Math.ceil(this.minWidth * height / this.minHeight);
24661         
24662         if(this.minWidth > this.minHeight){
24663             width = 300;
24664             height = Math.ceil(this.minHeight * width / this.minWidth);
24665         }
24666         
24667         this.thumbEl.setStyle({
24668             width : width + 'px',
24669             height : height + 'px'
24670         });
24671
24672         return;
24673             
24674     },
24675     
24676     setThumbBoxPosition : function()
24677     {
24678         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24679         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24680         
24681         this.thumbEl.setLeft(x);
24682         this.thumbEl.setTop(y);
24683         
24684     },
24685     
24686     baseRotateLevel : function()
24687     {
24688         this.baseRotate = 1;
24689         
24690         if(
24691                 typeof(this.exif) != 'undefined' &&
24692                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24693                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24694         ){
24695             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24696         }
24697         
24698         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24699         
24700     },
24701     
24702     baseScaleLevel : function()
24703     {
24704         var width, height;
24705         
24706         if(this.baseRotate == 6 || this.baseRotate == 8){
24707             
24708             width = this.thumbEl.getHeight();
24709             this.baseScale = height / this.imageEl.OriginHeight;
24710             
24711             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24712                 height = this.thumbEl.getWidth();
24713                 this.baseScale = height / this.imageEl.OriginHeight;
24714             }
24715             
24716             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24717                 height = this.thumbEl.getWidth();
24718                 this.baseScale = height / this.imageEl.OriginHeight;
24719                 
24720                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24721                     width = this.thumbEl.getHeight();
24722                     this.baseScale = width / this.imageEl.OriginWidth;
24723                 }
24724             }
24725             
24726             return;
24727         }
24728         
24729         width = this.thumbEl.getWidth();
24730         this.baseScale = width / this.imageEl.OriginWidth;
24731         
24732         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24733             height = this.thumbEl.getHeight();
24734             this.baseScale = height / this.imageEl.OriginHeight;
24735         }
24736         
24737         
24738         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24739             
24740             height = this.thumbEl.getHeight();
24741             this.baseScale = height / this.imageEl.OriginHeight;
24742             
24743             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24744                 width = this.thumbEl.getWidth();
24745                 this.baseScale = width / this.imageEl.OriginWidth;
24746             }
24747             
24748         }
24749         
24750         return;
24751     },
24752     
24753     getScaleLevel : function()
24754     {
24755         return this.baseScale * Math.pow(1.1, this.scale);
24756     },
24757     
24758     onTouchStart : function(e)
24759     {
24760         if(!this.canvasLoaded){
24761             this.beforeSelectFile(e);
24762             return;
24763         }
24764         
24765         var touches = e.browserEvent.touches;
24766         
24767         if(!touches){
24768             return;
24769         }
24770         
24771         if(touches.length == 1){
24772             this.onMouseDown(e);
24773             return;
24774         }
24775         
24776         if(touches.length != 2){
24777             return;
24778         }
24779         
24780         var coords = [];
24781         
24782         for(var i = 0, finger; finger = touches[i]; i++){
24783             coords.push(finger.pageX, finger.pageY);
24784         }
24785         
24786         var x = Math.pow(coords[0] - coords[2], 2);
24787         var y = Math.pow(coords[1] - coords[3], 2);
24788         
24789         this.startDistance = Math.sqrt(x + y);
24790         
24791         this.startScale = this.scale;
24792         
24793         this.pinching = true;
24794         this.dragable = false;
24795         
24796     },
24797     
24798     onTouchMove : function(e)
24799     {
24800         if(!this.pinching && !this.dragable){
24801             return;
24802         }
24803         
24804         var touches = e.browserEvent.touches;
24805         
24806         if(!touches){
24807             return;
24808         }
24809         
24810         if(this.dragable){
24811             this.onMouseMove(e);
24812             return;
24813         }
24814         
24815         var coords = [];
24816         
24817         for(var i = 0, finger; finger = touches[i]; i++){
24818             coords.push(finger.pageX, finger.pageY);
24819         }
24820         
24821         var x = Math.pow(coords[0] - coords[2], 2);
24822         var y = Math.pow(coords[1] - coords[3], 2);
24823         
24824         this.endDistance = Math.sqrt(x + y);
24825         
24826         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24827         
24828         if(!this.zoomable()){
24829             this.scale = this.startScale;
24830             return;
24831         }
24832         
24833         this.draw();
24834         
24835     },
24836     
24837     onTouchEnd : function(e)
24838     {
24839         this.pinching = false;
24840         this.dragable = false;
24841         
24842     },
24843     
24844     prepare : function(input)
24845     {        
24846         this.file = false;
24847         this.exif = {};
24848         
24849         if(typeof(input) === 'string'){
24850             this.loadCanvas(input);
24851             return;
24852         }
24853         
24854         if(!input.files || !input.files[0] || !this.urlAPI){
24855             return;
24856         }
24857         
24858         this.file = input.files[0];
24859         this.cropType = this.file.type;
24860         
24861         var _this = this;
24862         
24863         if(this.fireEvent('prepare', this, this.file) != false){
24864             
24865             var reader = new FileReader();
24866             
24867             reader.onload = function (e) {
24868                 if (e.target.error) {
24869                     Roo.log(e.target.error);
24870                     return;
24871                 }
24872                 
24873                 var buffer = e.target.result,
24874                     dataView = new DataView(buffer),
24875                     offset = 2,
24876                     maxOffset = dataView.byteLength - 4,
24877                     markerBytes,
24878                     markerLength;
24879                 
24880                 if (dataView.getUint16(0) === 0xffd8) {
24881                     while (offset < maxOffset) {
24882                         markerBytes = dataView.getUint16(offset);
24883                         
24884                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24885                             markerLength = dataView.getUint16(offset + 2) + 2;
24886                             if (offset + markerLength > dataView.byteLength) {
24887                                 Roo.log('Invalid meta data: Invalid segment size.');
24888                                 break;
24889                             }
24890                             
24891                             if(markerBytes == 0xffe1){
24892                                 _this.parseExifData(
24893                                     dataView,
24894                                     offset,
24895                                     markerLength
24896                                 );
24897                             }
24898                             
24899                             offset += markerLength;
24900                             
24901                             continue;
24902                         }
24903                         
24904                         break;
24905                     }
24906                     
24907                 }
24908                 
24909                 var url = _this.urlAPI.createObjectURL(_this.file);
24910                 
24911                 _this.loadCanvas(url);
24912                 
24913                 return;
24914             }
24915             
24916             reader.readAsArrayBuffer(this.file);
24917             
24918         }
24919         
24920     },
24921     
24922     parseExifData : function(dataView, offset, length)
24923     {
24924         var tiffOffset = offset + 10,
24925             littleEndian,
24926             dirOffset;
24927     
24928         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24929             // No Exif data, might be XMP data instead
24930             return;
24931         }
24932         
24933         // Check for the ASCII code for "Exif" (0x45786966):
24934         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24935             // No Exif data, might be XMP data instead
24936             return;
24937         }
24938         if (tiffOffset + 8 > dataView.byteLength) {
24939             Roo.log('Invalid Exif data: Invalid segment size.');
24940             return;
24941         }
24942         // Check for the two null bytes:
24943         if (dataView.getUint16(offset + 8) !== 0x0000) {
24944             Roo.log('Invalid Exif data: Missing byte alignment offset.');
24945             return;
24946         }
24947         // Check the byte alignment:
24948         switch (dataView.getUint16(tiffOffset)) {
24949         case 0x4949:
24950             littleEndian = true;
24951             break;
24952         case 0x4D4D:
24953             littleEndian = false;
24954             break;
24955         default:
24956             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24957             return;
24958         }
24959         // Check for the TIFF tag marker (0x002A):
24960         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24961             Roo.log('Invalid Exif data: Missing TIFF marker.');
24962             return;
24963         }
24964         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24965         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24966         
24967         this.parseExifTags(
24968             dataView,
24969             tiffOffset,
24970             tiffOffset + dirOffset,
24971             littleEndian
24972         );
24973     },
24974     
24975     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24976     {
24977         var tagsNumber,
24978             dirEndOffset,
24979             i;
24980         if (dirOffset + 6 > dataView.byteLength) {
24981             Roo.log('Invalid Exif data: Invalid directory offset.');
24982             return;
24983         }
24984         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24985         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24986         if (dirEndOffset + 4 > dataView.byteLength) {
24987             Roo.log('Invalid Exif data: Invalid directory size.');
24988             return;
24989         }
24990         for (i = 0; i < tagsNumber; i += 1) {
24991             this.parseExifTag(
24992                 dataView,
24993                 tiffOffset,
24994                 dirOffset + 2 + 12 * i, // tag offset
24995                 littleEndian
24996             );
24997         }
24998         // Return the offset to the next directory:
24999         return dataView.getUint32(dirEndOffset, littleEndian);
25000     },
25001     
25002     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25003     {
25004         var tag = dataView.getUint16(offset, littleEndian);
25005         
25006         this.exif[tag] = this.getExifValue(
25007             dataView,
25008             tiffOffset,
25009             offset,
25010             dataView.getUint16(offset + 2, littleEndian), // tag type
25011             dataView.getUint32(offset + 4, littleEndian), // tag length
25012             littleEndian
25013         );
25014     },
25015     
25016     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25017     {
25018         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25019             tagSize,
25020             dataOffset,
25021             values,
25022             i,
25023             str,
25024             c;
25025     
25026         if (!tagType) {
25027             Roo.log('Invalid Exif data: Invalid tag type.');
25028             return;
25029         }
25030         
25031         tagSize = tagType.size * length;
25032         // Determine if the value is contained in the dataOffset bytes,
25033         // or if the value at the dataOffset is a pointer to the actual data:
25034         dataOffset = tagSize > 4 ?
25035                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25036         if (dataOffset + tagSize > dataView.byteLength) {
25037             Roo.log('Invalid Exif data: Invalid data offset.');
25038             return;
25039         }
25040         if (length === 1) {
25041             return tagType.getValue(dataView, dataOffset, littleEndian);
25042         }
25043         values = [];
25044         for (i = 0; i < length; i += 1) {
25045             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25046         }
25047         
25048         if (tagType.ascii) {
25049             str = '';
25050             // Concatenate the chars:
25051             for (i = 0; i < values.length; i += 1) {
25052                 c = values[i];
25053                 // Ignore the terminating NULL byte(s):
25054                 if (c === '\u0000') {
25055                     break;
25056                 }
25057                 str += c;
25058             }
25059             return str;
25060         }
25061         return values;
25062     }
25063     
25064 });
25065
25066 Roo.apply(Roo.bootstrap.UploadCropbox, {
25067     tags : {
25068         'Orientation': 0x0112
25069     },
25070     
25071     Orientation: {
25072             1: 0, //'top-left',
25073 //            2: 'top-right',
25074             3: 180, //'bottom-right',
25075 //            4: 'bottom-left',
25076 //            5: 'left-top',
25077             6: 90, //'right-top',
25078 //            7: 'right-bottom',
25079             8: 270 //'left-bottom'
25080     },
25081     
25082     exifTagTypes : {
25083         // byte, 8-bit unsigned int:
25084         1: {
25085             getValue: function (dataView, dataOffset) {
25086                 return dataView.getUint8(dataOffset);
25087             },
25088             size: 1
25089         },
25090         // ascii, 8-bit byte:
25091         2: {
25092             getValue: function (dataView, dataOffset) {
25093                 return String.fromCharCode(dataView.getUint8(dataOffset));
25094             },
25095             size: 1,
25096             ascii: true
25097         },
25098         // short, 16 bit int:
25099         3: {
25100             getValue: function (dataView, dataOffset, littleEndian) {
25101                 return dataView.getUint16(dataOffset, littleEndian);
25102             },
25103             size: 2
25104         },
25105         // long, 32 bit int:
25106         4: {
25107             getValue: function (dataView, dataOffset, littleEndian) {
25108                 return dataView.getUint32(dataOffset, littleEndian);
25109             },
25110             size: 4
25111         },
25112         // rational = two long values, first is numerator, second is denominator:
25113         5: {
25114             getValue: function (dataView, dataOffset, littleEndian) {
25115                 return dataView.getUint32(dataOffset, littleEndian) /
25116                     dataView.getUint32(dataOffset + 4, littleEndian);
25117             },
25118             size: 8
25119         },
25120         // slong, 32 bit signed int:
25121         9: {
25122             getValue: function (dataView, dataOffset, littleEndian) {
25123                 return dataView.getInt32(dataOffset, littleEndian);
25124             },
25125             size: 4
25126         },
25127         // srational, two slongs, first is numerator, second is denominator:
25128         10: {
25129             getValue: function (dataView, dataOffset, littleEndian) {
25130                 return dataView.getInt32(dataOffset, littleEndian) /
25131                     dataView.getInt32(dataOffset + 4, littleEndian);
25132             },
25133             size: 8
25134         }
25135     },
25136     
25137     footer : {
25138         STANDARD : [
25139             {
25140                 tag : 'div',
25141                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25142                 action : 'rotate-left',
25143                 cn : [
25144                     {
25145                         tag : 'button',
25146                         cls : 'btn btn-default',
25147                         html : '<i class="fa fa-undo"></i>'
25148                     }
25149                 ]
25150             },
25151             {
25152                 tag : 'div',
25153                 cls : 'btn-group roo-upload-cropbox-picture',
25154                 action : 'picture',
25155                 cn : [
25156                     {
25157                         tag : 'button',
25158                         cls : 'btn btn-default',
25159                         html : '<i class="fa fa-picture-o"></i>'
25160                     }
25161                 ]
25162             },
25163             {
25164                 tag : 'div',
25165                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25166                 action : 'rotate-right',
25167                 cn : [
25168                     {
25169                         tag : 'button',
25170                         cls : 'btn btn-default',
25171                         html : '<i class="fa fa-repeat"></i>'
25172                     }
25173                 ]
25174             }
25175         ],
25176         DOCUMENT : [
25177             {
25178                 tag : 'div',
25179                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25180                 action : 'rotate-left',
25181                 cn : [
25182                     {
25183                         tag : 'button',
25184                         cls : 'btn btn-default',
25185                         html : '<i class="fa fa-undo"></i>'
25186                     }
25187                 ]
25188             },
25189             {
25190                 tag : 'div',
25191                 cls : 'btn-group roo-upload-cropbox-download',
25192                 action : 'download',
25193                 cn : [
25194                     {
25195                         tag : 'button',
25196                         cls : 'btn btn-default',
25197                         html : '<i class="fa fa-download"></i>'
25198                     }
25199                 ]
25200             },
25201             {
25202                 tag : 'div',
25203                 cls : 'btn-group roo-upload-cropbox-crop',
25204                 action : 'crop',
25205                 cn : [
25206                     {
25207                         tag : 'button',
25208                         cls : 'btn btn-default',
25209                         html : '<i class="fa fa-crop"></i>'
25210                     }
25211                 ]
25212             },
25213             {
25214                 tag : 'div',
25215                 cls : 'btn-group roo-upload-cropbox-trash',
25216                 action : 'trash',
25217                 cn : [
25218                     {
25219                         tag : 'button',
25220                         cls : 'btn btn-default',
25221                         html : '<i class="fa fa-trash"></i>'
25222                     }
25223                 ]
25224             },
25225             {
25226                 tag : 'div',
25227                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25228                 action : 'rotate-right',
25229                 cn : [
25230                     {
25231                         tag : 'button',
25232                         cls : 'btn btn-default',
25233                         html : '<i class="fa fa-repeat"></i>'
25234                     }
25235                 ]
25236             }
25237         ]
25238     }
25239 });
25240
25241 /*
25242 * Licence: LGPL
25243 */
25244
25245 /**
25246  * @class Roo.bootstrap.DocumentManager
25247  * @extends Roo.bootstrap.Component
25248  * Bootstrap DocumentManager class
25249  * @cfg {String} paramName default 'imageUpload'
25250  * @cfg {String} method default POST
25251  * @cfg {String} url action url
25252  * @cfg {Number} boxes number of boxes default 12
25253  * @cfg {Boolean} multiple multiple upload default true
25254  * @cfg {Number} minWidth default 300
25255  * @cfg {Number} minHeight default 300
25256  * @cfg {Number} thumbSize default 300
25257  * @cfg {String} fieldLabel
25258  * @cfg {Number} labelWidth default 4
25259  * @cfg {String} labelAlign (left|top) default left
25260  * 
25261  * @constructor
25262  * Create a new DocumentManager
25263  * @param {Object} config The config object
25264  */
25265
25266 Roo.bootstrap.DocumentManager = function(config){
25267     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25268     
25269     this.addEvents({
25270         /**
25271          * @event initial
25272          * Fire when initial the DocumentManager
25273          * @param {Roo.bootstrap.DocumentManager} this
25274          */
25275         "initial" : true,
25276         /**
25277          * @event inspect
25278          * inspect selected file
25279          * @param {Roo.bootstrap.DocumentManager} this
25280          * @param {File} file
25281          */
25282         "inspect" : true,
25283         /**
25284          * @event exception
25285          * Fire when xhr load exception
25286          * @param {Roo.bootstrap.DocumentManager} this
25287          * @param {XMLHttpRequest} xhr
25288          */
25289         "exception" : true,
25290         /**
25291          * @event prepare
25292          * prepare the form data
25293          * @param {Roo.bootstrap.DocumentManager} this
25294          * @param {Object} formData
25295          */
25296         "prepare" : true,
25297         /**
25298          * @event remove
25299          * Fire when remove the file
25300          * @param {Roo.bootstrap.DocumentManager} this
25301          * @param {Object} file
25302          */
25303         "remove" : true,
25304         /**
25305          * @event refresh
25306          * Fire after refresh the file
25307          * @param {Roo.bootstrap.DocumentManager} this
25308          */
25309         "refresh" : true,
25310         /**
25311          * @event click
25312          * Fire after click the image
25313          * @param {Roo.bootstrap.DocumentManager} this
25314          * @param {Object} file
25315          */
25316         "click" : true
25317         
25318     });
25319 };
25320
25321 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25322     
25323     boxes : 12,
25324     inputName : '',
25325     minWidth : 300,
25326     minHeight : 300,
25327     thumbSize : 300,
25328     multiple : true,
25329     files : [],
25330     method : 'POST',
25331     url : '',
25332     paramName : 'imageUpload',
25333     fieldLabel : '',
25334     labelWidth : 4,
25335     labelAlign : 'left',
25336     
25337     getAutoCreate : function()
25338     {   
25339         var managerWidget = {
25340             tag : 'div',
25341             cls : 'roo-document-manager',
25342             cn : [
25343                 {
25344                     tag : 'input',
25345                     cls : 'roo-document-manager-selector',
25346                     type : 'file'
25347                 },
25348                 {
25349                     tag : 'div',
25350                     cls : 'roo-document-manager-uploader',
25351                     cn : [
25352                         {
25353                             tag : 'div',
25354                             cls : 'roo-document-manager-upload-btn',
25355                             html : '<i class="fa fa-plus"></i>'
25356                         }
25357                     ]
25358                     
25359                 }
25360             ]
25361         };
25362         
25363         var content = [
25364             {
25365                 tag : 'div',
25366                 cls : 'column col-md-12',
25367                 cn : managerWidget
25368             }
25369         ];
25370         
25371         if(this.fieldLabel.length){
25372             
25373             content = [
25374                 {
25375                     tag : 'div',
25376                     cls : 'column col-md-12',
25377                     html : this.fieldLabel
25378                 },
25379                 {
25380                     tag : 'div',
25381                     cls : 'column col-md-12',
25382                     cn : managerWidget
25383                 }
25384             ];
25385
25386             if(this.labelAlign == 'left'){
25387                 content = [
25388                     {
25389                         tag : 'div',
25390                         cls : 'column col-md-' + this.labelWidth,
25391                         html : this.fieldLabel
25392                     },
25393                     {
25394                         tag : 'div',
25395                         cls : 'column col-md-' + (12 - this.labelWidth),
25396                         cn : managerWidget
25397                     }
25398                 ];
25399                 
25400             }
25401         }
25402         
25403         var cfg = {
25404             tag : 'div',
25405             cls : 'row clearfix',
25406             cn : content
25407         };
25408         
25409         return cfg;
25410         
25411     },
25412     
25413     initEvents : function()
25414     {
25415         this.managerEl = this.el.select('.roo-document-manager', true).first();
25416         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25417         
25418         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25419         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25420         this.selectorEl.hide();
25421         
25422         if(this.multiple){
25423             this.selectorEl.attr('multiple', 'multiple');
25424         }
25425         
25426         this.selectorEl.on('change', this.onSelect, this);
25427         
25428         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25429         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25430         
25431         this.uploader.on('click', this.onUpload, this);
25432         
25433         var _this = this;
25434         
25435         window.addEventListener("resize", function() { _this.refresh(); } );
25436         
25437         this.fireEvent('initial', this);
25438     },
25439     
25440     onUpload : function(e)
25441     {
25442         e.preventDefault();
25443         
25444         this.selectorEl.dom.click();
25445         
25446     },
25447     
25448     onSelect : function(e)
25449     {
25450         e.preventDefault();
25451         
25452         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25453             return;
25454         }
25455         
25456         Roo.each(this.selectorEl.dom.files, function(file){
25457             if(this.fireEvent('inspect', this, file) != false){
25458                 this.files.push(file);
25459             }
25460         }, this);
25461         
25462         this.process();
25463         
25464     },
25465     
25466     process : function()
25467     {
25468         this.selectorEl.dom.value = '';
25469         
25470         if(!this.files.length){
25471             return;
25472         }
25473         
25474         if(this.files.length > this.boxes){
25475             this.files = this.files.slice(0, this.boxes);
25476         }
25477         
25478         var xhr = new XMLHttpRequest();
25479         
25480         Roo.each(this.files, function(file, index){
25481             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25482                 return;
25483             }
25484             
25485             file.xhr = xhr;
25486             
25487             this.managerEl.createChild({
25488                 tag : 'div',
25489                 cls : 'roo-document-manager-loading',
25490                 cn : [
25491                     {
25492                         tag : 'div',
25493                         tooltip : file.name,
25494                         cls : 'roo-document-manager-thumb',
25495                         html : '<i class="fa fa-spinner fa-pulse"></i>'
25496                     }
25497                 ]
25498
25499             });
25500             
25501         }, this);
25502         
25503         if(this.files.length > this.boxes - 1 ){
25504             this.uploader.hide();
25505         }
25506         
25507         var headers = {
25508             "Accept": "application/json",
25509             "Cache-Control": "no-cache",
25510             "X-Requested-With": "XMLHttpRequest"
25511         };
25512         
25513         xhr.open(this.method, this.url, true);
25514         
25515         for (var headerName in headers) {
25516             var headerValue = headers[headerName];
25517             if (headerValue) {
25518                 xhr.setRequestHeader(headerName, headerValue);
25519             }
25520         }
25521         
25522         var _this = this;
25523         
25524         xhr.onload = function()
25525         {
25526             _this.xhrOnLoad(xhr);
25527         }
25528         
25529         xhr.onerror = function()
25530         {
25531             _this.xhrOnError(xhr);
25532         }
25533         
25534         var formData = new FormData();
25535
25536         formData.append('returnHTML', 'NO');
25537         
25538         Roo.each(this.files, function(file, index){
25539             
25540             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25541                 return;
25542             }
25543             
25544             formData.append(this.getParamName(index), file, file.name);
25545             
25546         }, this);
25547         
25548         if(this.fireEvent('prepare', this, formData) != false){
25549             xhr.send(formData);
25550         };
25551         
25552     },
25553     
25554     getParamName : function(i)
25555     {
25556         if(!this.multiple){
25557             return this.paramName;
25558         }
25559         
25560         return this.paramName + "_" + i;
25561     },
25562     
25563     refresh : function()
25564     {
25565         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25566             el.remove();
25567         }, this);
25568         
25569         
25570         var files = [];
25571         
25572         Roo.each(this.files, function(file){
25573             
25574             if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25575                 return;
25576             }
25577             
25578             if(file.target){
25579                 files.push(file);
25580                 return;
25581             }
25582             
25583             var previewEl = this.managerEl.createChild({
25584                 tag : 'div',
25585                 cls : 'roo-document-manager-preview',
25586                 cn : [
25587                     {
25588                         tag : 'div',
25589                         tooltip : file.filename,
25590                         cls : 'roo-document-manager-thumb',
25591                         html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25592                     },
25593                     {
25594                         tag : 'button',
25595                         cls : 'close',
25596                         html : 'x'
25597                     }
25598                 ]
25599             });
25600             
25601             var close = previewEl.select('button.close', true).first();
25602             
25603             close.on('click', this.onRemove, this, file);
25604             
25605             file.target = previewEl;
25606             
25607             var image = previewEl.select('img', true).first();
25608             
25609             image.on('click', this.onClick, this, file);
25610             
25611             files.push(file);
25612             
25613             return;
25614             
25615         }, this);
25616         
25617         this.files = files;
25618         
25619         this.uploader.show();
25620         
25621         if(this.files.length > this.boxes - 1){
25622             this.uploader.hide();
25623         }
25624         
25625         Roo.isTouch ? this.closable(false) : this.closable(true);
25626         
25627         this.fireEvent('refresh', this);
25628     },
25629     
25630     onRemove : function(e, el, o)
25631     {
25632         e.preventDefault();
25633         
25634         this.fireEvent('remove', this, o);
25635         
25636     },
25637     
25638     remove : function(o)
25639     {
25640         var files = [];
25641         
25642         Roo.each(this.files, function(file){
25643             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25644                 files.push(file);
25645                 return;
25646             }
25647
25648             o.target.remove();
25649
25650         }, this);
25651         
25652         this.files = files;
25653         
25654         this.refresh();
25655     },
25656     
25657     onClick : function(e, el, o)
25658     {
25659         e.preventDefault();
25660         
25661         this.fireEvent('click', this, o);
25662         
25663     },
25664     
25665     closable : function(closable)
25666     {
25667         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25668             
25669             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25670             
25671             if(closable){
25672                 el.show();
25673                 return;
25674             }
25675             
25676             el.hide();
25677             
25678         }, this);
25679     },
25680     
25681     xhrOnLoad : function(xhr)
25682     {
25683         if (xhr.readyState !== 4) {
25684             this.refresh();
25685             this.fireEvent('exception', this, xhr);
25686             return;
25687         }
25688
25689         var response = Roo.decode(xhr.responseText);
25690         
25691         if(!response.success){
25692             this.refresh();
25693             this.fireEvent('exception', this, xhr);
25694             return;
25695         }
25696         
25697         var i = 0;
25698         
25699         Roo.each(this.files, function(file, index){
25700             
25701             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25702                 return;
25703             }
25704             
25705             this.files[index] = response.data[i];
25706             i++;
25707             
25708             return;
25709             
25710         }, this);
25711         
25712         this.refresh();
25713         
25714     },
25715     
25716     xhrOnError : function()
25717     {
25718         Roo.log('xhr on error');
25719         
25720         var response = Roo.decode(xhr.responseText);
25721           
25722         Roo.log(response);
25723     }
25724     
25725     
25726     
25727 });
25728 /*
25729 * Licence: LGPL
25730 */
25731
25732 /**
25733  * @class Roo.bootstrap.DocumentViewer
25734  * @extends Roo.bootstrap.Component
25735  * Bootstrap DocumentViewer class
25736  * 
25737  * @constructor
25738  * Create a new DocumentViewer
25739  * @param {Object} config The config object
25740  */
25741
25742 Roo.bootstrap.DocumentViewer = function(config){
25743     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25744     
25745     this.addEvents({
25746         /**
25747          * @event initial
25748          * Fire after initEvent
25749          * @param {Roo.bootstrap.DocumentViewer} this
25750          */
25751         "initial" : true,
25752         /**
25753          * @event click
25754          * Fire after click
25755          * @param {Roo.bootstrap.DocumentViewer} this
25756          */
25757         "click" : true,
25758         /**
25759          * @event trash
25760          * Fire after trash button
25761          * @param {Roo.bootstrap.DocumentViewer} this
25762          */
25763         "trash" : true
25764         
25765     });
25766 };
25767
25768 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
25769     
25770     getAutoCreate : function()
25771     {
25772         var cfg = {
25773             tag : 'div',
25774             cls : 'roo-document-viewer',
25775             cn : [
25776                 {
25777                     tag : 'div',
25778                     cls : 'roo-document-viewer-body',
25779                     cn : [
25780                         {
25781                             tag : 'div',
25782                             cls : 'roo-document-viewer-thumb',
25783                             cn : [
25784                                 {
25785                                     tag : 'img',
25786                                     cls : 'roo-document-viewer-image'
25787                                 }
25788                             ]
25789                         }
25790                     ]
25791                 },
25792                 {
25793                     tag : 'div',
25794                     cls : 'roo-document-viewer-footer',
25795                     cn : {
25796                         tag : 'div',
25797                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25798                         cn : [
25799                             {
25800                                 tag : 'div',
25801                                 cls : 'btn-group',
25802                                 cn : [
25803                                     {
25804                                         tag : 'button',
25805                                         cls : 'btn btn-default roo-document-viewer-trash',
25806                                         html : '<i class="fa fa-trash"></i>'
25807                                     }
25808                                 ]
25809                             }
25810                         ]
25811                     }
25812                 }
25813             ]
25814         };
25815         
25816         return cfg;
25817     },
25818     
25819     initEvents : function()
25820     {
25821         
25822         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25823         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25824         
25825         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25826         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25827         
25828         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25829         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25830         
25831         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25832         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25833         
25834         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25835         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25836         
25837         this.bodyEl.on('click', this.onClick, this);
25838         
25839         this.trashBtn.on('click', this.onTrash, this);
25840         
25841     },
25842     
25843     initial : function()
25844     {
25845 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25846         
25847         
25848         this.fireEvent('initial', this);
25849         
25850     },
25851     
25852     onClick : function(e)
25853     {
25854         e.preventDefault();
25855         
25856         this.fireEvent('click', this);
25857     },
25858     
25859     onTrash : function(e)
25860     {
25861         e.preventDefault();
25862         
25863         this.fireEvent('trash', this);
25864     }
25865     
25866 });