roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         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  * 
7482  * 
7483  * 
7484  * @constructor
7485  * Create a new Input
7486  * @param {Object} config The config object
7487  */
7488
7489 Roo.bootstrap.Input = function(config){
7490     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7491    
7492         this.addEvents({
7493             /**
7494              * @event focus
7495              * Fires when this field receives input focus.
7496              * @param {Roo.form.Field} this
7497              */
7498             focus : true,
7499             /**
7500              * @event blur
7501              * Fires when this field loses input focus.
7502              * @param {Roo.form.Field} this
7503              */
7504             blur : true,
7505             /**
7506              * @event specialkey
7507              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7508              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7509              * @param {Roo.form.Field} this
7510              * @param {Roo.EventObject} e The event object
7511              */
7512             specialkey : true,
7513             /**
7514              * @event change
7515              * Fires just before the field blurs if the field value has changed.
7516              * @param {Roo.form.Field} this
7517              * @param {Mixed} newValue The new value
7518              * @param {Mixed} oldValue The original value
7519              */
7520             change : true,
7521             /**
7522              * @event invalid
7523              * Fires after the field has been marked as invalid.
7524              * @param {Roo.form.Field} this
7525              * @param {String} msg The validation message
7526              */
7527             invalid : true,
7528             /**
7529              * @event valid
7530              * Fires after the field has been validated with no errors.
7531              * @param {Roo.form.Field} this
7532              */
7533             valid : true,
7534              /**
7535              * @event keyup
7536              * Fires after the key up
7537              * @param {Roo.form.Field} this
7538              * @param {Roo.EventObject}  e The event Object
7539              */
7540             keyup : true
7541         });
7542 };
7543
7544 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7545      /**
7546      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7547       automatic validation (defaults to "keyup").
7548      */
7549     validationEvent : "keyup",
7550      /**
7551      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7552      */
7553     validateOnBlur : true,
7554     /**
7555      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7556      */
7557     validationDelay : 250,
7558      /**
7559      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7560      */
7561     focusClass : "x-form-focus",  // not needed???
7562     
7563        
7564     /**
7565      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7566      */
7567     invalidClass : "has-warning",
7568     
7569     /**
7570      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7571      */
7572     validClass : "has-success",
7573     
7574     /**
7575      * @cfg {Boolean} hasFeedback (true|false) default true
7576      */
7577     hasFeedback : true,
7578     
7579     /**
7580      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7581      */
7582     invalidFeedbackClass : "glyphicon-warning-sign",
7583     
7584     /**
7585      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7586      */
7587     validFeedbackClass : "glyphicon-ok",
7588     
7589     /**
7590      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7591      */
7592     selectOnFocus : false,
7593     
7594      /**
7595      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7596      */
7597     maskRe : null,
7598        /**
7599      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7600      */
7601     vtype : null,
7602     
7603       /**
7604      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7605      */
7606     disableKeyFilter : false,
7607     
7608        /**
7609      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7610      */
7611     disabled : false,
7612      /**
7613      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7614      */
7615     allowBlank : true,
7616     /**
7617      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7618      */
7619     blankText : "This field is required",
7620     
7621      /**
7622      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7623      */
7624     minLength : 0,
7625     /**
7626      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7627      */
7628     maxLength : Number.MAX_VALUE,
7629     /**
7630      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7631      */
7632     minLengthText : "The minimum length for this field is {0}",
7633     /**
7634      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7635      */
7636     maxLengthText : "The maximum length for this field is {0}",
7637   
7638     
7639     /**
7640      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7641      * If available, this function will be called only after the basic validators all return true, and will be passed the
7642      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7643      */
7644     validator : null,
7645     /**
7646      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7647      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7648      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7649      */
7650     regex : null,
7651     /**
7652      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7653      */
7654     regexText : "",
7655     
7656     autocomplete: false,
7657     
7658     
7659     fieldLabel : '',
7660     inputType : 'text',
7661     
7662     name : false,
7663     placeholder: false,
7664     before : false,
7665     after : false,
7666     size : false,
7667     hasFocus : false,
7668     preventMark: false,
7669     isFormField : true,
7670     value : '',
7671     labelWidth : 2,
7672     labelAlign : false,
7673     readOnly : false,
7674     align : false,
7675     formatedValue : false,
7676     
7677     parentLabelAlign : function()
7678     {
7679         var parent = this;
7680         while (parent.parent()) {
7681             parent = parent.parent();
7682             if (typeof(parent.labelAlign) !='undefined') {
7683                 return parent.labelAlign;
7684             }
7685         }
7686         return 'left';
7687         
7688     },
7689     
7690     getAutoCreate : function(){
7691         
7692         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7693         
7694         var id = Roo.id();
7695         
7696         var cfg = {};
7697         
7698         if(this.inputType != 'hidden'){
7699             cfg.cls = 'form-group' //input-group
7700         }
7701         
7702         var input =  {
7703             tag: 'input',
7704             id : id,
7705             type : this.inputType,
7706             value : this.value,
7707             cls : 'form-control',
7708             placeholder : this.placeholder || '',
7709             autocomplete : this.autocomplete || 'new-password'
7710         };
7711         
7712         
7713         if(this.align){
7714             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7715         }
7716         
7717         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7718             input.maxLength = this.maxLength;
7719         }
7720         
7721         if (this.disabled) {
7722             input.disabled=true;
7723         }
7724         
7725         if (this.readOnly) {
7726             input.readonly=true;
7727         }
7728         
7729         if (this.name) {
7730             input.name = this.name;
7731         }
7732         if (this.size) {
7733             input.cls += ' input-' + this.size;
7734         }
7735         var settings=this;
7736         ['xs','sm','md','lg'].map(function(size){
7737             if (settings[size]) {
7738                 cfg.cls += ' col-' + size + '-' + settings[size];
7739             }
7740         });
7741         
7742         var inputblock = input;
7743         
7744         var feedback = {
7745             tag: 'span',
7746             cls: 'glyphicon form-control-feedback'
7747         };
7748             
7749         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7750             
7751             inputblock = {
7752                 cls : 'has-feedback',
7753                 cn :  [
7754                     input,
7755                     feedback
7756                 ] 
7757             };  
7758         }
7759         
7760         if (this.before || this.after) {
7761             
7762             inputblock = {
7763                 cls : 'input-group',
7764                 cn :  [] 
7765             };
7766             
7767             if (this.before && typeof(this.before) == 'string') {
7768                 
7769                 inputblock.cn.push({
7770                     tag :'span',
7771                     cls : 'roo-input-before input-group-addon',
7772                     html : this.before
7773                 });
7774             }
7775             if (this.before && typeof(this.before) == 'object') {
7776                 this.before = Roo.factory(this.before);
7777                 Roo.log(this.before);
7778                 inputblock.cn.push({
7779                     tag :'span',
7780                     cls : 'roo-input-before input-group-' +
7781                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7782                 });
7783             }
7784             
7785             inputblock.cn.push(input);
7786             
7787             if (this.after && typeof(this.after) == 'string') {
7788                 inputblock.cn.push({
7789                     tag :'span',
7790                     cls : 'roo-input-after input-group-addon',
7791                     html : this.after
7792                 });
7793             }
7794             if (this.after && typeof(this.after) == 'object') {
7795                 this.after = Roo.factory(this.after);
7796                 Roo.log(this.after);
7797                 inputblock.cn.push({
7798                     tag :'span',
7799                     cls : 'roo-input-after input-group-' +
7800                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7801                 });
7802             }
7803             
7804             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7805                 inputblock.cls += ' has-feedback';
7806                 inputblock.cn.push(feedback);
7807             }
7808         };
7809         
7810         if (align ==='left' && this.fieldLabel.length) {
7811                 Roo.log("left and has label");
7812                 cfg.cn = [
7813                     
7814                     {
7815                         tag: 'label',
7816                         'for' :  id,
7817                         cls : 'control-label col-sm-' + this.labelWidth,
7818                         html : this.fieldLabel
7819                         
7820                     },
7821                     {
7822                         cls : "col-sm-" + (12 - this.labelWidth), 
7823                         cn: [
7824                             inputblock
7825                         ]
7826                     }
7827                     
7828                 ];
7829         } else if ( this.fieldLabel.length) {
7830                 Roo.log(" label");
7831                  cfg.cn = [
7832                    
7833                     {
7834                         tag: 'label',
7835                         //cls : 'input-group-addon',
7836                         html : this.fieldLabel
7837                         
7838                     },
7839                     
7840                     inputblock
7841                     
7842                 ];
7843
7844         } else {
7845             
7846                 Roo.log(" no label && no align");
7847                 cfg.cn = [
7848                     
7849                         inputblock
7850                     
7851                 ];
7852                 
7853                 
7854         };
7855         Roo.log('input-parentType: ' + this.parentType);
7856         
7857         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7858            cfg.cls += ' navbar-form';
7859            Roo.log(cfg);
7860         }
7861         
7862         return cfg;
7863         
7864     },
7865     /**
7866      * return the real input element.
7867      */
7868     inputEl: function ()
7869     {
7870         return this.el.select('input.form-control',true).first();
7871     },
7872     
7873     tooltipEl : function()
7874     {
7875         return this.inputEl();
7876     },
7877     
7878     setDisabled : function(v)
7879     {
7880         var i  = this.inputEl().dom;
7881         if (!v) {
7882             i.removeAttribute('disabled');
7883             return;
7884             
7885         }
7886         i.setAttribute('disabled','true');
7887     },
7888     initEvents : function()
7889     {
7890           
7891         this.inputEl().on("keydown" , this.fireKey,  this);
7892         this.inputEl().on("focus", this.onFocus,  this);
7893         this.inputEl().on("blur", this.onBlur,  this);
7894         
7895         this.inputEl().relayEvent('keyup', this);
7896  
7897         // reference to original value for reset
7898         this.originalValue = this.getValue();
7899         //Roo.form.TextField.superclass.initEvents.call(this);
7900         if(this.validationEvent == 'keyup'){
7901             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7902             this.inputEl().on('keyup', this.filterValidation, this);
7903         }
7904         else if(this.validationEvent !== false){
7905             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7906         }
7907         
7908         if(this.selectOnFocus){
7909             this.on("focus", this.preFocus, this);
7910             
7911         }
7912         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7913             this.inputEl().on("keypress", this.filterKeys, this);
7914         }
7915        /* if(this.grow){
7916             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7917             this.el.on("click", this.autoSize,  this);
7918         }
7919         */
7920         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7921             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7922         }
7923         
7924         if (typeof(this.before) == 'object') {
7925             this.before.render(this.el.select('.roo-input-before',true).first());
7926         }
7927         if (typeof(this.after) == 'object') {
7928             this.after.render(this.el.select('.roo-input-after',true).first());
7929         }
7930         
7931         
7932     },
7933     filterValidation : function(e){
7934         if(!e.isNavKeyPress()){
7935             this.validationTask.delay(this.validationDelay);
7936         }
7937     },
7938      /**
7939      * Validates the field value
7940      * @return {Boolean} True if the value is valid, else false
7941      */
7942     validate : function(){
7943         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7944         if(this.disabled || this.validateValue(this.getRawValue())){
7945             this.markValid();
7946             return true;
7947         }
7948         
7949         this.markInvalid();
7950         return false;
7951     },
7952     
7953     
7954     /**
7955      * Validates a value according to the field's validation rules and marks the field as invalid
7956      * if the validation fails
7957      * @param {Mixed} value The value to validate
7958      * @return {Boolean} True if the value is valid, else false
7959      */
7960     validateValue : function(value){
7961         if(value.length < 1)  { // if it's blank
7962             if(this.allowBlank){
7963                 return true;
7964             }
7965             return false;
7966         }
7967         
7968         if(value.length < this.minLength){
7969             return false;
7970         }
7971         if(value.length > this.maxLength){
7972             return false;
7973         }
7974         if(this.vtype){
7975             var vt = Roo.form.VTypes;
7976             if(!vt[this.vtype](value, this)){
7977                 return false;
7978             }
7979         }
7980         if(typeof this.validator == "function"){
7981             var msg = this.validator(value);
7982             if(msg !== true){
7983                 return false;
7984             }
7985         }
7986         
7987         if(this.regex && !this.regex.test(value)){
7988             return false;
7989         }
7990         
7991         return true;
7992     },
7993
7994     
7995     
7996      // private
7997     fireKey : function(e){
7998         //Roo.log('field ' + e.getKey());
7999         if(e.isNavKeyPress()){
8000             this.fireEvent("specialkey", this, e);
8001         }
8002     },
8003     focus : function (selectText){
8004         if(this.rendered){
8005             this.inputEl().focus();
8006             if(selectText === true){
8007                 this.inputEl().dom.select();
8008             }
8009         }
8010         return this;
8011     } ,
8012     
8013     onFocus : function(){
8014         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8015            // this.el.addClass(this.focusClass);
8016         }
8017         if(!this.hasFocus){
8018             this.hasFocus = true;
8019             this.startValue = this.getValue();
8020             this.fireEvent("focus", this);
8021         }
8022     },
8023     
8024     beforeBlur : Roo.emptyFn,
8025
8026     
8027     // private
8028     onBlur : function(){
8029         this.beforeBlur();
8030         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8031             //this.el.removeClass(this.focusClass);
8032         }
8033         this.hasFocus = false;
8034         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8035             this.validate();
8036         }
8037         var v = this.getValue();
8038         if(String(v) !== String(this.startValue)){
8039             this.fireEvent('change', this, v, this.startValue);
8040         }
8041         this.fireEvent("blur", this);
8042     },
8043     
8044     /**
8045      * Resets the current field value to the originally loaded value and clears any validation messages
8046      */
8047     reset : function(){
8048         this.setValue(this.originalValue);
8049         this.validate();
8050     },
8051      /**
8052      * Returns the name of the field
8053      * @return {Mixed} name The name field
8054      */
8055     getName: function(){
8056         return this.name;
8057     },
8058      /**
8059      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8060      * @return {Mixed} value The field value
8061      */
8062     getValue : function(){
8063         
8064         var v = this.inputEl().getValue();
8065         
8066         return v;
8067     },
8068     /**
8069      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8070      * @return {Mixed} value The field value
8071      */
8072     getRawValue : function(){
8073         var v = this.inputEl().getValue();
8074         
8075         return v;
8076     },
8077     
8078     /**
8079      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8080      * @param {Mixed} value The value to set
8081      */
8082     setRawValue : function(v){
8083         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8084     },
8085     
8086     selectText : function(start, end){
8087         var v = this.getRawValue();
8088         if(v.length > 0){
8089             start = start === undefined ? 0 : start;
8090             end = end === undefined ? v.length : end;
8091             var d = this.inputEl().dom;
8092             if(d.setSelectionRange){
8093                 d.setSelectionRange(start, end);
8094             }else if(d.createTextRange){
8095                 var range = d.createTextRange();
8096                 range.moveStart("character", start);
8097                 range.moveEnd("character", v.length-end);
8098                 range.select();
8099             }
8100         }
8101     },
8102     
8103     /**
8104      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8105      * @param {Mixed} value The value to set
8106      */
8107     setValue : function(v){
8108         this.value = v;
8109         if(this.rendered){
8110             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8111             this.validate();
8112         }
8113     },
8114     
8115     /*
8116     processValue : function(value){
8117         if(this.stripCharsRe){
8118             var newValue = value.replace(this.stripCharsRe, '');
8119             if(newValue !== value){
8120                 this.setRawValue(newValue);
8121                 return newValue;
8122             }
8123         }
8124         return value;
8125     },
8126   */
8127     preFocus : function(){
8128         
8129         if(this.selectOnFocus){
8130             this.inputEl().dom.select();
8131         }
8132     },
8133     filterKeys : function(e){
8134         var k = e.getKey();
8135         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8136             return;
8137         }
8138         var c = e.getCharCode(), cc = String.fromCharCode(c);
8139         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8140             return;
8141         }
8142         if(!this.maskRe.test(cc)){
8143             e.stopEvent();
8144         }
8145     },
8146      /**
8147      * Clear any invalid styles/messages for this field
8148      */
8149     clearInvalid : function(){
8150         
8151         if(!this.el || this.preventMark){ // not rendered
8152             return;
8153         }
8154         this.el.removeClass(this.invalidClass);
8155         
8156         this.fireEvent('valid', this);
8157     },
8158     
8159      /**
8160      * Mark this field as valid
8161      */
8162     markValid : function(){
8163         if(!this.el  || this.preventMark){ // not rendered
8164             return;
8165         }
8166         
8167         this.el.removeClass([this.invalidClass, this.validClass]);
8168         
8169         if(this.disabled || this.allowBlank){
8170             return;
8171         }
8172         
8173         this.el.addClass(this.validClass);
8174         
8175         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8176             
8177             var feedback = this.el.select('.form-control-feedback', true).first();
8178             
8179             if(feedback){
8180                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8181                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8182             }
8183             
8184         }
8185         
8186         this.fireEvent('valid', this);
8187     },
8188     
8189      /**
8190      * Mark this field as invalid
8191      * @param {String} msg The validation message
8192      */
8193     markInvalid : function(msg){
8194         if(!this.el  || this.preventMark){ // not rendered
8195             return;
8196         }
8197         
8198         this.el.removeClass([this.invalidClass, this.validClass]);
8199         
8200         if(this.disabled || this.allowBlank){
8201             return;
8202         }
8203         
8204         this.el.addClass(this.invalidClass);
8205         
8206         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8207             
8208             var feedback = this.el.select('.form-control-feedback', true).first();
8209             
8210             if(feedback){
8211                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8212                 
8213                 if(this.getValue().length){
8214                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8215                 }
8216                 
8217             }
8218             
8219         }
8220         
8221         this.fireEvent('invalid', this, msg);
8222     },
8223     // private
8224     SafariOnKeyDown : function(event)
8225     {
8226         // this is a workaround for a password hang bug on chrome/ webkit.
8227         
8228         var isSelectAll = false;
8229         
8230         if(this.inputEl().dom.selectionEnd > 0){
8231             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8232         }
8233         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8234             event.preventDefault();
8235             this.setValue('');
8236             return;
8237         }
8238         
8239         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8240             
8241             event.preventDefault();
8242             // this is very hacky as keydown always get's upper case.
8243             //
8244             var cc = String.fromCharCode(event.getCharCode());
8245             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8246             
8247         }
8248     },
8249     adjustWidth : function(tag, w){
8250         tag = tag.toLowerCase();
8251         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8252             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8253                 if(tag == 'input'){
8254                     return w + 2;
8255                 }
8256                 if(tag == 'textarea'){
8257                     return w-2;
8258                 }
8259             }else if(Roo.isOpera){
8260                 if(tag == 'input'){
8261                     return w + 2;
8262                 }
8263                 if(tag == 'textarea'){
8264                     return w-2;
8265                 }
8266             }
8267         }
8268         return w;
8269     }
8270     
8271 });
8272
8273  
8274 /*
8275  * - LGPL
8276  *
8277  * Input
8278  * 
8279  */
8280
8281 /**
8282  * @class Roo.bootstrap.TextArea
8283  * @extends Roo.bootstrap.Input
8284  * Bootstrap TextArea class
8285  * @cfg {Number} cols Specifies the visible width of a text area
8286  * @cfg {Number} rows Specifies the visible number of lines in a text area
8287  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8288  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8289  * @cfg {string} html text
8290  * 
8291  * @constructor
8292  * Create a new TextArea
8293  * @param {Object} config The config object
8294  */
8295
8296 Roo.bootstrap.TextArea = function(config){
8297     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8298    
8299 };
8300
8301 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8302      
8303     cols : false,
8304     rows : 5,
8305     readOnly : false,
8306     warp : 'soft',
8307     resize : false,
8308     value: false,
8309     html: false,
8310     
8311     getAutoCreate : function(){
8312         
8313         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8314         
8315         var id = Roo.id();
8316         
8317         var cfg = {};
8318         
8319         var input =  {
8320             tag: 'textarea',
8321             id : id,
8322             warp : this.warp,
8323             rows : this.rows,
8324             value : this.value || '',
8325             html: this.html || '',
8326             cls : 'form-control',
8327             placeholder : this.placeholder || '' 
8328             
8329         };
8330         
8331         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8332             input.maxLength = this.maxLength;
8333         }
8334         
8335         if(this.resize){
8336             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8337         }
8338         
8339         if(this.cols){
8340             input.cols = this.cols;
8341         }
8342         
8343         if (this.readOnly) {
8344             input.readonly = true;
8345         }
8346         
8347         if (this.name) {
8348             input.name = this.name;
8349         }
8350         
8351         if (this.size) {
8352             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8353         }
8354         
8355         var settings=this;
8356         ['xs','sm','md','lg'].map(function(size){
8357             if (settings[size]) {
8358                 cfg.cls += ' col-' + size + '-' + settings[size];
8359             }
8360         });
8361         
8362         var inputblock = input;
8363         
8364         if(this.hasFeedback && !this.allowBlank){
8365             
8366             var feedback = {
8367                 tag: 'span',
8368                 cls: 'glyphicon form-control-feedback'
8369             };
8370
8371             inputblock = {
8372                 cls : 'has-feedback',
8373                 cn :  [
8374                     input,
8375                     feedback
8376                 ] 
8377             };  
8378         }
8379         
8380         
8381         if (this.before || this.after) {
8382             
8383             inputblock = {
8384                 cls : 'input-group',
8385                 cn :  [] 
8386             };
8387             if (this.before) {
8388                 inputblock.cn.push({
8389                     tag :'span',
8390                     cls : 'input-group-addon',
8391                     html : this.before
8392                 });
8393             }
8394             
8395             inputblock.cn.push(input);
8396             
8397             if(this.hasFeedback && !this.allowBlank){
8398                 inputblock.cls += ' has-feedback';
8399                 inputblock.cn.push(feedback);
8400             }
8401             
8402             if (this.after) {
8403                 inputblock.cn.push({
8404                     tag :'span',
8405                     cls : 'input-group-addon',
8406                     html : this.after
8407                 });
8408             }
8409             
8410         }
8411         
8412         if (align ==='left' && this.fieldLabel.length) {
8413                 Roo.log("left and has label");
8414                 cfg.cn = [
8415                     
8416                     {
8417                         tag: 'label',
8418                         'for' :  id,
8419                         cls : 'control-label col-sm-' + this.labelWidth,
8420                         html : this.fieldLabel
8421                         
8422                     },
8423                     {
8424                         cls : "col-sm-" + (12 - this.labelWidth), 
8425                         cn: [
8426                             inputblock
8427                         ]
8428                     }
8429                     
8430                 ];
8431         } else if ( this.fieldLabel.length) {
8432                 Roo.log(" label");
8433                  cfg.cn = [
8434                    
8435                     {
8436                         tag: 'label',
8437                         //cls : 'input-group-addon',
8438                         html : this.fieldLabel
8439                         
8440                     },
8441                     
8442                     inputblock
8443                     
8444                 ];
8445
8446         } else {
8447             
8448                    Roo.log(" no label && no align");
8449                 cfg.cn = [
8450                     
8451                         inputblock
8452                     
8453                 ];
8454                 
8455                 
8456         }
8457         
8458         if (this.disabled) {
8459             input.disabled=true;
8460         }
8461         
8462         return cfg;
8463         
8464     },
8465     /**
8466      * return the real textarea element.
8467      */
8468     inputEl: function ()
8469     {
8470         return this.el.select('textarea.form-control',true).first();
8471     }
8472 });
8473
8474  
8475 /*
8476  * - LGPL
8477  *
8478  * trigger field - base class for combo..
8479  * 
8480  */
8481  
8482 /**
8483  * @class Roo.bootstrap.TriggerField
8484  * @extends Roo.bootstrap.Input
8485  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8486  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8487  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8488  * for which you can provide a custom implementation.  For example:
8489  * <pre><code>
8490 var trigger = new Roo.bootstrap.TriggerField();
8491 trigger.onTriggerClick = myTriggerFn;
8492 trigger.applyTo('my-field');
8493 </code></pre>
8494  *
8495  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8496  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8497  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8498  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8499  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8500
8501  * @constructor
8502  * Create a new TriggerField.
8503  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8504  * to the base TextField)
8505  */
8506 Roo.bootstrap.TriggerField = function(config){
8507     this.mimicing = false;
8508     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8509 };
8510
8511 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8512     /**
8513      * @cfg {String} triggerClass A CSS class to apply to the trigger
8514      */
8515      /**
8516      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8517      */
8518     hideTrigger:false,
8519
8520     /**
8521      * @cfg {Boolean} removable (true|false) special filter default false
8522      */
8523     removable : false,
8524     
8525     /** @cfg {Boolean} grow @hide */
8526     /** @cfg {Number} growMin @hide */
8527     /** @cfg {Number} growMax @hide */
8528
8529     /**
8530      * @hide 
8531      * @method
8532      */
8533     autoSize: Roo.emptyFn,
8534     // private
8535     monitorTab : true,
8536     // private
8537     deferHeight : true,
8538
8539     
8540     actionMode : 'wrap',
8541     
8542     caret : false,
8543     
8544     
8545     getAutoCreate : function(){
8546        
8547         var align = this.labelAlign || this.parentLabelAlign();
8548         
8549         var id = Roo.id();
8550         
8551         var cfg = {
8552             cls: 'form-group' //input-group
8553         };
8554         
8555         
8556         var input =  {
8557             tag: 'input',
8558             id : id,
8559             type : this.inputType,
8560             cls : 'form-control',
8561             autocomplete: 'new-password',
8562             placeholder : this.placeholder || '' 
8563             
8564         };
8565         if (this.name) {
8566             input.name = this.name;
8567         }
8568         if (this.size) {
8569             input.cls += ' input-' + this.size;
8570         }
8571         
8572         if (this.disabled) {
8573             input.disabled=true;
8574         }
8575         
8576         var inputblock = input;
8577         
8578         if(this.hasFeedback && !this.allowBlank){
8579             
8580             var feedback = {
8581                 tag: 'span',
8582                 cls: 'glyphicon form-control-feedback'
8583             };
8584             
8585             if(this.removable && !this.editable && !this.tickable){
8586                 inputblock = {
8587                     cls : 'has-feedback',
8588                     cn :  [
8589                         inputblock,
8590                         {
8591                             tag: 'button',
8592                             html : 'x',
8593                             cls : 'roo-combo-removable-btn close'
8594                         },
8595                         feedback
8596                     ] 
8597                 };
8598             } else {
8599                 inputblock = {
8600                     cls : 'has-feedback',
8601                     cn :  [
8602                         inputblock,
8603                         feedback
8604                     ] 
8605                 };
8606             }
8607
8608         } else {
8609             if(this.removable && !this.editable && !this.tickable){
8610                 inputblock = {
8611                     cls : 'roo-removable',
8612                     cn :  [
8613                         inputblock,
8614                         {
8615                             tag: 'button',
8616                             html : 'x',
8617                             cls : 'roo-combo-removable-btn close'
8618                         }
8619                     ] 
8620                 };
8621             }
8622         }
8623         
8624         if (this.before || this.after) {
8625             
8626             inputblock = {
8627                 cls : 'input-group',
8628                 cn :  [] 
8629             };
8630             if (this.before) {
8631                 inputblock.cn.push({
8632                     tag :'span',
8633                     cls : 'input-group-addon',
8634                     html : this.before
8635                 });
8636             }
8637             
8638             inputblock.cn.push(input);
8639             
8640             if(this.hasFeedback && !this.allowBlank){
8641                 inputblock.cls += ' has-feedback';
8642                 inputblock.cn.push(feedback);
8643             }
8644             
8645             if (this.after) {
8646                 inputblock.cn.push({
8647                     tag :'span',
8648                     cls : 'input-group-addon',
8649                     html : this.after
8650                 });
8651             }
8652             
8653         };
8654         
8655         var box = {
8656             tag: 'div',
8657             cn: [
8658                 {
8659                     tag: 'input',
8660                     type : 'hidden',
8661                     cls: 'form-hidden-field'
8662                 },
8663                 inputblock
8664             ]
8665             
8666         };
8667         
8668         if(this.multiple){
8669             Roo.log('multiple');
8670             
8671             box = {
8672                 tag: 'div',
8673                 cn: [
8674                     {
8675                         tag: 'input',
8676                         type : 'hidden',
8677                         cls: 'form-hidden-field'
8678                     },
8679                     {
8680                         tag: 'ul',
8681                         cls: 'select2-choices',
8682                         cn:[
8683                             {
8684                                 tag: 'li',
8685                                 cls: 'select2-search-field',
8686                                 cn: [
8687
8688                                     inputblock
8689                                 ]
8690                             }
8691                         ]
8692                     }
8693                 ]
8694             }
8695         };
8696         
8697         var combobox = {
8698             cls: 'select2-container input-group',
8699             cn: [
8700                 box
8701 //                {
8702 //                    tag: 'ul',
8703 //                    cls: 'typeahead typeahead-long dropdown-menu',
8704 //                    style: 'display:none'
8705 //                }
8706             ]
8707         };
8708         
8709         if(!this.multiple && this.showToggleBtn){
8710             
8711             var caret = {
8712                         tag: 'span',
8713                         cls: 'caret'
8714              };
8715             if (this.caret != false) {
8716                 caret = {
8717                      tag: 'i',
8718                      cls: 'fa fa-' + this.caret
8719                 };
8720                 
8721             }
8722             
8723             combobox.cn.push({
8724                 tag :'span',
8725                 cls : 'input-group-addon btn dropdown-toggle',
8726                 cn : [
8727                     caret,
8728                     {
8729                         tag: 'span',
8730                         cls: 'combobox-clear',
8731                         cn  : [
8732                             {
8733                                 tag : 'i',
8734                                 cls: 'icon-remove'
8735                             }
8736                         ]
8737                     }
8738                 ]
8739
8740             })
8741         }
8742         
8743         if(this.multiple){
8744             combobox.cls += ' select2-container-multi';
8745         }
8746         
8747         if (align ==='left' && this.fieldLabel.length) {
8748             
8749                 Roo.log("left and has label");
8750                 cfg.cn = [
8751                     
8752                     {
8753                         tag: 'label',
8754                         'for' :  id,
8755                         cls : 'control-label col-sm-' + this.labelWidth,
8756                         html : this.fieldLabel
8757                         
8758                     },
8759                     {
8760                         cls : "col-sm-" + (12 - this.labelWidth), 
8761                         cn: [
8762                             combobox
8763                         ]
8764                     }
8765                     
8766                 ];
8767         } else if ( this.fieldLabel.length) {
8768                 Roo.log(" label");
8769                  cfg.cn = [
8770                    
8771                     {
8772                         tag: 'label',
8773                         //cls : 'input-group-addon',
8774                         html : this.fieldLabel
8775                         
8776                     },
8777                     
8778                     combobox
8779                     
8780                 ];
8781
8782         } else {
8783             
8784                 Roo.log(" no label && no align");
8785                 cfg = combobox
8786                      
8787                 
8788         }
8789          
8790         var settings=this;
8791         ['xs','sm','md','lg'].map(function(size){
8792             if (settings[size]) {
8793                 cfg.cls += ' col-' + size + '-' + settings[size];
8794             }
8795         });
8796         Roo.log(cfg);
8797         return cfg;
8798         
8799     },
8800     
8801     
8802     
8803     // private
8804     onResize : function(w, h){
8805 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8806 //        if(typeof w == 'number'){
8807 //            var x = w - this.trigger.getWidth();
8808 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8809 //            this.trigger.setStyle('left', x+'px');
8810 //        }
8811     },
8812
8813     // private
8814     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8815
8816     // private
8817     getResizeEl : function(){
8818         return this.inputEl();
8819     },
8820
8821     // private
8822     getPositionEl : function(){
8823         return this.inputEl();
8824     },
8825
8826     // private
8827     alignErrorIcon : function(){
8828         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8829     },
8830
8831     // private
8832     initEvents : function(){
8833         
8834         this.createList();
8835         
8836         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8837         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8838         if(!this.multiple && this.showToggleBtn){
8839             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8840             if(this.hideTrigger){
8841                 this.trigger.setDisplayed(false);
8842             }
8843             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8844         }
8845         
8846         if(this.multiple){
8847             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8848         }
8849         
8850         if(this.removable && !this.editable && !this.tickable){
8851             var close = this.closeTriggerEl();
8852             
8853             if(close){
8854                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8855                 close.on('click', this.removeBtnClick, this, close);
8856             }
8857         }
8858         
8859         //this.trigger.addClassOnOver('x-form-trigger-over');
8860         //this.trigger.addClassOnClick('x-form-trigger-click');
8861         
8862         //if(!this.width){
8863         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8864         //}
8865     },
8866     
8867     closeTriggerEl : function()
8868     {
8869         var close = this.el.select('.roo-combo-removable-btn', true).first();
8870         return close ? close : false;
8871     },
8872     
8873     removeBtnClick : function(e, h, el)
8874     {
8875         e.preventDefault();
8876         
8877         if(this.fireEvent("remove", this) !== false){
8878             this.reset();
8879         }
8880     },
8881     
8882     createList : function()
8883     {
8884         this.list = Roo.get(document.body).createChild({
8885             tag: 'ul',
8886             cls: 'typeahead typeahead-long dropdown-menu',
8887             style: 'display:none'
8888         });
8889         
8890         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8891         
8892     },
8893
8894     // private
8895     initTrigger : function(){
8896        
8897     },
8898
8899     // private
8900     onDestroy : function(){
8901         if(this.trigger){
8902             this.trigger.removeAllListeners();
8903           //  this.trigger.remove();
8904         }
8905         //if(this.wrap){
8906         //    this.wrap.remove();
8907         //}
8908         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8909     },
8910
8911     // private
8912     onFocus : function(){
8913         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8914         /*
8915         if(!this.mimicing){
8916             this.wrap.addClass('x-trigger-wrap-focus');
8917             this.mimicing = true;
8918             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8919             if(this.monitorTab){
8920                 this.el.on("keydown", this.checkTab, this);
8921             }
8922         }
8923         */
8924     },
8925
8926     // private
8927     checkTab : function(e){
8928         if(e.getKey() == e.TAB){
8929             this.triggerBlur();
8930         }
8931     },
8932
8933     // private
8934     onBlur : function(){
8935         // do nothing
8936     },
8937
8938     // private
8939     mimicBlur : function(e, t){
8940         /*
8941         if(!this.wrap.contains(t) && this.validateBlur()){
8942             this.triggerBlur();
8943         }
8944         */
8945     },
8946
8947     // private
8948     triggerBlur : function(){
8949         this.mimicing = false;
8950         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8951         if(this.monitorTab){
8952             this.el.un("keydown", this.checkTab, this);
8953         }
8954         //this.wrap.removeClass('x-trigger-wrap-focus');
8955         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8956     },
8957
8958     // private
8959     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8960     validateBlur : function(e, t){
8961         return true;
8962     },
8963
8964     // private
8965     onDisable : function(){
8966         this.inputEl().dom.disabled = true;
8967         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8968         //if(this.wrap){
8969         //    this.wrap.addClass('x-item-disabled');
8970         //}
8971     },
8972
8973     // private
8974     onEnable : function(){
8975         this.inputEl().dom.disabled = false;
8976         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8977         //if(this.wrap){
8978         //    this.el.removeClass('x-item-disabled');
8979         //}
8980     },
8981
8982     // private
8983     onShow : function(){
8984         var ae = this.getActionEl();
8985         
8986         if(ae){
8987             ae.dom.style.display = '';
8988             ae.dom.style.visibility = 'visible';
8989         }
8990     },
8991
8992     // private
8993     
8994     onHide : function(){
8995         var ae = this.getActionEl();
8996         ae.dom.style.display = 'none';
8997     },
8998
8999     /**
9000      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9001      * by an implementing function.
9002      * @method
9003      * @param {EventObject} e
9004      */
9005     onTriggerClick : Roo.emptyFn
9006 });
9007  /*
9008  * Based on:
9009  * Ext JS Library 1.1.1
9010  * Copyright(c) 2006-2007, Ext JS, LLC.
9011  *
9012  * Originally Released Under LGPL - original licence link has changed is not relivant.
9013  *
9014  * Fork - LGPL
9015  * <script type="text/javascript">
9016  */
9017
9018
9019 /**
9020  * @class Roo.data.SortTypes
9021  * @singleton
9022  * Defines the default sorting (casting?) comparison functions used when sorting data.
9023  */
9024 Roo.data.SortTypes = {
9025     /**
9026      * Default sort that does nothing
9027      * @param {Mixed} s The value being converted
9028      * @return {Mixed} The comparison value
9029      */
9030     none : function(s){
9031         return s;
9032     },
9033     
9034     /**
9035      * The regular expression used to strip tags
9036      * @type {RegExp}
9037      * @property
9038      */
9039     stripTagsRE : /<\/?[^>]+>/gi,
9040     
9041     /**
9042      * Strips all HTML tags to sort on text only
9043      * @param {Mixed} s The value being converted
9044      * @return {String} The comparison value
9045      */
9046     asText : function(s){
9047         return String(s).replace(this.stripTagsRE, "");
9048     },
9049     
9050     /**
9051      * Strips all HTML tags to sort on text only - Case insensitive
9052      * @param {Mixed} s The value being converted
9053      * @return {String} The comparison value
9054      */
9055     asUCText : function(s){
9056         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9057     },
9058     
9059     /**
9060      * Case insensitive string
9061      * @param {Mixed} s The value being converted
9062      * @return {String} The comparison value
9063      */
9064     asUCString : function(s) {
9065         return String(s).toUpperCase();
9066     },
9067     
9068     /**
9069      * Date sorting
9070      * @param {Mixed} s The value being converted
9071      * @return {Number} The comparison value
9072      */
9073     asDate : function(s) {
9074         if(!s){
9075             return 0;
9076         }
9077         if(s instanceof Date){
9078             return s.getTime();
9079         }
9080         return Date.parse(String(s));
9081     },
9082     
9083     /**
9084      * Float sorting
9085      * @param {Mixed} s The value being converted
9086      * @return {Float} The comparison value
9087      */
9088     asFloat : function(s) {
9089         var val = parseFloat(String(s).replace(/,/g, ""));
9090         if(isNaN(val)) val = 0;
9091         return val;
9092     },
9093     
9094     /**
9095      * Integer sorting
9096      * @param {Mixed} s The value being converted
9097      * @return {Number} The comparison value
9098      */
9099     asInt : function(s) {
9100         var val = parseInt(String(s).replace(/,/g, ""));
9101         if(isNaN(val)) val = 0;
9102         return val;
9103     }
9104 };/*
9105  * Based on:
9106  * Ext JS Library 1.1.1
9107  * Copyright(c) 2006-2007, Ext JS, LLC.
9108  *
9109  * Originally Released Under LGPL - original licence link has changed is not relivant.
9110  *
9111  * Fork - LGPL
9112  * <script type="text/javascript">
9113  */
9114
9115 /**
9116 * @class Roo.data.Record
9117  * Instances of this class encapsulate both record <em>definition</em> information, and record
9118  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9119  * to access Records cached in an {@link Roo.data.Store} object.<br>
9120  * <p>
9121  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9122  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9123  * objects.<br>
9124  * <p>
9125  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9126  * @constructor
9127  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9128  * {@link #create}. The parameters are the same.
9129  * @param {Array} data An associative Array of data values keyed by the field name.
9130  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9131  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9132  * not specified an integer id is generated.
9133  */
9134 Roo.data.Record = function(data, id){
9135     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9136     this.data = data;
9137 };
9138
9139 /**
9140  * Generate a constructor for a specific record layout.
9141  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9142  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9143  * Each field definition object may contain the following properties: <ul>
9144  * <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,
9145  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9146  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9147  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9148  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9149  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9150  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9151  * this may be omitted.</p></li>
9152  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9153  * <ul><li>auto (Default, implies no conversion)</li>
9154  * <li>string</li>
9155  * <li>int</li>
9156  * <li>float</li>
9157  * <li>boolean</li>
9158  * <li>date</li></ul></p></li>
9159  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9160  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9161  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9162  * by the Reader into an object that will be stored in the Record. It is passed the
9163  * following parameters:<ul>
9164  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9165  * </ul></p></li>
9166  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9167  * </ul>
9168  * <br>usage:<br><pre><code>
9169 var TopicRecord = Roo.data.Record.create(
9170     {name: 'title', mapping: 'topic_title'},
9171     {name: 'author', mapping: 'username'},
9172     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9173     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9174     {name: 'lastPoster', mapping: 'user2'},
9175     {name: 'excerpt', mapping: 'post_text'}
9176 );
9177
9178 var myNewRecord = new TopicRecord({
9179     title: 'Do my job please',
9180     author: 'noobie',
9181     totalPosts: 1,
9182     lastPost: new Date(),
9183     lastPoster: 'Animal',
9184     excerpt: 'No way dude!'
9185 });
9186 myStore.add(myNewRecord);
9187 </code></pre>
9188  * @method create
9189  * @static
9190  */
9191 Roo.data.Record.create = function(o){
9192     var f = function(){
9193         f.superclass.constructor.apply(this, arguments);
9194     };
9195     Roo.extend(f, Roo.data.Record);
9196     var p = f.prototype;
9197     p.fields = new Roo.util.MixedCollection(false, function(field){
9198         return field.name;
9199     });
9200     for(var i = 0, len = o.length; i < len; i++){
9201         p.fields.add(new Roo.data.Field(o[i]));
9202     }
9203     f.getField = function(name){
9204         return p.fields.get(name);  
9205     };
9206     return f;
9207 };
9208
9209 Roo.data.Record.AUTO_ID = 1000;
9210 Roo.data.Record.EDIT = 'edit';
9211 Roo.data.Record.REJECT = 'reject';
9212 Roo.data.Record.COMMIT = 'commit';
9213
9214 Roo.data.Record.prototype = {
9215     /**
9216      * Readonly flag - true if this record has been modified.
9217      * @type Boolean
9218      */
9219     dirty : false,
9220     editing : false,
9221     error: null,
9222     modified: null,
9223
9224     // private
9225     join : function(store){
9226         this.store = store;
9227     },
9228
9229     /**
9230      * Set the named field to the specified value.
9231      * @param {String} name The name of the field to set.
9232      * @param {Object} value The value to set the field to.
9233      */
9234     set : function(name, value){
9235         if(this.data[name] == value){
9236             return;
9237         }
9238         this.dirty = true;
9239         if(!this.modified){
9240             this.modified = {};
9241         }
9242         if(typeof this.modified[name] == 'undefined'){
9243             this.modified[name] = this.data[name];
9244         }
9245         this.data[name] = value;
9246         if(!this.editing && this.store){
9247             this.store.afterEdit(this);
9248         }       
9249     },
9250
9251     /**
9252      * Get the value of the named field.
9253      * @param {String} name The name of the field to get the value of.
9254      * @return {Object} The value of the field.
9255      */
9256     get : function(name){
9257         return this.data[name]; 
9258     },
9259
9260     // private
9261     beginEdit : function(){
9262         this.editing = true;
9263         this.modified = {}; 
9264     },
9265
9266     // private
9267     cancelEdit : function(){
9268         this.editing = false;
9269         delete this.modified;
9270     },
9271
9272     // private
9273     endEdit : function(){
9274         this.editing = false;
9275         if(this.dirty && this.store){
9276             this.store.afterEdit(this);
9277         }
9278     },
9279
9280     /**
9281      * Usually called by the {@link Roo.data.Store} which owns the Record.
9282      * Rejects all changes made to the Record since either creation, or the last commit operation.
9283      * Modified fields are reverted to their original values.
9284      * <p>
9285      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9286      * of reject operations.
9287      */
9288     reject : function(){
9289         var m = this.modified;
9290         for(var n in m){
9291             if(typeof m[n] != "function"){
9292                 this.data[n] = m[n];
9293             }
9294         }
9295         this.dirty = false;
9296         delete this.modified;
9297         this.editing = false;
9298         if(this.store){
9299             this.store.afterReject(this);
9300         }
9301     },
9302
9303     /**
9304      * Usually called by the {@link Roo.data.Store} which owns the Record.
9305      * Commits all changes made to the Record since either creation, or the last commit operation.
9306      * <p>
9307      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9308      * of commit operations.
9309      */
9310     commit : function(){
9311         this.dirty = false;
9312         delete this.modified;
9313         this.editing = false;
9314         if(this.store){
9315             this.store.afterCommit(this);
9316         }
9317     },
9318
9319     // private
9320     hasError : function(){
9321         return this.error != null;
9322     },
9323
9324     // private
9325     clearError : function(){
9326         this.error = null;
9327     },
9328
9329     /**
9330      * Creates a copy of this record.
9331      * @param {String} id (optional) A new record id if you don't want to use this record's id
9332      * @return {Record}
9333      */
9334     copy : function(newId) {
9335         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9336     }
9337 };/*
9338  * Based on:
9339  * Ext JS Library 1.1.1
9340  * Copyright(c) 2006-2007, Ext JS, LLC.
9341  *
9342  * Originally Released Under LGPL - original licence link has changed is not relivant.
9343  *
9344  * Fork - LGPL
9345  * <script type="text/javascript">
9346  */
9347
9348
9349
9350 /**
9351  * @class Roo.data.Store
9352  * @extends Roo.util.Observable
9353  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9354  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9355  * <p>
9356  * 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
9357  * has no knowledge of the format of the data returned by the Proxy.<br>
9358  * <p>
9359  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9360  * instances from the data object. These records are cached and made available through accessor functions.
9361  * @constructor
9362  * Creates a new Store.
9363  * @param {Object} config A config object containing the objects needed for the Store to access data,
9364  * and read the data into Records.
9365  */
9366 Roo.data.Store = function(config){
9367     this.data = new Roo.util.MixedCollection(false);
9368     this.data.getKey = function(o){
9369         return o.id;
9370     };
9371     this.baseParams = {};
9372     // private
9373     this.paramNames = {
9374         "start" : "start",
9375         "limit" : "limit",
9376         "sort" : "sort",
9377         "dir" : "dir",
9378         "multisort" : "_multisort"
9379     };
9380
9381     if(config && config.data){
9382         this.inlineData = config.data;
9383         delete config.data;
9384     }
9385
9386     Roo.apply(this, config);
9387     
9388     if(this.reader){ // reader passed
9389         this.reader = Roo.factory(this.reader, Roo.data);
9390         this.reader.xmodule = this.xmodule || false;
9391         if(!this.recordType){
9392             this.recordType = this.reader.recordType;
9393         }
9394         if(this.reader.onMetaChange){
9395             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9396         }
9397     }
9398
9399     if(this.recordType){
9400         this.fields = this.recordType.prototype.fields;
9401     }
9402     this.modified = [];
9403
9404     this.addEvents({
9405         /**
9406          * @event datachanged
9407          * Fires when the data cache has changed, and a widget which is using this Store
9408          * as a Record cache should refresh its view.
9409          * @param {Store} this
9410          */
9411         datachanged : true,
9412         /**
9413          * @event metachange
9414          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9415          * @param {Store} this
9416          * @param {Object} meta The JSON metadata
9417          */
9418         metachange : true,
9419         /**
9420          * @event add
9421          * Fires when Records have been added to the Store
9422          * @param {Store} this
9423          * @param {Roo.data.Record[]} records The array of Records added
9424          * @param {Number} index The index at which the record(s) were added
9425          */
9426         add : true,
9427         /**
9428          * @event remove
9429          * Fires when a Record has been removed from the Store
9430          * @param {Store} this
9431          * @param {Roo.data.Record} record The Record that was removed
9432          * @param {Number} index The index at which the record was removed
9433          */
9434         remove : true,
9435         /**
9436          * @event update
9437          * Fires when a Record has been updated
9438          * @param {Store} this
9439          * @param {Roo.data.Record} record The Record that was updated
9440          * @param {String} operation The update operation being performed.  Value may be one of:
9441          * <pre><code>
9442  Roo.data.Record.EDIT
9443  Roo.data.Record.REJECT
9444  Roo.data.Record.COMMIT
9445          * </code></pre>
9446          */
9447         update : true,
9448         /**
9449          * @event clear
9450          * Fires when the data cache has been cleared.
9451          * @param {Store} this
9452          */
9453         clear : true,
9454         /**
9455          * @event beforeload
9456          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9457          * the load action will be canceled.
9458          * @param {Store} this
9459          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9460          */
9461         beforeload : true,
9462         /**
9463          * @event beforeloadadd
9464          * Fires after a new set of Records has been loaded.
9465          * @param {Store} this
9466          * @param {Roo.data.Record[]} records The Records that were loaded
9467          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9468          */
9469         beforeloadadd : true,
9470         /**
9471          * @event load
9472          * Fires after a new set of Records has been loaded, before they are added to the store.
9473          * @param {Store} this
9474          * @param {Roo.data.Record[]} records The Records that were loaded
9475          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9476          * @params {Object} return from reader
9477          */
9478         load : true,
9479         /**
9480          * @event loadexception
9481          * Fires if an exception occurs in the Proxy during loading.
9482          * Called with the signature of the Proxy's "loadexception" event.
9483          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9484          * 
9485          * @param {Proxy} 
9486          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9487          * @param {Object} load options 
9488          * @param {Object} jsonData from your request (normally this contains the Exception)
9489          */
9490         loadexception : true
9491     });
9492     
9493     if(this.proxy){
9494         this.proxy = Roo.factory(this.proxy, Roo.data);
9495         this.proxy.xmodule = this.xmodule || false;
9496         this.relayEvents(this.proxy,  ["loadexception"]);
9497     }
9498     this.sortToggle = {};
9499     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9500
9501     Roo.data.Store.superclass.constructor.call(this);
9502
9503     if(this.inlineData){
9504         this.loadData(this.inlineData);
9505         delete this.inlineData;
9506     }
9507 };
9508
9509 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9510      /**
9511     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9512     * without a remote query - used by combo/forms at present.
9513     */
9514     
9515     /**
9516     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9517     */
9518     /**
9519     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9520     */
9521     /**
9522     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9523     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9524     */
9525     /**
9526     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9527     * on any HTTP request
9528     */
9529     /**
9530     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9531     */
9532     /**
9533     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9534     */
9535     multiSort: false,
9536     /**
9537     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9538     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9539     */
9540     remoteSort : false,
9541
9542     /**
9543     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9544      * loaded or when a record is removed. (defaults to false).
9545     */
9546     pruneModifiedRecords : false,
9547
9548     // private
9549     lastOptions : null,
9550
9551     /**
9552      * Add Records to the Store and fires the add event.
9553      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9554      */
9555     add : function(records){
9556         records = [].concat(records);
9557         for(var i = 0, len = records.length; i < len; i++){
9558             records[i].join(this);
9559         }
9560         var index = this.data.length;
9561         this.data.addAll(records);
9562         this.fireEvent("add", this, records, index);
9563     },
9564
9565     /**
9566      * Remove a Record from the Store and fires the remove event.
9567      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9568      */
9569     remove : function(record){
9570         var index = this.data.indexOf(record);
9571         this.data.removeAt(index);
9572         if(this.pruneModifiedRecords){
9573             this.modified.remove(record);
9574         }
9575         this.fireEvent("remove", this, record, index);
9576     },
9577
9578     /**
9579      * Remove all Records from the Store and fires the clear event.
9580      */
9581     removeAll : function(){
9582         this.data.clear();
9583         if(this.pruneModifiedRecords){
9584             this.modified = [];
9585         }
9586         this.fireEvent("clear", this);
9587     },
9588
9589     /**
9590      * Inserts Records to the Store at the given index and fires the add event.
9591      * @param {Number} index The start index at which to insert the passed Records.
9592      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9593      */
9594     insert : function(index, records){
9595         records = [].concat(records);
9596         for(var i = 0, len = records.length; i < len; i++){
9597             this.data.insert(index, records[i]);
9598             records[i].join(this);
9599         }
9600         this.fireEvent("add", this, records, index);
9601     },
9602
9603     /**
9604      * Get the index within the cache of the passed Record.
9605      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9606      * @return {Number} The index of the passed Record. Returns -1 if not found.
9607      */
9608     indexOf : function(record){
9609         return this.data.indexOf(record);
9610     },
9611
9612     /**
9613      * Get the index within the cache of the Record with the passed id.
9614      * @param {String} id The id of the Record to find.
9615      * @return {Number} The index of the Record. Returns -1 if not found.
9616      */
9617     indexOfId : function(id){
9618         return this.data.indexOfKey(id);
9619     },
9620
9621     /**
9622      * Get the Record with the specified id.
9623      * @param {String} id The id of the Record to find.
9624      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9625      */
9626     getById : function(id){
9627         return this.data.key(id);
9628     },
9629
9630     /**
9631      * Get the Record at the specified index.
9632      * @param {Number} index The index of the Record to find.
9633      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9634      */
9635     getAt : function(index){
9636         return this.data.itemAt(index);
9637     },
9638
9639     /**
9640      * Returns a range of Records between specified indices.
9641      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9642      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9643      * @return {Roo.data.Record[]} An array of Records
9644      */
9645     getRange : function(start, end){
9646         return this.data.getRange(start, end);
9647     },
9648
9649     // private
9650     storeOptions : function(o){
9651         o = Roo.apply({}, o);
9652         delete o.callback;
9653         delete o.scope;
9654         this.lastOptions = o;
9655     },
9656
9657     /**
9658      * Loads the Record cache from the configured Proxy using the configured Reader.
9659      * <p>
9660      * If using remote paging, then the first load call must specify the <em>start</em>
9661      * and <em>limit</em> properties in the options.params property to establish the initial
9662      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9663      * <p>
9664      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9665      * and this call will return before the new data has been loaded. Perform any post-processing
9666      * in a callback function, or in a "load" event handler.</strong>
9667      * <p>
9668      * @param {Object} options An object containing properties which control loading options:<ul>
9669      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9670      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9671      * passed the following arguments:<ul>
9672      * <li>r : Roo.data.Record[]</li>
9673      * <li>options: Options object from the load call</li>
9674      * <li>success: Boolean success indicator</li></ul></li>
9675      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9676      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9677      * </ul>
9678      */
9679     load : function(options){
9680         options = options || {};
9681         if(this.fireEvent("beforeload", this, options) !== false){
9682             this.storeOptions(options);
9683             var p = Roo.apply(options.params || {}, this.baseParams);
9684             // if meta was not loaded from remote source.. try requesting it.
9685             if (!this.reader.metaFromRemote) {
9686                 p._requestMeta = 1;
9687             }
9688             if(this.sortInfo && this.remoteSort){
9689                 var pn = this.paramNames;
9690                 p[pn["sort"]] = this.sortInfo.field;
9691                 p[pn["dir"]] = this.sortInfo.direction;
9692             }
9693             if (this.multiSort) {
9694                 var pn = this.paramNames;
9695                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9696             }
9697             
9698             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9699         }
9700     },
9701
9702     /**
9703      * Reloads the Record cache from the configured Proxy using the configured Reader and
9704      * the options from the last load operation performed.
9705      * @param {Object} options (optional) An object containing properties which may override the options
9706      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9707      * the most recently used options are reused).
9708      */
9709     reload : function(options){
9710         this.load(Roo.applyIf(options||{}, this.lastOptions));
9711     },
9712
9713     // private
9714     // Called as a callback by the Reader during a load operation.
9715     loadRecords : function(o, options, success){
9716         if(!o || success === false){
9717             if(success !== false){
9718                 this.fireEvent("load", this, [], options, o);
9719             }
9720             if(options.callback){
9721                 options.callback.call(options.scope || this, [], options, false);
9722             }
9723             return;
9724         }
9725         // if data returned failure - throw an exception.
9726         if (o.success === false) {
9727             // show a message if no listener is registered.
9728             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9729                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9730             }
9731             // loadmask wil be hooked into this..
9732             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9733             return;
9734         }
9735         var r = o.records, t = o.totalRecords || r.length;
9736         
9737         this.fireEvent("beforeloadadd", this, r, options, o);
9738         
9739         if(!options || options.add !== true){
9740             if(this.pruneModifiedRecords){
9741                 this.modified = [];
9742             }
9743             for(var i = 0, len = r.length; i < len; i++){
9744                 r[i].join(this);
9745             }
9746             if(this.snapshot){
9747                 this.data = this.snapshot;
9748                 delete this.snapshot;
9749             }
9750             this.data.clear();
9751             this.data.addAll(r);
9752             this.totalLength = t;
9753             this.applySort();
9754             this.fireEvent("datachanged", this);
9755         }else{
9756             this.totalLength = Math.max(t, this.data.length+r.length);
9757             this.add(r);
9758         }
9759         this.fireEvent("load", this, r, options, o);
9760         if(options.callback){
9761             options.callback.call(options.scope || this, r, options, true);
9762         }
9763     },
9764
9765
9766     /**
9767      * Loads data from a passed data block. A Reader which understands the format of the data
9768      * must have been configured in the constructor.
9769      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9770      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9771      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9772      */
9773     loadData : function(o, append){
9774         var r = this.reader.readRecords(o);
9775         this.loadRecords(r, {add: append}, true);
9776     },
9777
9778     /**
9779      * Gets the number of cached records.
9780      * <p>
9781      * <em>If using paging, this may not be the total size of the dataset. If the data object
9782      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9783      * the data set size</em>
9784      */
9785     getCount : function(){
9786         return this.data.length || 0;
9787     },
9788
9789     /**
9790      * Gets the total number of records in the dataset as returned by the server.
9791      * <p>
9792      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9793      * the dataset size</em>
9794      */
9795     getTotalCount : function(){
9796         return this.totalLength || 0;
9797     },
9798
9799     /**
9800      * Returns the sort state of the Store as an object with two properties:
9801      * <pre><code>
9802  field {String} The name of the field by which the Records are sorted
9803  direction {String} The sort order, "ASC" or "DESC"
9804      * </code></pre>
9805      */
9806     getSortState : function(){
9807         return this.sortInfo;
9808     },
9809
9810     // private
9811     applySort : function(){
9812         if(this.sortInfo && !this.remoteSort){
9813             var s = this.sortInfo, f = s.field;
9814             var st = this.fields.get(f).sortType;
9815             var fn = function(r1, r2){
9816                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9817                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9818             };
9819             this.data.sort(s.direction, fn);
9820             if(this.snapshot && this.snapshot != this.data){
9821                 this.snapshot.sort(s.direction, fn);
9822             }
9823         }
9824     },
9825
9826     /**
9827      * Sets the default sort column and order to be used by the next load operation.
9828      * @param {String} fieldName The name of the field to sort by.
9829      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9830      */
9831     setDefaultSort : function(field, dir){
9832         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9833     },
9834
9835     /**
9836      * Sort the Records.
9837      * If remote sorting is used, the sort is performed on the server, and the cache is
9838      * reloaded. If local sorting is used, the cache is sorted internally.
9839      * @param {String} fieldName The name of the field to sort by.
9840      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9841      */
9842     sort : function(fieldName, dir){
9843         var f = this.fields.get(fieldName);
9844         if(!dir){
9845             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9846             
9847             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9848                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9849             }else{
9850                 dir = f.sortDir;
9851             }
9852         }
9853         this.sortToggle[f.name] = dir;
9854         this.sortInfo = {field: f.name, direction: dir};
9855         if(!this.remoteSort){
9856             this.applySort();
9857             this.fireEvent("datachanged", this);
9858         }else{
9859             this.load(this.lastOptions);
9860         }
9861     },
9862
9863     /**
9864      * Calls the specified function for each of the Records in the cache.
9865      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9866      * Returning <em>false</em> aborts and exits the iteration.
9867      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9868      */
9869     each : function(fn, scope){
9870         this.data.each(fn, scope);
9871     },
9872
9873     /**
9874      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9875      * (e.g., during paging).
9876      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9877      */
9878     getModifiedRecords : function(){
9879         return this.modified;
9880     },
9881
9882     // private
9883     createFilterFn : function(property, value, anyMatch){
9884         if(!value.exec){ // not a regex
9885             value = String(value);
9886             if(value.length == 0){
9887                 return false;
9888             }
9889             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9890         }
9891         return function(r){
9892             return value.test(r.data[property]);
9893         };
9894     },
9895
9896     /**
9897      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9898      * @param {String} property A field on your records
9899      * @param {Number} start The record index to start at (defaults to 0)
9900      * @param {Number} end The last record index to include (defaults to length - 1)
9901      * @return {Number} The sum
9902      */
9903     sum : function(property, start, end){
9904         var rs = this.data.items, v = 0;
9905         start = start || 0;
9906         end = (end || end === 0) ? end : rs.length-1;
9907
9908         for(var i = start; i <= end; i++){
9909             v += (rs[i].data[property] || 0);
9910         }
9911         return v;
9912     },
9913
9914     /**
9915      * Filter the records by a specified property.
9916      * @param {String} field A field on your records
9917      * @param {String/RegExp} value Either a string that the field
9918      * should start with or a RegExp to test against the field
9919      * @param {Boolean} anyMatch True to match any part not just the beginning
9920      */
9921     filter : function(property, value, anyMatch){
9922         var fn = this.createFilterFn(property, value, anyMatch);
9923         return fn ? this.filterBy(fn) : this.clearFilter();
9924     },
9925
9926     /**
9927      * Filter by a function. The specified function will be called with each
9928      * record in this data source. If the function returns true the record is included,
9929      * otherwise it is filtered.
9930      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9931      * @param {Object} scope (optional) The scope of the function (defaults to this)
9932      */
9933     filterBy : function(fn, scope){
9934         this.snapshot = this.snapshot || this.data;
9935         this.data = this.queryBy(fn, scope||this);
9936         this.fireEvent("datachanged", this);
9937     },
9938
9939     /**
9940      * Query the records by a specified property.
9941      * @param {String} field A field on your records
9942      * @param {String/RegExp} value Either a string that the field
9943      * should start with or a RegExp to test against the field
9944      * @param {Boolean} anyMatch True to match any part not just the beginning
9945      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9946      */
9947     query : function(property, value, anyMatch){
9948         var fn = this.createFilterFn(property, value, anyMatch);
9949         return fn ? this.queryBy(fn) : this.data.clone();
9950     },
9951
9952     /**
9953      * Query by a function. The specified function will be called with each
9954      * record in this data source. If the function returns true the record is included
9955      * in the results.
9956      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9957      * @param {Object} scope (optional) The scope of the function (defaults to this)
9958       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9959      **/
9960     queryBy : function(fn, scope){
9961         var data = this.snapshot || this.data;
9962         return data.filterBy(fn, scope||this);
9963     },
9964
9965     /**
9966      * Collects unique values for a particular dataIndex from this store.
9967      * @param {String} dataIndex The property to collect
9968      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9969      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9970      * @return {Array} An array of the unique values
9971      **/
9972     collect : function(dataIndex, allowNull, bypassFilter){
9973         var d = (bypassFilter === true && this.snapshot) ?
9974                 this.snapshot.items : this.data.items;
9975         var v, sv, r = [], l = {};
9976         for(var i = 0, len = d.length; i < len; i++){
9977             v = d[i].data[dataIndex];
9978             sv = String(v);
9979             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9980                 l[sv] = true;
9981                 r[r.length] = v;
9982             }
9983         }
9984         return r;
9985     },
9986
9987     /**
9988      * Revert to a view of the Record cache with no filtering applied.
9989      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9990      */
9991     clearFilter : function(suppressEvent){
9992         if(this.snapshot && this.snapshot != this.data){
9993             this.data = this.snapshot;
9994             delete this.snapshot;
9995             if(suppressEvent !== true){
9996                 this.fireEvent("datachanged", this);
9997             }
9998         }
9999     },
10000
10001     // private
10002     afterEdit : function(record){
10003         if(this.modified.indexOf(record) == -1){
10004             this.modified.push(record);
10005         }
10006         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10007     },
10008     
10009     // private
10010     afterReject : function(record){
10011         this.modified.remove(record);
10012         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10013     },
10014
10015     // private
10016     afterCommit : function(record){
10017         this.modified.remove(record);
10018         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10019     },
10020
10021     /**
10022      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10023      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10024      */
10025     commitChanges : function(){
10026         var m = this.modified.slice(0);
10027         this.modified = [];
10028         for(var i = 0, len = m.length; i < len; i++){
10029             m[i].commit();
10030         }
10031     },
10032
10033     /**
10034      * Cancel outstanding changes on all changed records.
10035      */
10036     rejectChanges : function(){
10037         var m = this.modified.slice(0);
10038         this.modified = [];
10039         for(var i = 0, len = m.length; i < len; i++){
10040             m[i].reject();
10041         }
10042     },
10043
10044     onMetaChange : function(meta, rtype, o){
10045         this.recordType = rtype;
10046         this.fields = rtype.prototype.fields;
10047         delete this.snapshot;
10048         this.sortInfo = meta.sortInfo || this.sortInfo;
10049         this.modified = [];
10050         this.fireEvent('metachange', this, this.reader.meta);
10051     },
10052     
10053     moveIndex : function(data, type)
10054     {
10055         var index = this.indexOf(data);
10056         
10057         var newIndex = index + type;
10058         
10059         this.remove(data);
10060         
10061         this.insert(newIndex, data);
10062         
10063     }
10064 });/*
10065  * Based on:
10066  * Ext JS Library 1.1.1
10067  * Copyright(c) 2006-2007, Ext JS, LLC.
10068  *
10069  * Originally Released Under LGPL - original licence link has changed is not relivant.
10070  *
10071  * Fork - LGPL
10072  * <script type="text/javascript">
10073  */
10074
10075 /**
10076  * @class Roo.data.SimpleStore
10077  * @extends Roo.data.Store
10078  * Small helper class to make creating Stores from Array data easier.
10079  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10080  * @cfg {Array} fields An array of field definition objects, or field name strings.
10081  * @cfg {Array} data The multi-dimensional array of data
10082  * @constructor
10083  * @param {Object} config
10084  */
10085 Roo.data.SimpleStore = function(config){
10086     Roo.data.SimpleStore.superclass.constructor.call(this, {
10087         isLocal : true,
10088         reader: new Roo.data.ArrayReader({
10089                 id: config.id
10090             },
10091             Roo.data.Record.create(config.fields)
10092         ),
10093         proxy : new Roo.data.MemoryProxy(config.data)
10094     });
10095     this.load();
10096 };
10097 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107
10108 /**
10109 /**
10110  * @extends Roo.data.Store
10111  * @class Roo.data.JsonStore
10112  * Small helper class to make creating Stores for JSON data easier. <br/>
10113 <pre><code>
10114 var store = new Roo.data.JsonStore({
10115     url: 'get-images.php',
10116     root: 'images',
10117     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10118 });
10119 </code></pre>
10120  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10121  * JsonReader and HttpProxy (unless inline data is provided).</b>
10122  * @cfg {Array} fields An array of field definition objects, or field name strings.
10123  * @constructor
10124  * @param {Object} config
10125  */
10126 Roo.data.JsonStore = function(c){
10127     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10128         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10129         reader: new Roo.data.JsonReader(c, c.fields)
10130     }));
10131 };
10132 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10133  * Based on:
10134  * Ext JS Library 1.1.1
10135  * Copyright(c) 2006-2007, Ext JS, LLC.
10136  *
10137  * Originally Released Under LGPL - original licence link has changed is not relivant.
10138  *
10139  * Fork - LGPL
10140  * <script type="text/javascript">
10141  */
10142
10143  
10144 Roo.data.Field = function(config){
10145     if(typeof config == "string"){
10146         config = {name: config};
10147     }
10148     Roo.apply(this, config);
10149     
10150     if(!this.type){
10151         this.type = "auto";
10152     }
10153     
10154     var st = Roo.data.SortTypes;
10155     // named sortTypes are supported, here we look them up
10156     if(typeof this.sortType == "string"){
10157         this.sortType = st[this.sortType];
10158     }
10159     
10160     // set default sortType for strings and dates
10161     if(!this.sortType){
10162         switch(this.type){
10163             case "string":
10164                 this.sortType = st.asUCString;
10165                 break;
10166             case "date":
10167                 this.sortType = st.asDate;
10168                 break;
10169             default:
10170                 this.sortType = st.none;
10171         }
10172     }
10173
10174     // define once
10175     var stripRe = /[\$,%]/g;
10176
10177     // prebuilt conversion function for this field, instead of
10178     // switching every time we're reading a value
10179     if(!this.convert){
10180         var cv, dateFormat = this.dateFormat;
10181         switch(this.type){
10182             case "":
10183             case "auto":
10184             case undefined:
10185                 cv = function(v){ return v; };
10186                 break;
10187             case "string":
10188                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10189                 break;
10190             case "int":
10191                 cv = function(v){
10192                     return v !== undefined && v !== null && v !== '' ?
10193                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10194                     };
10195                 break;
10196             case "float":
10197                 cv = function(v){
10198                     return v !== undefined && v !== null && v !== '' ?
10199                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10200                     };
10201                 break;
10202             case "bool":
10203             case "boolean":
10204                 cv = function(v){ return v === true || v === "true" || v == 1; };
10205                 break;
10206             case "date":
10207                 cv = function(v){
10208                     if(!v){
10209                         return '';
10210                     }
10211                     if(v instanceof Date){
10212                         return v;
10213                     }
10214                     if(dateFormat){
10215                         if(dateFormat == "timestamp"){
10216                             return new Date(v*1000);
10217                         }
10218                         return Date.parseDate(v, dateFormat);
10219                     }
10220                     var parsed = Date.parse(v);
10221                     return parsed ? new Date(parsed) : null;
10222                 };
10223              break;
10224             
10225         }
10226         this.convert = cv;
10227     }
10228 };
10229
10230 Roo.data.Field.prototype = {
10231     dateFormat: null,
10232     defaultValue: "",
10233     mapping: null,
10234     sortType : null,
10235     sortDir : "ASC"
10236 };/*
10237  * Based on:
10238  * Ext JS Library 1.1.1
10239  * Copyright(c) 2006-2007, Ext JS, LLC.
10240  *
10241  * Originally Released Under LGPL - original licence link has changed is not relivant.
10242  *
10243  * Fork - LGPL
10244  * <script type="text/javascript">
10245  */
10246  
10247 // Base class for reading structured data from a data source.  This class is intended to be
10248 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10249
10250 /**
10251  * @class Roo.data.DataReader
10252  * Base class for reading structured data from a data source.  This class is intended to be
10253  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10254  */
10255
10256 Roo.data.DataReader = function(meta, recordType){
10257     
10258     this.meta = meta;
10259     
10260     this.recordType = recordType instanceof Array ? 
10261         Roo.data.Record.create(recordType) : recordType;
10262 };
10263
10264 Roo.data.DataReader.prototype = {
10265      /**
10266      * Create an empty record
10267      * @param {Object} data (optional) - overlay some values
10268      * @return {Roo.data.Record} record created.
10269      */
10270     newRow :  function(d) {
10271         var da =  {};
10272         this.recordType.prototype.fields.each(function(c) {
10273             switch( c.type) {
10274                 case 'int' : da[c.name] = 0; break;
10275                 case 'date' : da[c.name] = new Date(); break;
10276                 case 'float' : da[c.name] = 0.0; break;
10277                 case 'boolean' : da[c.name] = false; break;
10278                 default : da[c.name] = ""; break;
10279             }
10280             
10281         });
10282         return new this.recordType(Roo.apply(da, d));
10283     }
10284     
10285 };/*
10286  * Based on:
10287  * Ext JS Library 1.1.1
10288  * Copyright(c) 2006-2007, Ext JS, LLC.
10289  *
10290  * Originally Released Under LGPL - original licence link has changed is not relivant.
10291  *
10292  * Fork - LGPL
10293  * <script type="text/javascript">
10294  */
10295
10296 /**
10297  * @class Roo.data.DataProxy
10298  * @extends Roo.data.Observable
10299  * This class is an abstract base class for implementations which provide retrieval of
10300  * unformatted data objects.<br>
10301  * <p>
10302  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10303  * (of the appropriate type which knows how to parse the data object) to provide a block of
10304  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10305  * <p>
10306  * Custom implementations must implement the load method as described in
10307  * {@link Roo.data.HttpProxy#load}.
10308  */
10309 Roo.data.DataProxy = function(){
10310     this.addEvents({
10311         /**
10312          * @event beforeload
10313          * Fires before a network request is made to retrieve a data object.
10314          * @param {Object} This DataProxy object.
10315          * @param {Object} params The params parameter to the load function.
10316          */
10317         beforeload : true,
10318         /**
10319          * @event load
10320          * Fires before the load method's callback is called.
10321          * @param {Object} This DataProxy object.
10322          * @param {Object} o The data object.
10323          * @param {Object} arg The callback argument object passed to the load function.
10324          */
10325         load : true,
10326         /**
10327          * @event loadexception
10328          * Fires if an Exception occurs during data retrieval.
10329          * @param {Object} This DataProxy object.
10330          * @param {Object} o The data object.
10331          * @param {Object} arg The callback argument object passed to the load function.
10332          * @param {Object} e The Exception.
10333          */
10334         loadexception : true
10335     });
10336     Roo.data.DataProxy.superclass.constructor.call(this);
10337 };
10338
10339 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10340
10341     /**
10342      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10343      */
10344 /*
10345  * Based on:
10346  * Ext JS Library 1.1.1
10347  * Copyright(c) 2006-2007, Ext JS, LLC.
10348  *
10349  * Originally Released Under LGPL - original licence link has changed is not relivant.
10350  *
10351  * Fork - LGPL
10352  * <script type="text/javascript">
10353  */
10354 /**
10355  * @class Roo.data.MemoryProxy
10356  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10357  * to the Reader when its load method is called.
10358  * @constructor
10359  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10360  */
10361 Roo.data.MemoryProxy = function(data){
10362     if (data.data) {
10363         data = data.data;
10364     }
10365     Roo.data.MemoryProxy.superclass.constructor.call(this);
10366     this.data = data;
10367 };
10368
10369 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10370     /**
10371      * Load data from the requested source (in this case an in-memory
10372      * data object passed to the constructor), read the data object into
10373      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10374      * process that block using the passed callback.
10375      * @param {Object} params This parameter is not used by the MemoryProxy class.
10376      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10377      * object into a block of Roo.data.Records.
10378      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10379      * The function must be passed <ul>
10380      * <li>The Record block object</li>
10381      * <li>The "arg" argument from the load function</li>
10382      * <li>A boolean success indicator</li>
10383      * </ul>
10384      * @param {Object} scope The scope in which to call the callback
10385      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10386      */
10387     load : function(params, reader, callback, scope, arg){
10388         params = params || {};
10389         var result;
10390         try {
10391             result = reader.readRecords(this.data);
10392         }catch(e){
10393             this.fireEvent("loadexception", this, arg, null, e);
10394             callback.call(scope, null, arg, false);
10395             return;
10396         }
10397         callback.call(scope, result, arg, true);
10398     },
10399     
10400     // private
10401     update : function(params, records){
10402         
10403     }
10404 });/*
10405  * Based on:
10406  * Ext JS Library 1.1.1
10407  * Copyright(c) 2006-2007, Ext JS, LLC.
10408  *
10409  * Originally Released Under LGPL - original licence link has changed is not relivant.
10410  *
10411  * Fork - LGPL
10412  * <script type="text/javascript">
10413  */
10414 /**
10415  * @class Roo.data.HttpProxy
10416  * @extends Roo.data.DataProxy
10417  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10418  * configured to reference a certain URL.<br><br>
10419  * <p>
10420  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10421  * from which the running page was served.<br><br>
10422  * <p>
10423  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10424  * <p>
10425  * Be aware that to enable the browser to parse an XML document, the server must set
10426  * the Content-Type header in the HTTP response to "text/xml".
10427  * @constructor
10428  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10429  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10430  * will be used to make the request.
10431  */
10432 Roo.data.HttpProxy = function(conn){
10433     Roo.data.HttpProxy.superclass.constructor.call(this);
10434     // is conn a conn config or a real conn?
10435     this.conn = conn;
10436     this.useAjax = !conn || !conn.events;
10437   
10438 };
10439
10440 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10441     // thse are take from connection...
10442     
10443     /**
10444      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10445      */
10446     /**
10447      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10448      * extra parameters to each request made by this object. (defaults to undefined)
10449      */
10450     /**
10451      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10452      *  to each request made by this object. (defaults to undefined)
10453      */
10454     /**
10455      * @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)
10456      */
10457     /**
10458      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10459      */
10460      /**
10461      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10462      * @type Boolean
10463      */
10464   
10465
10466     /**
10467      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10468      * @type Boolean
10469      */
10470     /**
10471      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10472      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10473      * a finer-grained basis than the DataProxy events.
10474      */
10475     getConnection : function(){
10476         return this.useAjax ? Roo.Ajax : this.conn;
10477     },
10478
10479     /**
10480      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10481      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10482      * process that block using the passed callback.
10483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10484      * for the request to the remote server.
10485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10486      * object into a block of Roo.data.Records.
10487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10488      * The function must be passed <ul>
10489      * <li>The Record block object</li>
10490      * <li>The "arg" argument from the load function</li>
10491      * <li>A boolean success indicator</li>
10492      * </ul>
10493      * @param {Object} scope The scope in which to call the callback
10494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10495      */
10496     load : function(params, reader, callback, scope, arg){
10497         if(this.fireEvent("beforeload", this, params) !== false){
10498             var  o = {
10499                 params : params || {},
10500                 request: {
10501                     callback : callback,
10502                     scope : scope,
10503                     arg : arg
10504                 },
10505                 reader: reader,
10506                 callback : this.loadResponse,
10507                 scope: this
10508             };
10509             if(this.useAjax){
10510                 Roo.applyIf(o, this.conn);
10511                 if(this.activeRequest){
10512                     Roo.Ajax.abort(this.activeRequest);
10513                 }
10514                 this.activeRequest = Roo.Ajax.request(o);
10515             }else{
10516                 this.conn.request(o);
10517             }
10518         }else{
10519             callback.call(scope||this, null, arg, false);
10520         }
10521     },
10522
10523     // private
10524     loadResponse : function(o, success, response){
10525         delete this.activeRequest;
10526         if(!success){
10527             this.fireEvent("loadexception", this, o, response);
10528             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10529             return;
10530         }
10531         var result;
10532         try {
10533             result = o.reader.read(response);
10534         }catch(e){
10535             this.fireEvent("loadexception", this, o, response, e);
10536             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10537             return;
10538         }
10539         
10540         this.fireEvent("load", this, o, o.request.arg);
10541         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10542     },
10543
10544     // private
10545     update : function(dataSet){
10546
10547     },
10548
10549     // private
10550     updateResponse : function(dataSet){
10551
10552     }
10553 });/*
10554  * Based on:
10555  * Ext JS Library 1.1.1
10556  * Copyright(c) 2006-2007, Ext JS, LLC.
10557  *
10558  * Originally Released Under LGPL - original licence link has changed is not relivant.
10559  *
10560  * Fork - LGPL
10561  * <script type="text/javascript">
10562  */
10563
10564 /**
10565  * @class Roo.data.ScriptTagProxy
10566  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10567  * other than the originating domain of the running page.<br><br>
10568  * <p>
10569  * <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
10570  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10571  * <p>
10572  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10573  * source code that is used as the source inside a &lt;script> tag.<br><br>
10574  * <p>
10575  * In order for the browser to process the returned data, the server must wrap the data object
10576  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10577  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10578  * depending on whether the callback name was passed:
10579  * <p>
10580  * <pre><code>
10581 boolean scriptTag = false;
10582 String cb = request.getParameter("callback");
10583 if (cb != null) {
10584     scriptTag = true;
10585     response.setContentType("text/javascript");
10586 } else {
10587     response.setContentType("application/x-json");
10588 }
10589 Writer out = response.getWriter();
10590 if (scriptTag) {
10591     out.write(cb + "(");
10592 }
10593 out.print(dataBlock.toJsonString());
10594 if (scriptTag) {
10595     out.write(");");
10596 }
10597 </pre></code>
10598  *
10599  * @constructor
10600  * @param {Object} config A configuration object.
10601  */
10602 Roo.data.ScriptTagProxy = function(config){
10603     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10604     Roo.apply(this, config);
10605     this.head = document.getElementsByTagName("head")[0];
10606 };
10607
10608 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10609
10610 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10611     /**
10612      * @cfg {String} url The URL from which to request the data object.
10613      */
10614     /**
10615      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10616      */
10617     timeout : 30000,
10618     /**
10619      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10620      * the server the name of the callback function set up by the load call to process the returned data object.
10621      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10622      * javascript output which calls this named function passing the data object as its only parameter.
10623      */
10624     callbackParam : "callback",
10625     /**
10626      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10627      * name to the request.
10628      */
10629     nocache : true,
10630
10631     /**
10632      * Load data from the configured URL, read the data object into
10633      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10634      * process that block using the passed callback.
10635      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10636      * for the request to the remote server.
10637      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10638      * object into a block of Roo.data.Records.
10639      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10640      * The function must be passed <ul>
10641      * <li>The Record block object</li>
10642      * <li>The "arg" argument from the load function</li>
10643      * <li>A boolean success indicator</li>
10644      * </ul>
10645      * @param {Object} scope The scope in which to call the callback
10646      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10647      */
10648     load : function(params, reader, callback, scope, arg){
10649         if(this.fireEvent("beforeload", this, params) !== false){
10650
10651             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10652
10653             var url = this.url;
10654             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10655             if(this.nocache){
10656                 url += "&_dc=" + (new Date().getTime());
10657             }
10658             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10659             var trans = {
10660                 id : transId,
10661                 cb : "stcCallback"+transId,
10662                 scriptId : "stcScript"+transId,
10663                 params : params,
10664                 arg : arg,
10665                 url : url,
10666                 callback : callback,
10667                 scope : scope,
10668                 reader : reader
10669             };
10670             var conn = this;
10671
10672             window[trans.cb] = function(o){
10673                 conn.handleResponse(o, trans);
10674             };
10675
10676             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10677
10678             if(this.autoAbort !== false){
10679                 this.abort();
10680             }
10681
10682             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10683
10684             var script = document.createElement("script");
10685             script.setAttribute("src", url);
10686             script.setAttribute("type", "text/javascript");
10687             script.setAttribute("id", trans.scriptId);
10688             this.head.appendChild(script);
10689
10690             this.trans = trans;
10691         }else{
10692             callback.call(scope||this, null, arg, false);
10693         }
10694     },
10695
10696     // private
10697     isLoading : function(){
10698         return this.trans ? true : false;
10699     },
10700
10701     /**
10702      * Abort the current server request.
10703      */
10704     abort : function(){
10705         if(this.isLoading()){
10706             this.destroyTrans(this.trans);
10707         }
10708     },
10709
10710     // private
10711     destroyTrans : function(trans, isLoaded){
10712         this.head.removeChild(document.getElementById(trans.scriptId));
10713         clearTimeout(trans.timeoutId);
10714         if(isLoaded){
10715             window[trans.cb] = undefined;
10716             try{
10717                 delete window[trans.cb];
10718             }catch(e){}
10719         }else{
10720             // if hasn't been loaded, wait for load to remove it to prevent script error
10721             window[trans.cb] = function(){
10722                 window[trans.cb] = undefined;
10723                 try{
10724                     delete window[trans.cb];
10725                 }catch(e){}
10726             };
10727         }
10728     },
10729
10730     // private
10731     handleResponse : function(o, trans){
10732         this.trans = false;
10733         this.destroyTrans(trans, true);
10734         var result;
10735         try {
10736             result = trans.reader.readRecords(o);
10737         }catch(e){
10738             this.fireEvent("loadexception", this, o, trans.arg, e);
10739             trans.callback.call(trans.scope||window, null, trans.arg, false);
10740             return;
10741         }
10742         this.fireEvent("load", this, o, trans.arg);
10743         trans.callback.call(trans.scope||window, result, trans.arg, true);
10744     },
10745
10746     // private
10747     handleFailure : function(trans){
10748         this.trans = false;
10749         this.destroyTrans(trans, false);
10750         this.fireEvent("loadexception", this, null, trans.arg);
10751         trans.callback.call(trans.scope||window, null, trans.arg, false);
10752     }
10753 });/*
10754  * Based on:
10755  * Ext JS Library 1.1.1
10756  * Copyright(c) 2006-2007, Ext JS, LLC.
10757  *
10758  * Originally Released Under LGPL - original licence link has changed is not relivant.
10759  *
10760  * Fork - LGPL
10761  * <script type="text/javascript">
10762  */
10763
10764 /**
10765  * @class Roo.data.JsonReader
10766  * @extends Roo.data.DataReader
10767  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10768  * based on mappings in a provided Roo.data.Record constructor.
10769  * 
10770  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10771  * in the reply previously. 
10772  * 
10773  * <p>
10774  * Example code:
10775  * <pre><code>
10776 var RecordDef = Roo.data.Record.create([
10777     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10778     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10779 ]);
10780 var myReader = new Roo.data.JsonReader({
10781     totalProperty: "results",    // The property which contains the total dataset size (optional)
10782     root: "rows",                // The property which contains an Array of row objects
10783     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10784 }, RecordDef);
10785 </code></pre>
10786  * <p>
10787  * This would consume a JSON file like this:
10788  * <pre><code>
10789 { 'results': 2, 'rows': [
10790     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10791     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10792 }
10793 </code></pre>
10794  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10795  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10796  * paged from the remote server.
10797  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10798  * @cfg {String} root name of the property which contains the Array of row objects.
10799  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10800  * @cfg {Array} fields Array of field definition objects
10801  * @constructor
10802  * Create a new JsonReader
10803  * @param {Object} meta Metadata configuration options
10804  * @param {Object} recordType Either an Array of field definition objects,
10805  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10806  */
10807 Roo.data.JsonReader = function(meta, recordType){
10808     
10809     meta = meta || {};
10810     // set some defaults:
10811     Roo.applyIf(meta, {
10812         totalProperty: 'total',
10813         successProperty : 'success',
10814         root : 'data',
10815         id : 'id'
10816     });
10817     
10818     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10819 };
10820 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10821     
10822     /**
10823      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10824      * Used by Store query builder to append _requestMeta to params.
10825      * 
10826      */
10827     metaFromRemote : false,
10828     /**
10829      * This method is only used by a DataProxy which has retrieved data from a remote server.
10830      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10831      * @return {Object} data A data block which is used by an Roo.data.Store object as
10832      * a cache of Roo.data.Records.
10833      */
10834     read : function(response){
10835         var json = response.responseText;
10836        
10837         var o = /* eval:var:o */ eval("("+json+")");
10838         if(!o) {
10839             throw {message: "JsonReader.read: Json object not found"};
10840         }
10841         
10842         if(o.metaData){
10843             
10844             delete this.ef;
10845             this.metaFromRemote = true;
10846             this.meta = o.metaData;
10847             this.recordType = Roo.data.Record.create(o.metaData.fields);
10848             this.onMetaChange(this.meta, this.recordType, o);
10849         }
10850         return this.readRecords(o);
10851     },
10852
10853     // private function a store will implement
10854     onMetaChange : function(meta, recordType, o){
10855
10856     },
10857
10858     /**
10859          * @ignore
10860          */
10861     simpleAccess: function(obj, subsc) {
10862         return obj[subsc];
10863     },
10864
10865         /**
10866          * @ignore
10867          */
10868     getJsonAccessor: function(){
10869         var re = /[\[\.]/;
10870         return function(expr) {
10871             try {
10872                 return(re.test(expr))
10873                     ? new Function("obj", "return obj." + expr)
10874                     : function(obj){
10875                         return obj[expr];
10876                     };
10877             } catch(e){}
10878             return Roo.emptyFn;
10879         };
10880     }(),
10881
10882     /**
10883      * Create a data block containing Roo.data.Records from an XML document.
10884      * @param {Object} o An object which contains an Array of row objects in the property specified
10885      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10886      * which contains the total size of the dataset.
10887      * @return {Object} data A data block which is used by an Roo.data.Store object as
10888      * a cache of Roo.data.Records.
10889      */
10890     readRecords : function(o){
10891         /**
10892          * After any data loads, the raw JSON data is available for further custom processing.
10893          * @type Object
10894          */
10895         this.o = o;
10896         var s = this.meta, Record = this.recordType,
10897             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10898
10899 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10900         if (!this.ef) {
10901             if(s.totalProperty) {
10902                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10903                 }
10904                 if(s.successProperty) {
10905                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10906                 }
10907                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10908                 if (s.id) {
10909                         var g = this.getJsonAccessor(s.id);
10910                         this.getId = function(rec) {
10911                                 var r = g(rec);  
10912                                 return (r === undefined || r === "") ? null : r;
10913                         };
10914                 } else {
10915                         this.getId = function(){return null;};
10916                 }
10917             this.ef = [];
10918             for(var jj = 0; jj < fl; jj++){
10919                 f = fi[jj];
10920                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10921                 this.ef[jj] = this.getJsonAccessor(map);
10922             }
10923         }
10924
10925         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10926         if(s.totalProperty){
10927             var vt = parseInt(this.getTotal(o), 10);
10928             if(!isNaN(vt)){
10929                 totalRecords = vt;
10930             }
10931         }
10932         if(s.successProperty){
10933             var vs = this.getSuccess(o);
10934             if(vs === false || vs === 'false'){
10935                 success = false;
10936             }
10937         }
10938         var records = [];
10939         for(var i = 0; i < c; i++){
10940                 var n = root[i];
10941             var values = {};
10942             var id = this.getId(n);
10943             for(var j = 0; j < fl; j++){
10944                 f = fi[j];
10945             var v = this.ef[j](n);
10946             if (!f.convert) {
10947                 Roo.log('missing convert for ' + f.name);
10948                 Roo.log(f);
10949                 continue;
10950             }
10951             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10952             }
10953             var record = new Record(values, id);
10954             record.json = n;
10955             records[i] = record;
10956         }
10957         return {
10958             raw : o,
10959             success : success,
10960             records : records,
10961             totalRecords : totalRecords
10962         };
10963     }
10964 });/*
10965  * Based on:
10966  * Ext JS Library 1.1.1
10967  * Copyright(c) 2006-2007, Ext JS, LLC.
10968  *
10969  * Originally Released Under LGPL - original licence link has changed is not relivant.
10970  *
10971  * Fork - LGPL
10972  * <script type="text/javascript">
10973  */
10974
10975 /**
10976  * @class Roo.data.ArrayReader
10977  * @extends Roo.data.DataReader
10978  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10979  * Each element of that Array represents a row of data fields. The
10980  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10981  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10982  * <p>
10983  * Example code:.
10984  * <pre><code>
10985 var RecordDef = Roo.data.Record.create([
10986     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10987     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10988 ]);
10989 var myReader = new Roo.data.ArrayReader({
10990     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10991 }, RecordDef);
10992 </code></pre>
10993  * <p>
10994  * This would consume an Array like this:
10995  * <pre><code>
10996 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10997   </code></pre>
10998  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10999  * @constructor
11000  * Create a new JsonReader
11001  * @param {Object} meta Metadata configuration options.
11002  * @param {Object} recordType Either an Array of field definition objects
11003  * as specified to {@link Roo.data.Record#create},
11004  * or an {@link Roo.data.Record} object
11005  * created using {@link Roo.data.Record#create}.
11006  */
11007 Roo.data.ArrayReader = function(meta, recordType){
11008     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11009 };
11010
11011 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11012     /**
11013      * Create a data block containing Roo.data.Records from an XML document.
11014      * @param {Object} o An Array of row objects which represents the dataset.
11015      * @return {Object} data A data block which is used by an Roo.data.Store object as
11016      * a cache of Roo.data.Records.
11017      */
11018     readRecords : function(o){
11019         var sid = this.meta ? this.meta.id : null;
11020         var recordType = this.recordType, fields = recordType.prototype.fields;
11021         var records = [];
11022         var root = o;
11023             for(var i = 0; i < root.length; i++){
11024                     var n = root[i];
11025                 var values = {};
11026                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11027                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11028                 var f = fields.items[j];
11029                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11030                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11031                 v = f.convert(v);
11032                 values[f.name] = v;
11033             }
11034                 var record = new recordType(values, id);
11035                 record.json = n;
11036                 records[records.length] = record;
11037             }
11038             return {
11039                 records : records,
11040                 totalRecords : records.length
11041             };
11042     }
11043 });/*
11044  * - LGPL
11045  * * 
11046  */
11047
11048 /**
11049  * @class Roo.bootstrap.ComboBox
11050  * @extends Roo.bootstrap.TriggerField
11051  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11052  * @cfg {Boolean} append (true|false) default false
11053  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11054  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11055  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11056  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11057  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11058  * @cfg {Boolean} animate default true
11059  * @cfg {Boolean} emptyResultText only for touch device
11060  * @constructor
11061  * Create a new ComboBox.
11062  * @param {Object} config Configuration options
11063  */
11064 Roo.bootstrap.ComboBox = function(config){
11065     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11066     this.addEvents({
11067         /**
11068          * @event expand
11069          * Fires when the dropdown list is expanded
11070              * @param {Roo.bootstrap.ComboBox} combo This combo box
11071              */
11072         'expand' : true,
11073         /**
11074          * @event collapse
11075          * Fires when the dropdown list is collapsed
11076              * @param {Roo.bootstrap.ComboBox} combo This combo box
11077              */
11078         'collapse' : true,
11079         /**
11080          * @event beforeselect
11081          * Fires before a list item is selected. Return false to cancel the selection.
11082              * @param {Roo.bootstrap.ComboBox} combo This combo box
11083              * @param {Roo.data.Record} record The data record returned from the underlying store
11084              * @param {Number} index The index of the selected item in the dropdown list
11085              */
11086         'beforeselect' : true,
11087         /**
11088          * @event select
11089          * Fires when a list item is selected
11090              * @param {Roo.bootstrap.ComboBox} combo This combo box
11091              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11092              * @param {Number} index The index of the selected item in the dropdown list
11093              */
11094         'select' : true,
11095         /**
11096          * @event beforequery
11097          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11098          * The event object passed has these properties:
11099              * @param {Roo.bootstrap.ComboBox} combo This combo box
11100              * @param {String} query The query
11101              * @param {Boolean} forceAll true to force "all" query
11102              * @param {Boolean} cancel true to cancel the query
11103              * @param {Object} e The query event object
11104              */
11105         'beforequery': true,
11106          /**
11107          * @event add
11108          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11109              * @param {Roo.bootstrap.ComboBox} combo This combo box
11110              */
11111         'add' : true,
11112         /**
11113          * @event edit
11114          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11115              * @param {Roo.bootstrap.ComboBox} combo This combo box
11116              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11117              */
11118         'edit' : true,
11119         /**
11120          * @event remove
11121          * Fires when the remove value from the combobox array
11122              * @param {Roo.bootstrap.ComboBox} combo This combo box
11123              */
11124         'remove' : true,
11125         /**
11126          * @event specialfilter
11127          * Fires when specialfilter
11128             * @param {Roo.bootstrap.ComboBox} combo This combo box
11129             */
11130         'specialfilter' : true
11131         
11132     });
11133     
11134     this.item = [];
11135     this.tickItems = [];
11136     
11137     this.selectedIndex = -1;
11138     if(this.mode == 'local'){
11139         if(config.queryDelay === undefined){
11140             this.queryDelay = 10;
11141         }
11142         if(config.minChars === undefined){
11143             this.minChars = 0;
11144         }
11145     }
11146 };
11147
11148 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11149      
11150     /**
11151      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11152      * rendering into an Roo.Editor, defaults to false)
11153      */
11154     /**
11155      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11156      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11157      */
11158     /**
11159      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11160      */
11161     /**
11162      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11163      * the dropdown list (defaults to undefined, with no header element)
11164      */
11165
11166      /**
11167      * @cfg {String/Roo.Template} tpl The template to use to render the output
11168      */
11169      
11170      /**
11171      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11172      */
11173     listWidth: undefined,
11174     /**
11175      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11176      * mode = 'remote' or 'text' if mode = 'local')
11177      */
11178     displayField: undefined,
11179     
11180     /**
11181      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11182      * mode = 'remote' or 'value' if mode = 'local'). 
11183      * Note: use of a valueField requires the user make a selection
11184      * in order for a value to be mapped.
11185      */
11186     valueField: undefined,
11187     
11188     
11189     /**
11190      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11191      * field's data value (defaults to the underlying DOM element's name)
11192      */
11193     hiddenName: undefined,
11194     /**
11195      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11196      */
11197     listClass: '',
11198     /**
11199      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11200      */
11201     selectedClass: 'active',
11202     
11203     /**
11204      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11205      */
11206     shadow:'sides',
11207     /**
11208      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11209      * anchor positions (defaults to 'tl-bl')
11210      */
11211     listAlign: 'tl-bl?',
11212     /**
11213      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11214      */
11215     maxHeight: 300,
11216     /**
11217      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11218      * query specified by the allQuery config option (defaults to 'query')
11219      */
11220     triggerAction: 'query',
11221     /**
11222      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11223      * (defaults to 4, does not apply if editable = false)
11224      */
11225     minChars : 4,
11226     /**
11227      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11228      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11229      */
11230     typeAhead: false,
11231     /**
11232      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11233      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11234      */
11235     queryDelay: 500,
11236     /**
11237      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11238      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11239      */
11240     pageSize: 0,
11241     /**
11242      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11243      * when editable = true (defaults to false)
11244      */
11245     selectOnFocus:false,
11246     /**
11247      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11248      */
11249     queryParam: 'query',
11250     /**
11251      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11252      * when mode = 'remote' (defaults to 'Loading...')
11253      */
11254     loadingText: 'Loading...',
11255     /**
11256      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11257      */
11258     resizable: false,
11259     /**
11260      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11261      */
11262     handleHeight : 8,
11263     /**
11264      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11265      * traditional select (defaults to true)
11266      */
11267     editable: true,
11268     /**
11269      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11270      */
11271     allQuery: '',
11272     /**
11273      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11274      */
11275     mode: 'remote',
11276     /**
11277      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11278      * listWidth has a higher value)
11279      */
11280     minListWidth : 70,
11281     /**
11282      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11283      * allow the user to set arbitrary text into the field (defaults to false)
11284      */
11285     forceSelection:false,
11286     /**
11287      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11288      * if typeAhead = true (defaults to 250)
11289      */
11290     typeAheadDelay : 250,
11291     /**
11292      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11293      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11294      */
11295     valueNotFoundText : undefined,
11296     /**
11297      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11298      */
11299     blockFocus : false,
11300     
11301     /**
11302      * @cfg {Boolean} disableClear Disable showing of clear button.
11303      */
11304     disableClear : false,
11305     /**
11306      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11307      */
11308     alwaysQuery : false,
11309     
11310     /**
11311      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11312      */
11313     multiple : false,
11314     
11315     /**
11316      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11317      */
11318     invalidClass : "has-warning",
11319     
11320     /**
11321      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11322      */
11323     validClass : "has-success",
11324     
11325     /**
11326      * @cfg {Boolean} specialFilter (true|false) special filter default false
11327      */
11328     specialFilter : false,
11329     
11330     /**
11331      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11332      */
11333     mobileTouchView : true,
11334     
11335     //private
11336     addicon : false,
11337     editicon: false,
11338     
11339     page: 0,
11340     hasQuery: false,
11341     append: false,
11342     loadNext: false,
11343     autoFocus : true,
11344     tickable : false,
11345     btnPosition : 'right',
11346     triggerList : true,
11347     showToggleBtn : true,
11348     animate : true,
11349     emptyResultText: 'Empty',
11350     // element that contains real text value.. (when hidden is used..)
11351     
11352     getAutoCreate : function()
11353     {
11354         var cfg = false;
11355         
11356         /*
11357          * Touch Devices
11358          */
11359         
11360         if(Roo.isTouch && this.mobileTouchView){
11361             cfg = this.getAutoCreateTouchView();
11362             return cfg;;
11363         }
11364         
11365         /*
11366          *  Normal ComboBox
11367          */
11368         if(!this.tickable){
11369             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11370             return cfg;
11371         }
11372         
11373         /*
11374          *  ComboBox with tickable selections
11375          */
11376              
11377         var align = this.labelAlign || this.parentLabelAlign();
11378         
11379         cfg = {
11380             cls : 'form-group roo-combobox-tickable' //input-group
11381         };
11382         
11383         var buttons = {
11384             tag : 'div',
11385             cls : 'tickable-buttons',
11386             cn : [
11387                 {
11388                     tag : 'button',
11389                     type : 'button',
11390                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11391                     html : 'Edit'
11392                 },
11393                 {
11394                     tag : 'button',
11395                     type : 'button',
11396                     name : 'ok',
11397                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11398                     html : 'Done'
11399                 },
11400                 {
11401                     tag : 'button',
11402                     type : 'button',
11403                     name : 'cancel',
11404                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11405                     html : 'Cancel'
11406                 }
11407             ]
11408         };
11409         
11410         if(this.editable){
11411             buttons.cn.unshift({
11412                 tag: 'input',
11413                 cls: 'select2-search-field-input'
11414             });
11415         }
11416         
11417         var _this = this;
11418         
11419         Roo.each(buttons.cn, function(c){
11420             if (_this.size) {
11421                 c.cls += ' btn-' + _this.size;
11422             }
11423
11424             if (_this.disabled) {
11425                 c.disabled = true;
11426             }
11427         });
11428         
11429         var box = {
11430             tag: 'div',
11431             cn: [
11432                 {
11433                     tag: 'input',
11434                     type : 'hidden',
11435                     cls: 'form-hidden-field'
11436                 },
11437                 {
11438                     tag: 'ul',
11439                     cls: 'select2-choices',
11440                     cn:[
11441                         {
11442                             tag: 'li',
11443                             cls: 'select2-search-field',
11444                             cn: [
11445
11446                                 buttons
11447                             ]
11448                         }
11449                     ]
11450                 }
11451             ]
11452         }
11453         
11454         var combobox = {
11455             cls: 'select2-container input-group select2-container-multi',
11456             cn: [
11457                 box
11458 //                {
11459 //                    tag: 'ul',
11460 //                    cls: 'typeahead typeahead-long dropdown-menu',
11461 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11462 //                }
11463             ]
11464         };
11465         
11466         if(this.hasFeedback && !this.allowBlank){
11467             
11468             var feedback = {
11469                 tag: 'span',
11470                 cls: 'glyphicon form-control-feedback'
11471             };
11472
11473             combobox.cn.push(feedback);
11474         }
11475         
11476         if (align ==='left' && this.fieldLabel.length) {
11477             
11478                 Roo.log("left and has label");
11479                 cfg.cn = [
11480                     
11481                     {
11482                         tag: 'label',
11483                         'for' :  id,
11484                         cls : 'control-label col-sm-' + this.labelWidth,
11485                         html : this.fieldLabel
11486                         
11487                     },
11488                     {
11489                         cls : "col-sm-" + (12 - this.labelWidth), 
11490                         cn: [
11491                             combobox
11492                         ]
11493                     }
11494                     
11495                 ];
11496         } else if ( this.fieldLabel.length) {
11497                 Roo.log(" label");
11498                  cfg.cn = [
11499                    
11500                     {
11501                         tag: 'label',
11502                         //cls : 'input-group-addon',
11503                         html : this.fieldLabel
11504                         
11505                     },
11506                     
11507                     combobox
11508                     
11509                 ];
11510
11511         } else {
11512             
11513                 Roo.log(" no label && no align");
11514                 cfg = combobox
11515                      
11516                 
11517         }
11518          
11519         var settings=this;
11520         ['xs','sm','md','lg'].map(function(size){
11521             if (settings[size]) {
11522                 cfg.cls += ' col-' + size + '-' + settings[size];
11523             }
11524         });
11525         
11526         return cfg;
11527         
11528     },
11529     
11530     // private
11531     initEvents: function()
11532     {
11533         
11534         if (!this.store) {
11535             throw "can not find store for combo";
11536         }
11537         
11538         this.store = Roo.factory(this.store, Roo.data);
11539         
11540         /*
11541          * Touch Devices
11542          */
11543         
11544         if(Roo.isTouch && this.mobileTouchView){
11545             this.initTouchView();
11546             return;
11547         }
11548         
11549         if(this.tickable){
11550             this.initTickableEvents();
11551             return;
11552         }
11553         
11554         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11555         
11556         if(this.hiddenName){
11557             
11558             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11559             
11560             this.hiddenField.dom.value =
11561                 this.hiddenValue !== undefined ? this.hiddenValue :
11562                 this.value !== undefined ? this.value : '';
11563
11564             // prevent input submission
11565             this.el.dom.removeAttribute('name');
11566             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11567              
11568              
11569         }
11570         //if(Roo.isGecko){
11571         //    this.el.dom.setAttribute('autocomplete', 'off');
11572         //}
11573         
11574         var cls = 'x-combo-list';
11575         
11576         //this.list = new Roo.Layer({
11577         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11578         //});
11579         
11580         var _this = this;
11581         
11582         (function(){
11583             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11584             _this.list.setWidth(lw);
11585         }).defer(100);
11586         
11587         this.list.on('mouseover', this.onViewOver, this);
11588         this.list.on('mousemove', this.onViewMove, this);
11589         
11590         this.list.on('scroll', this.onViewScroll, this);
11591         
11592         /*
11593         this.list.swallowEvent('mousewheel');
11594         this.assetHeight = 0;
11595
11596         if(this.title){
11597             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11598             this.assetHeight += this.header.getHeight();
11599         }
11600
11601         this.innerList = this.list.createChild({cls:cls+'-inner'});
11602         this.innerList.on('mouseover', this.onViewOver, this);
11603         this.innerList.on('mousemove', this.onViewMove, this);
11604         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11605         
11606         if(this.allowBlank && !this.pageSize && !this.disableClear){
11607             this.footer = this.list.createChild({cls:cls+'-ft'});
11608             this.pageTb = new Roo.Toolbar(this.footer);
11609            
11610         }
11611         if(this.pageSize){
11612             this.footer = this.list.createChild({cls:cls+'-ft'});
11613             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11614                     {pageSize: this.pageSize});
11615             
11616         }
11617         
11618         if (this.pageTb && this.allowBlank && !this.disableClear) {
11619             var _this = this;
11620             this.pageTb.add(new Roo.Toolbar.Fill(), {
11621                 cls: 'x-btn-icon x-btn-clear',
11622                 text: '&#160;',
11623                 handler: function()
11624                 {
11625                     _this.collapse();
11626                     _this.clearValue();
11627                     _this.onSelect(false, -1);
11628                 }
11629             });
11630         }
11631         if (this.footer) {
11632             this.assetHeight += this.footer.getHeight();
11633         }
11634         */
11635             
11636         if(!this.tpl){
11637             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11638         }
11639
11640         this.view = new Roo.View(this.list, this.tpl, {
11641             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11642         });
11643         //this.view.wrapEl.setDisplayed(false);
11644         this.view.on('click', this.onViewClick, this);
11645         
11646         
11647         
11648         this.store.on('beforeload', this.onBeforeLoad, this);
11649         this.store.on('load', this.onLoad, this);
11650         this.store.on('loadexception', this.onLoadException, this);
11651         /*
11652         if(this.resizable){
11653             this.resizer = new Roo.Resizable(this.list,  {
11654                pinned:true, handles:'se'
11655             });
11656             this.resizer.on('resize', function(r, w, h){
11657                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11658                 this.listWidth = w;
11659                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11660                 this.restrictHeight();
11661             }, this);
11662             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11663         }
11664         */
11665         if(!this.editable){
11666             this.editable = true;
11667             this.setEditable(false);
11668         }
11669         
11670         /*
11671         
11672         if (typeof(this.events.add.listeners) != 'undefined') {
11673             
11674             this.addicon = this.wrap.createChild(
11675                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11676        
11677             this.addicon.on('click', function(e) {
11678                 this.fireEvent('add', this);
11679             }, this);
11680         }
11681         if (typeof(this.events.edit.listeners) != 'undefined') {
11682             
11683             this.editicon = this.wrap.createChild(
11684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11685             if (this.addicon) {
11686                 this.editicon.setStyle('margin-left', '40px');
11687             }
11688             this.editicon.on('click', function(e) {
11689                 
11690                 // we fire even  if inothing is selected..
11691                 this.fireEvent('edit', this, this.lastData );
11692                 
11693             }, this);
11694         }
11695         */
11696         
11697         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11698             "up" : function(e){
11699                 this.inKeyMode = true;
11700                 this.selectPrev();
11701             },
11702
11703             "down" : function(e){
11704                 if(!this.isExpanded()){
11705                     this.onTriggerClick();
11706                 }else{
11707                     this.inKeyMode = true;
11708                     this.selectNext();
11709                 }
11710             },
11711
11712             "enter" : function(e){
11713 //                this.onViewClick();
11714                 //return true;
11715                 this.collapse();
11716                 
11717                 if(this.fireEvent("specialkey", this, e)){
11718                     this.onViewClick(false);
11719                 }
11720                 
11721                 return true;
11722             },
11723
11724             "esc" : function(e){
11725                 this.collapse();
11726             },
11727
11728             "tab" : function(e){
11729                 this.collapse();
11730                 
11731                 if(this.fireEvent("specialkey", this, e)){
11732                     this.onViewClick(false);
11733                 }
11734                 
11735                 return true;
11736             },
11737
11738             scope : this,
11739
11740             doRelay : function(foo, bar, hname){
11741                 if(hname == 'down' || this.scope.isExpanded()){
11742                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11743                 }
11744                 return true;
11745             },
11746
11747             forceKeyDown: true
11748         });
11749         
11750         
11751         this.queryDelay = Math.max(this.queryDelay || 10,
11752                 this.mode == 'local' ? 10 : 250);
11753         
11754         
11755         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11756         
11757         if(this.typeAhead){
11758             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11759         }
11760         if(this.editable !== false){
11761             this.inputEl().on("keyup", this.onKeyUp, this);
11762         }
11763         if(this.forceSelection){
11764             this.inputEl().on('blur', this.doForce, this);
11765         }
11766         
11767         if(this.multiple){
11768             this.choices = this.el.select('ul.select2-choices', true).first();
11769             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11770         }
11771     },
11772     
11773     initTickableEvents: function()
11774     {   
11775         this.createList();
11776         
11777         if(this.hiddenName){
11778             
11779             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11780             
11781             this.hiddenField.dom.value =
11782                 this.hiddenValue !== undefined ? this.hiddenValue :
11783                 this.value !== undefined ? this.value : '';
11784
11785             // prevent input submission
11786             this.el.dom.removeAttribute('name');
11787             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11788              
11789              
11790         }
11791         
11792 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11793         
11794         this.choices = this.el.select('ul.select2-choices', true).first();
11795         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11796         if(this.triggerList){
11797             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11798         }
11799          
11800         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11801         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11802         
11803         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11804         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11805         
11806         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11807         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11808         
11809         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11810         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11811         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11812         
11813         this.okBtn.hide();
11814         this.cancelBtn.hide();
11815         
11816         var _this = this;
11817         
11818         (function(){
11819             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11820             _this.list.setWidth(lw);
11821         }).defer(100);
11822         
11823         this.list.on('mouseover', this.onViewOver, this);
11824         this.list.on('mousemove', this.onViewMove, this);
11825         
11826         this.list.on('scroll', this.onViewScroll, this);
11827         
11828         if(!this.tpl){
11829             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>';
11830         }
11831
11832         this.view = new Roo.View(this.list, this.tpl, {
11833             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11834         });
11835         
11836         //this.view.wrapEl.setDisplayed(false);
11837         this.view.on('click', this.onViewClick, this);
11838         
11839         
11840         
11841         this.store.on('beforeload', this.onBeforeLoad, this);
11842         this.store.on('load', this.onLoad, this);
11843         this.store.on('loadexception', this.onLoadException, this);
11844         
11845         if(this.editable){
11846             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11847                 "up" : function(e){
11848                     this.inKeyMode = true;
11849                     this.selectPrev();
11850                 },
11851
11852                 "down" : function(e){
11853                     this.inKeyMode = true;
11854                     this.selectNext();
11855                 },
11856
11857                 "enter" : function(e){
11858                     if(this.fireEvent("specialkey", this, e)){
11859                         this.onViewClick(false);
11860                     }
11861                     
11862                     return true;
11863                 },
11864
11865                 "esc" : function(e){
11866                     this.onTickableFooterButtonClick(e, false, false);
11867                 },
11868
11869                 "tab" : function(e){
11870                     this.fireEvent("specialkey", this, e);
11871                     
11872                     this.onTickableFooterButtonClick(e, false, false);
11873                     
11874                     return true;
11875                 },
11876
11877                 scope : this,
11878
11879                 doRelay : function(e, fn, key){
11880                     if(this.scope.isExpanded()){
11881                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11882                     }
11883                     return true;
11884                 },
11885
11886                 forceKeyDown: true
11887             });
11888         }
11889         
11890         this.queryDelay = Math.max(this.queryDelay || 10,
11891                 this.mode == 'local' ? 10 : 250);
11892         
11893         
11894         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11895         
11896         if(this.typeAhead){
11897             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11898         }
11899         
11900         if(this.editable !== false){
11901             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11902         }
11903         
11904     },
11905
11906     onDestroy : function(){
11907         if(this.view){
11908             this.view.setStore(null);
11909             this.view.el.removeAllListeners();
11910             this.view.el.remove();
11911             this.view.purgeListeners();
11912         }
11913         if(this.list){
11914             this.list.dom.innerHTML  = '';
11915         }
11916         
11917         if(this.store){
11918             this.store.un('beforeload', this.onBeforeLoad, this);
11919             this.store.un('load', this.onLoad, this);
11920             this.store.un('loadexception', this.onLoadException, this);
11921         }
11922         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11923     },
11924
11925     // private
11926     fireKey : function(e){
11927         if(e.isNavKeyPress() && !this.list.isVisible()){
11928             this.fireEvent("specialkey", this, e);
11929         }
11930     },
11931
11932     // private
11933     onResize: function(w, h){
11934 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11935 //        
11936 //        if(typeof w != 'number'){
11937 //            // we do not handle it!?!?
11938 //            return;
11939 //        }
11940 //        var tw = this.trigger.getWidth();
11941 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11942 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11943 //        var x = w - tw;
11944 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11945 //            
11946 //        //this.trigger.setStyle('left', x+'px');
11947 //        
11948 //        if(this.list && this.listWidth === undefined){
11949 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11950 //            this.list.setWidth(lw);
11951 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11952 //        }
11953         
11954     
11955         
11956     },
11957
11958     /**
11959      * Allow or prevent the user from directly editing the field text.  If false is passed,
11960      * the user will only be able to select from the items defined in the dropdown list.  This method
11961      * is the runtime equivalent of setting the 'editable' config option at config time.
11962      * @param {Boolean} value True to allow the user to directly edit the field text
11963      */
11964     setEditable : function(value){
11965         if(value == this.editable){
11966             return;
11967         }
11968         this.editable = value;
11969         if(!value){
11970             this.inputEl().dom.setAttribute('readOnly', true);
11971             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11972             this.inputEl().addClass('x-combo-noedit');
11973         }else{
11974             this.inputEl().dom.setAttribute('readOnly', false);
11975             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11976             this.inputEl().removeClass('x-combo-noedit');
11977         }
11978     },
11979
11980     // private
11981     
11982     onBeforeLoad : function(combo,opts){
11983         if(!this.hasFocus){
11984             return;
11985         }
11986          if (!opts.add) {
11987             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11988          }
11989         this.restrictHeight();
11990         this.selectedIndex = -1;
11991     },
11992
11993     // private
11994     onLoad : function(){
11995         
11996         this.hasQuery = false;
11997         
11998         if(!this.hasFocus){
11999             return;
12000         }
12001         
12002         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12003             this.loading.hide();
12004         }
12005              
12006         if(this.store.getCount() > 0){
12007             this.expand();
12008             this.restrictHeight();
12009             if(this.lastQuery == this.allQuery){
12010                 if(this.editable && !this.tickable){
12011                     this.inputEl().dom.select();
12012                 }
12013                 
12014                 if(
12015                     !this.selectByValue(this.value, true) &&
12016                     this.autoFocus && 
12017                     (
12018                         !this.store.lastOptions ||
12019                         typeof(this.store.lastOptions.add) == 'undefined' || 
12020                         this.store.lastOptions.add != true
12021                     )
12022                 ){
12023                     this.select(0, true);
12024                 }
12025             }else{
12026                 if(this.autoFocus){
12027                     this.selectNext();
12028                 }
12029                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12030                     this.taTask.delay(this.typeAheadDelay);
12031                 }
12032             }
12033         }else{
12034             this.onEmptyResults();
12035         }
12036         
12037         //this.el.focus();
12038     },
12039     // private
12040     onLoadException : function()
12041     {
12042         this.hasQuery = false;
12043         
12044         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12045             this.loading.hide();
12046         }
12047         
12048         if(this.tickable && this.editable){
12049             return;
12050         }
12051         
12052         this.collapse();
12053         
12054         Roo.log(this.store.reader.jsonData);
12055         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12056             // fixme
12057             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12058         }
12059         
12060         
12061     },
12062     // private
12063     onTypeAhead : function(){
12064         if(this.store.getCount() > 0){
12065             var r = this.store.getAt(0);
12066             var newValue = r.data[this.displayField];
12067             var len = newValue.length;
12068             var selStart = this.getRawValue().length;
12069             
12070             if(selStart != len){
12071                 this.setRawValue(newValue);
12072                 this.selectText(selStart, newValue.length);
12073             }
12074         }
12075     },
12076
12077     // private
12078     onSelect : function(record, index){
12079         
12080         if(this.fireEvent('beforeselect', this, record, index) !== false){
12081         
12082             this.setFromData(index > -1 ? record.data : false);
12083             
12084             this.collapse();
12085             this.fireEvent('select', this, record, index);
12086         }
12087     },
12088
12089     /**
12090      * Returns the currently selected field value or empty string if no value is set.
12091      * @return {String} value The selected value
12092      */
12093     getValue : function(){
12094         
12095         if(this.multiple){
12096             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12097         }
12098         
12099         if(this.valueField){
12100             return typeof this.value != 'undefined' ? this.value : '';
12101         }else{
12102             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12103         }
12104     },
12105
12106     /**
12107      * Clears any text/value currently set in the field
12108      */
12109     clearValue : function(){
12110         if(this.hiddenField){
12111             this.hiddenField.dom.value = '';
12112         }
12113         this.value = '';
12114         this.setRawValue('');
12115         this.lastSelectionText = '';
12116         this.lastData = false;
12117         
12118         var close = this.closeTriggerEl();
12119         
12120         if(close){
12121             close.hide();
12122         }
12123         
12124     },
12125
12126     /**
12127      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12128      * will be displayed in the field.  If the value does not match the data value of an existing item,
12129      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12130      * Otherwise the field will be blank (although the value will still be set).
12131      * @param {String} value The value to match
12132      */
12133     setValue : function(v){
12134         if(this.multiple){
12135             this.syncValue();
12136             return;
12137         }
12138         
12139         var text = v;
12140         if(this.valueField){
12141             var r = this.findRecord(this.valueField, v);
12142             if(r){
12143                 text = r.data[this.displayField];
12144             }else if(this.valueNotFoundText !== undefined){
12145                 text = this.valueNotFoundText;
12146             }
12147         }
12148         this.lastSelectionText = text;
12149         if(this.hiddenField){
12150             this.hiddenField.dom.value = v;
12151         }
12152         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12153         this.value = v;
12154         
12155         var close = this.closeTriggerEl();
12156         
12157         if(close){
12158             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12159         }
12160     },
12161     /**
12162      * @property {Object} the last set data for the element
12163      */
12164     
12165     lastData : false,
12166     /**
12167      * Sets the value of the field based on a object which is related to the record format for the store.
12168      * @param {Object} value the value to set as. or false on reset?
12169      */
12170     setFromData : function(o){
12171         
12172         if(this.multiple){
12173             this.addItem(o);
12174             return;
12175         }
12176             
12177         var dv = ''; // display value
12178         var vv = ''; // value value..
12179         this.lastData = o;
12180         if (this.displayField) {
12181             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12182         } else {
12183             // this is an error condition!!!
12184             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12185         }
12186         
12187         if(this.valueField){
12188             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12189         }
12190         
12191         var close = this.closeTriggerEl();
12192         
12193         if(close){
12194             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12195         }
12196         
12197         if(this.hiddenField){
12198             this.hiddenField.dom.value = vv;
12199             
12200             this.lastSelectionText = dv;
12201             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12202             this.value = vv;
12203             return;
12204         }
12205         // no hidden field.. - we store the value in 'value', but still display
12206         // display field!!!!
12207         this.lastSelectionText = dv;
12208         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12209         this.value = vv;
12210         
12211         
12212         
12213     },
12214     // private
12215     reset : function(){
12216         // overridden so that last data is reset..
12217         
12218         if(this.multiple){
12219             this.clearItem();
12220             return;
12221         }
12222         
12223         this.setValue(this.originalValue);
12224         this.clearInvalid();
12225         this.lastData = false;
12226         if (this.view) {
12227             this.view.clearSelections();
12228         }
12229     },
12230     // private
12231     findRecord : function(prop, value){
12232         var record;
12233         if(this.store.getCount() > 0){
12234             this.store.each(function(r){
12235                 if(r.data[prop] == value){
12236                     record = r;
12237                     return false;
12238                 }
12239                 return true;
12240             });
12241         }
12242         return record;
12243     },
12244     
12245     getName: function()
12246     {
12247         // returns hidden if it's set..
12248         if (!this.rendered) {return ''};
12249         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12250         
12251     },
12252     // private
12253     onViewMove : function(e, t){
12254         this.inKeyMode = false;
12255     },
12256
12257     // private
12258     onViewOver : function(e, t){
12259         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12260             return;
12261         }
12262         var item = this.view.findItemFromChild(t);
12263         
12264         if(item){
12265             var index = this.view.indexOf(item);
12266             this.select(index, false);
12267         }
12268     },
12269
12270     // private
12271     onViewClick : function(view, doFocus, el, e)
12272     {
12273         var index = this.view.getSelectedIndexes()[0];
12274         
12275         var r = this.store.getAt(index);
12276         
12277         if(this.tickable){
12278             
12279             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12280                 return;
12281             }
12282             
12283             var rm = false;
12284             var _this = this;
12285             
12286             Roo.each(this.tickItems, function(v,k){
12287                 
12288                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12289                     _this.tickItems.splice(k, 1);
12290                     
12291                     if(typeof(e) == 'undefined' && view == false){
12292                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12293                     }
12294                     
12295                     rm = true;
12296                     return;
12297                 }
12298             });
12299             
12300             if(rm){
12301                 return;
12302             }
12303             
12304             this.tickItems.push(r.data);
12305             
12306             if(typeof(e) == 'undefined' && view == false){
12307                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12308             }
12309                     
12310             return;
12311         }
12312         
12313         if(r){
12314             this.onSelect(r, index);
12315         }
12316         if(doFocus !== false && !this.blockFocus){
12317             this.inputEl().focus();
12318         }
12319     },
12320
12321     // private
12322     restrictHeight : function(){
12323         //this.innerList.dom.style.height = '';
12324         //var inner = this.innerList.dom;
12325         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12326         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12327         //this.list.beginUpdate();
12328         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12329         this.list.alignTo(this.inputEl(), this.listAlign);
12330         this.list.alignTo(this.inputEl(), this.listAlign);
12331         //this.list.endUpdate();
12332     },
12333
12334     // private
12335     onEmptyResults : function(){
12336         
12337         if(this.tickable && this.editable){
12338             this.restrictHeight();
12339             return;
12340         }
12341         
12342         this.collapse();
12343     },
12344
12345     /**
12346      * Returns true if the dropdown list is expanded, else false.
12347      */
12348     isExpanded : function(){
12349         return this.list.isVisible();
12350     },
12351
12352     /**
12353      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12354      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12355      * @param {String} value The data value of the item to select
12356      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12357      * selected item if it is not currently in view (defaults to true)
12358      * @return {Boolean} True if the value matched an item in the list, else false
12359      */
12360     selectByValue : function(v, scrollIntoView){
12361         if(v !== undefined && v !== null){
12362             var r = this.findRecord(this.valueField || this.displayField, v);
12363             if(r){
12364                 this.select(this.store.indexOf(r), scrollIntoView);
12365                 return true;
12366             }
12367         }
12368         return false;
12369     },
12370
12371     /**
12372      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12373      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12374      * @param {Number} index The zero-based index of the list item to select
12375      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12376      * selected item if it is not currently in view (defaults to true)
12377      */
12378     select : function(index, scrollIntoView){
12379         this.selectedIndex = index;
12380         this.view.select(index);
12381         if(scrollIntoView !== false){
12382             var el = this.view.getNode(index);
12383             /*
12384              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12385              */
12386             if(el){
12387                 this.list.scrollChildIntoView(el, false);
12388             }
12389         }
12390     },
12391
12392     // private
12393     selectNext : function(){
12394         var ct = this.store.getCount();
12395         if(ct > 0){
12396             if(this.selectedIndex == -1){
12397                 this.select(0);
12398             }else if(this.selectedIndex < ct-1){
12399                 this.select(this.selectedIndex+1);
12400             }
12401         }
12402     },
12403
12404     // private
12405     selectPrev : function(){
12406         var ct = this.store.getCount();
12407         if(ct > 0){
12408             if(this.selectedIndex == -1){
12409                 this.select(0);
12410             }else if(this.selectedIndex != 0){
12411                 this.select(this.selectedIndex-1);
12412             }
12413         }
12414     },
12415
12416     // private
12417     onKeyUp : function(e){
12418         if(this.editable !== false && !e.isSpecialKey()){
12419             this.lastKey = e.getKey();
12420             this.dqTask.delay(this.queryDelay);
12421         }
12422     },
12423
12424     // private
12425     validateBlur : function(){
12426         return !this.list || !this.list.isVisible();   
12427     },
12428
12429     // private
12430     initQuery : function(){
12431         
12432         var v = this.getRawValue();
12433         
12434         if(this.tickable && this.editable){
12435             v = this.tickableInputEl().getValue();
12436         }
12437         
12438         this.doQuery(v);
12439     },
12440
12441     // private
12442     doForce : function(){
12443         if(this.inputEl().dom.value.length > 0){
12444             this.inputEl().dom.value =
12445                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12446              
12447         }
12448     },
12449
12450     /**
12451      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12452      * query allowing the query action to be canceled if needed.
12453      * @param {String} query The SQL query to execute
12454      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12455      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12456      * saved in the current store (defaults to false)
12457      */
12458     doQuery : function(q, forceAll){
12459         
12460         if(q === undefined || q === null){
12461             q = '';
12462         }
12463         var qe = {
12464             query: q,
12465             forceAll: forceAll,
12466             combo: this,
12467             cancel:false
12468         };
12469         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12470             return false;
12471         }
12472         q = qe.query;
12473         
12474         forceAll = qe.forceAll;
12475         if(forceAll === true || (q.length >= this.minChars)){
12476             
12477             this.hasQuery = true;
12478             
12479             if(this.lastQuery != q || this.alwaysQuery){
12480                 this.lastQuery = q;
12481                 if(this.mode == 'local'){
12482                     this.selectedIndex = -1;
12483                     if(forceAll){
12484                         this.store.clearFilter();
12485                     }else{
12486                         
12487                         if(this.specialFilter){
12488                             this.fireEvent('specialfilter', this);
12489                             this.onLoad();
12490                             return;
12491                         }
12492                         
12493                         this.store.filter(this.displayField, q);
12494                     }
12495                     
12496                     this.store.fireEvent("datachanged", this.store);
12497                     
12498                     this.onLoad();
12499                     
12500                     
12501                 }else{
12502                     
12503                     this.store.baseParams[this.queryParam] = q;
12504                     
12505                     var options = {params : this.getParams(q)};
12506                     
12507                     if(this.loadNext){
12508                         options.add = true;
12509                         options.params.start = this.page * this.pageSize;
12510                     }
12511                     
12512                     this.store.load(options);
12513                     
12514                     /*
12515                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12516                      *  we should expand the list on onLoad
12517                      *  so command out it
12518                      */
12519 //                    this.expand();
12520                 }
12521             }else{
12522                 this.selectedIndex = -1;
12523                 this.onLoad();   
12524             }
12525         }
12526         
12527         this.loadNext = false;
12528     },
12529     
12530     // private
12531     getParams : function(q){
12532         var p = {};
12533         //p[this.queryParam] = q;
12534         
12535         if(this.pageSize){
12536             p.start = 0;
12537             p.limit = this.pageSize;
12538         }
12539         return p;
12540     },
12541
12542     /**
12543      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12544      */
12545     collapse : function(){
12546         if(!this.isExpanded()){
12547             return;
12548         }
12549         
12550         this.list.hide();
12551         
12552         if(this.tickable){
12553             this.hasFocus = false;
12554             this.okBtn.hide();
12555             this.cancelBtn.hide();
12556             this.trigger.show();
12557             
12558             if(this.editable){
12559                 this.tickableInputEl().dom.value = '';
12560                 this.tickableInputEl().blur();
12561             }
12562             
12563         }
12564         
12565         Roo.get(document).un('mousedown', this.collapseIf, this);
12566         Roo.get(document).un('mousewheel', this.collapseIf, this);
12567         if (!this.editable) {
12568             Roo.get(document).un('keydown', this.listKeyPress, this);
12569         }
12570         this.fireEvent('collapse', this);
12571     },
12572
12573     // private
12574     collapseIf : function(e){
12575         var in_combo  = e.within(this.el);
12576         var in_list =  e.within(this.list);
12577         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12578         
12579         if (in_combo || in_list || is_list) {
12580             //e.stopPropagation();
12581             return;
12582         }
12583         
12584         if(this.tickable){
12585             this.onTickableFooterButtonClick(e, false, false);
12586         }
12587
12588         this.collapse();
12589         
12590     },
12591
12592     /**
12593      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12594      */
12595     expand : function(){
12596        
12597         if(this.isExpanded() || !this.hasFocus){
12598             return;
12599         }
12600         
12601         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12602         this.list.setWidth(lw);
12603         
12604         
12605          Roo.log('expand');
12606         
12607         this.list.show();
12608         
12609         this.restrictHeight();
12610         
12611         if(this.tickable){
12612             
12613             this.tickItems = Roo.apply([], this.item);
12614             
12615             this.okBtn.show();
12616             this.cancelBtn.show();
12617             this.trigger.hide();
12618             
12619             if(this.editable){
12620                 this.tickableInputEl().focus();
12621             }
12622             
12623         }
12624         
12625         Roo.get(document).on('mousedown', this.collapseIf, this);
12626         Roo.get(document).on('mousewheel', this.collapseIf, this);
12627         if (!this.editable) {
12628             Roo.get(document).on('keydown', this.listKeyPress, this);
12629         }
12630         
12631         this.fireEvent('expand', this);
12632     },
12633
12634     // private
12635     // Implements the default empty TriggerField.onTriggerClick function
12636     onTriggerClick : function(e)
12637     {
12638         Roo.log('trigger click');
12639         
12640         if(this.disabled || !this.triggerList){
12641             return;
12642         }
12643         
12644         this.page = 0;
12645         this.loadNext = false;
12646         
12647         if(this.isExpanded()){
12648             this.collapse();
12649             if (!this.blockFocus) {
12650                 this.inputEl().focus();
12651             }
12652             
12653         }else {
12654             this.hasFocus = true;
12655             if(this.triggerAction == 'all') {
12656                 this.doQuery(this.allQuery, true);
12657             } else {
12658                 this.doQuery(this.getRawValue());
12659             }
12660             if (!this.blockFocus) {
12661                 this.inputEl().focus();
12662             }
12663         }
12664     },
12665     
12666     onTickableTriggerClick : function(e)
12667     {
12668         if(this.disabled){
12669             return;
12670         }
12671         
12672         this.page = 0;
12673         this.loadNext = false;
12674         this.hasFocus = true;
12675         
12676         if(this.triggerAction == 'all') {
12677             this.doQuery(this.allQuery, true);
12678         } else {
12679             this.doQuery(this.getRawValue());
12680         }
12681     },
12682     
12683     onSearchFieldClick : function(e)
12684     {
12685         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12686             this.onTickableFooterButtonClick(e, false, false);
12687             return;
12688         }
12689         
12690         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12691             return;
12692         }
12693         
12694         this.page = 0;
12695         this.loadNext = false;
12696         this.hasFocus = true;
12697         
12698         if(this.triggerAction == 'all') {
12699             this.doQuery(this.allQuery, true);
12700         } else {
12701             this.doQuery(this.getRawValue());
12702         }
12703     },
12704     
12705     listKeyPress : function(e)
12706     {
12707         //Roo.log('listkeypress');
12708         // scroll to first matching element based on key pres..
12709         if (e.isSpecialKey()) {
12710             return false;
12711         }
12712         var k = String.fromCharCode(e.getKey()).toUpperCase();
12713         //Roo.log(k);
12714         var match  = false;
12715         var csel = this.view.getSelectedNodes();
12716         var cselitem = false;
12717         if (csel.length) {
12718             var ix = this.view.indexOf(csel[0]);
12719             cselitem  = this.store.getAt(ix);
12720             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12721                 cselitem = false;
12722             }
12723             
12724         }
12725         
12726         this.store.each(function(v) { 
12727             if (cselitem) {
12728                 // start at existing selection.
12729                 if (cselitem.id == v.id) {
12730                     cselitem = false;
12731                 }
12732                 return true;
12733             }
12734                 
12735             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12736                 match = this.store.indexOf(v);
12737                 return false;
12738             }
12739             return true;
12740         }, this);
12741         
12742         if (match === false) {
12743             return true; // no more action?
12744         }
12745         // scroll to?
12746         this.view.select(match);
12747         var sn = Roo.get(this.view.getSelectedNodes()[0])
12748         sn.scrollIntoView(sn.dom.parentNode, false);
12749     },
12750     
12751     onViewScroll : function(e, t){
12752         
12753         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){
12754             return;
12755         }
12756         
12757         this.hasQuery = true;
12758         
12759         this.loading = this.list.select('.loading', true).first();
12760         
12761         if(this.loading === null){
12762             this.list.createChild({
12763                 tag: 'div',
12764                 cls: 'loading select2-more-results select2-active',
12765                 html: 'Loading more results...'
12766             })
12767             
12768             this.loading = this.list.select('.loading', true).first();
12769             
12770             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12771             
12772             this.loading.hide();
12773         }
12774         
12775         this.loading.show();
12776         
12777         var _combo = this;
12778         
12779         this.page++;
12780         this.loadNext = true;
12781         
12782         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12783         
12784         return;
12785     },
12786     
12787     addItem : function(o)
12788     {   
12789         var dv = ''; // display value
12790         
12791         if (this.displayField) {
12792             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12793         } else {
12794             // this is an error condition!!!
12795             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12796         }
12797         
12798         if(!dv.length){
12799             return;
12800         }
12801         
12802         var choice = this.choices.createChild({
12803             tag: 'li',
12804             cls: 'select2-search-choice',
12805             cn: [
12806                 {
12807                     tag: 'div',
12808                     html: dv
12809                 },
12810                 {
12811                     tag: 'a',
12812                     href: '#',
12813                     cls: 'select2-search-choice-close',
12814                     tabindex: '-1'
12815                 }
12816             ]
12817             
12818         }, this.searchField);
12819         
12820         var close = choice.select('a.select2-search-choice-close', true).first()
12821         
12822         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12823         
12824         this.item.push(o);
12825         
12826         this.lastData = o;
12827         
12828         this.syncValue();
12829         
12830         this.inputEl().dom.value = '';
12831         
12832         this.validate();
12833     },
12834     
12835     onRemoveItem : function(e, _self, o)
12836     {
12837         e.preventDefault();
12838         
12839         this.lastItem = Roo.apply([], this.item);
12840         
12841         var index = this.item.indexOf(o.data) * 1;
12842         
12843         if( index < 0){
12844             Roo.log('not this item?!');
12845             return;
12846         }
12847         
12848         this.item.splice(index, 1);
12849         o.item.remove();
12850         
12851         this.syncValue();
12852         
12853         this.fireEvent('remove', this, e);
12854         
12855         this.validate();
12856         
12857     },
12858     
12859     syncValue : function()
12860     {
12861         if(!this.item.length){
12862             this.clearValue();
12863             return;
12864         }
12865             
12866         var value = [];
12867         var _this = this;
12868         Roo.each(this.item, function(i){
12869             if(_this.valueField){
12870                 value.push(i[_this.valueField]);
12871                 return;
12872             }
12873
12874             value.push(i);
12875         });
12876
12877         this.value = value.join(',');
12878
12879         if(this.hiddenField){
12880             this.hiddenField.dom.value = this.value;
12881         }
12882         
12883         this.store.fireEvent("datachanged", this.store);
12884     },
12885     
12886     clearItem : function()
12887     {
12888         if(!this.multiple){
12889             return;
12890         }
12891         
12892         this.item = [];
12893         
12894         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12895            c.remove();
12896         });
12897         
12898         this.syncValue();
12899         
12900         this.validate();
12901     },
12902     
12903     inputEl: function ()
12904     {
12905         if(Roo.isTouch && this.mobileTouchView){
12906             return this.el.select('input.form-control',true).first();
12907         }
12908         
12909         if(this.tickable){
12910             return this.searchField;
12911         }
12912         
12913         return this.el.select('input.form-control',true).first();
12914     },
12915     
12916     
12917     onTickableFooterButtonClick : function(e, btn, el)
12918     {
12919         e.preventDefault();
12920         
12921         this.lastItem = Roo.apply([], this.item);
12922         
12923         if(btn && btn.name == 'cancel'){
12924             this.tickItems = Roo.apply([], this.item);
12925             this.collapse();
12926             return;
12927         }
12928         
12929         this.clearItem();
12930         
12931         var _this = this;
12932         
12933         Roo.each(this.tickItems, function(o){
12934             _this.addItem(o);
12935         });
12936         
12937         this.collapse();
12938         
12939     },
12940     
12941     validate : function()
12942     {
12943         var v = this.getRawValue();
12944         
12945         if(this.multiple){
12946             v = this.getValue();
12947         }
12948         
12949         if(this.disabled || this.allowBlank || v.length){
12950             this.markValid();
12951             return true;
12952         }
12953         
12954         this.markInvalid();
12955         return false;
12956     },
12957     
12958     tickableInputEl : function()
12959     {
12960         if(!this.tickable || !this.editable){
12961             return this.inputEl();
12962         }
12963         
12964         return this.inputEl().select('.select2-search-field-input', true).first();
12965     },
12966     
12967     
12968     getAutoCreateTouchView : function()
12969     {
12970         var id = Roo.id();
12971         
12972         var cfg = {
12973             cls: 'form-group' //input-group
12974         };
12975         
12976         var input =  {
12977             tag: 'input',
12978             id : id,
12979             type : this.inputType,
12980             cls : 'form-control x-combo-noedit',
12981             autocomplete: 'new-password',
12982             placeholder : this.placeholder || '',
12983             readonly : true
12984         };
12985         
12986         if (this.name) {
12987             input.name = this.name;
12988         }
12989         
12990         if (this.size) {
12991             input.cls += ' input-' + this.size;
12992         }
12993         
12994         if (this.disabled) {
12995             input.disabled = true;
12996         }
12997         
12998         var inputblock = {
12999             cls : '',
13000             cn : [
13001                 input
13002             ]
13003         };
13004         
13005         if(this.before){
13006             inputblock.cls += ' input-group';
13007             
13008             inputblock.cn.unshift({
13009                 tag :'span',
13010                 cls : 'input-group-addon',
13011                 html : this.before
13012             });
13013         }
13014         
13015         if(this.removable && !this.multiple){
13016             inputblock.cls += ' roo-removable';
13017             
13018             inputblock.cn.push({
13019                 tag: 'button',
13020                 html : 'x',
13021                 cls : 'roo-combo-removable-btn close'
13022             });
13023         }
13024
13025         if(this.hasFeedback && !this.allowBlank){
13026             
13027             inputblock.cls += ' has-feedback';
13028             
13029             inputblock.cn.push({
13030                 tag: 'span',
13031                 cls: 'glyphicon form-control-feedback'
13032             });
13033             
13034         }
13035         
13036         if (this.after) {
13037             
13038             inputblock.cls += (this.before) ? '' : ' input-group';
13039             
13040             inputblock.cn.push({
13041                 tag :'span',
13042                 cls : 'input-group-addon',
13043                 html : this.after
13044             });
13045         }
13046
13047         var box = {
13048             tag: 'div',
13049             cn: [
13050                 {
13051                     tag: 'input',
13052                     type : 'hidden',
13053                     cls: 'form-hidden-field'
13054                 },
13055                 inputblock
13056             ]
13057             
13058         };
13059         
13060         if(this.multiple){
13061             box = {
13062                 tag: 'div',
13063                 cn: [
13064                     {
13065                         tag: 'input',
13066                         type : 'hidden',
13067                         cls: 'form-hidden-field'
13068                     },
13069                     {
13070                         tag: 'ul',
13071                         cls: 'select2-choices',
13072                         cn:[
13073                             {
13074                                 tag: 'li',
13075                                 cls: 'select2-search-field',
13076                                 cn: [
13077
13078                                     inputblock
13079                                 ]
13080                             }
13081                         ]
13082                     }
13083                 ]
13084             }
13085         };
13086         
13087         var combobox = {
13088             cls: 'select2-container input-group',
13089             cn: [
13090                 box
13091             ]
13092         };
13093         
13094         if(this.multiple){
13095             combobox.cls += ' select2-container-multi';
13096         }
13097         
13098         var align = this.labelAlign || this.parentLabelAlign();
13099         
13100         cfg.cn = combobox;
13101         
13102         if(this.fieldLabel.length){
13103             
13104             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13105             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13106             
13107             cfg.cn = [
13108                 {
13109                     tag: 'label',
13110                     cls : 'control-label ' + lw,
13111                     html : this.fieldLabel
13112
13113                 },
13114                 {
13115                     cls : cw, 
13116                     cn: [
13117                         combobox
13118                     ]
13119                 }
13120             ];
13121         }
13122         
13123         var settings = this;
13124         
13125         ['xs','sm','md','lg'].map(function(size){
13126             if (settings[size]) {
13127                 cfg.cls += ' col-' + size + '-' + settings[size];
13128             }
13129         });
13130         
13131         return cfg;
13132     },
13133     
13134     initTouchView : function()
13135     {
13136         this.renderTouchView();
13137         
13138         this.touchViewEl.on('scroll', function(){
13139             this.el.dom.scrollTop = 0;
13140         }, this);
13141         
13142         this.inputEl().on("click", this.showTouchView, this);
13143         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13144         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13145         
13146         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13147         
13148         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13149         this.store.on('load', this.onTouchViewLoad, this);
13150         this.store.on('loadexception', this.onTouchViewLoadException, this);
13151         
13152         if(this.hiddenName){
13153             
13154             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13155             
13156             this.hiddenField.dom.value =
13157                 this.hiddenValue !== undefined ? this.hiddenValue :
13158                 this.value !== undefined ? this.value : '';
13159         
13160             this.el.dom.removeAttribute('name');
13161             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13162         }
13163         
13164         if(this.multiple){
13165             this.choices = this.el.select('ul.select2-choices', true).first();
13166             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13167         }
13168         
13169         if(this.removable && !this.multiple){
13170             var close = this.closeTriggerEl();
13171             if(close){
13172                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13173                 close.on('click', this.removeBtnClick, this, close);
13174             }
13175         }
13176         
13177         return;
13178         
13179         
13180     },
13181     
13182     renderTouchView : function()
13183     {
13184         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13185         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13186         
13187         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13188         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13189         
13190         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13191         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13192         this.touchViewBodyEl.setStyle('overflow', 'auto');
13193         
13194         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13195         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13196         
13197         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13198         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13199         
13200     },
13201     
13202     showTouchView : function()
13203     {
13204         this.touchViewHeaderEl.hide();
13205
13206         if(this.fieldLabel.length){
13207             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13208             this.touchViewHeaderEl.show();
13209         }
13210
13211         this.touchViewEl.show();
13212
13213         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13214         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13215
13216         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13217
13218         if(this.fieldLabel.length){
13219             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13220         }
13221         
13222         this.touchViewBodyEl.setHeight(bodyHeight);
13223
13224         if(this.animate){
13225             var _this = this;
13226             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13227         }else{
13228             this.touchViewEl.addClass('in');
13229         }
13230
13231         this.doTouchViewQuery();
13232         
13233     },
13234     
13235     hideTouchView : function()
13236     {
13237         this.touchViewEl.removeClass('in');
13238
13239         if(this.animate){
13240             var _this = this;
13241             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13242         }else{
13243             this.touchViewEl.setStyle('display', 'none');
13244         }
13245         
13246     },
13247     
13248     setTouchViewValue : function()
13249     {
13250         if(this.multiple){
13251             this.clearItem();
13252         
13253             var _this = this;
13254
13255             Roo.each(this.tickItems, function(o){
13256                 this.addItem(o);
13257             }, this);
13258         }
13259         
13260         this.hideTouchView();
13261     },
13262     
13263     doTouchViewQuery : function()
13264     {
13265         var qe = {
13266             query: '',
13267             forceAll: true,
13268             combo: this,
13269             cancel:false
13270         };
13271         
13272         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13273             return false;
13274         }
13275         
13276         if(!this.alwaysQuery || this.mode == 'local'){
13277             this.onTouchViewLoad();
13278             return;
13279         }
13280         
13281         this.store.load();
13282     },
13283     
13284     onTouchViewBeforeLoad : function(combo,opts)
13285     {
13286         return;
13287     },
13288
13289     // private
13290     onTouchViewLoad : function()
13291     {
13292         if(this.store.getCount() < 1){
13293             this.onTouchViewEmptyResults();
13294             return;
13295         }
13296         
13297         this.clearTouchView();
13298         
13299         var rawValue = this.getRawValue();
13300         
13301         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13302         
13303         this.tickItems = [];
13304         
13305         this.store.data.each(function(d, rowIndex){
13306             var row = this.touchViewListGroup.createChild(template);
13307             
13308             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13309                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13310             }
13311             
13312             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13313                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13314             }
13315             
13316             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13317                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13318                 this.tickItems.push(d.data);
13319             }
13320             
13321             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13322             
13323         }, this);
13324         
13325         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13326         
13327         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13328
13329         if(this.fieldLabel.length){
13330             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13331         }
13332
13333         var listHeight = this.touchViewListGroup.getHeight();
13334         
13335         if(firstChecked && listHeight > bodyHeight){
13336             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13337         }
13338         
13339     },
13340     
13341     onTouchViewLoadException : function()
13342     {
13343         this.hideTouchView();
13344     },
13345     
13346     onTouchViewEmptyResults : function()
13347     {
13348         this.clearTouchView();
13349         
13350         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13351         
13352         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13353         
13354     },
13355     
13356     clearTouchView : function()
13357     {
13358         this.touchViewListGroup.dom.innerHTML = '';
13359     },
13360     
13361     onTouchViewClick : function(e, el, o)
13362     {
13363         e.preventDefault();
13364         
13365         var row = o.row;
13366         var rowIndex = o.rowIndex;
13367         
13368         var r = this.store.getAt(rowIndex);
13369         
13370         if(!this.multiple){
13371             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13372                 c.dom.removeAttribute('checked');
13373             }, this);
13374             
13375             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13376         
13377             this.setFromData(r.data);
13378             
13379             var close = this.closeTriggerEl();
13380         
13381             if(close){
13382                 close.show();
13383             }
13384
13385             this.hideTouchView();
13386             
13387             this.fireEvent('select', this, r, rowIndex);
13388             
13389             return;
13390         }
13391         
13392         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13393             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13394             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13395             return;
13396         }
13397         
13398         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13399         this.addItem(r.data);
13400         this.tickItems.push(r.data);
13401         
13402     }
13403     
13404
13405     /** 
13406     * @cfg {Boolean} grow 
13407     * @hide 
13408     */
13409     /** 
13410     * @cfg {Number} growMin 
13411     * @hide 
13412     */
13413     /** 
13414     * @cfg {Number} growMax 
13415     * @hide 
13416     */
13417     /**
13418      * @hide
13419      * @method autoSize
13420      */
13421 });
13422
13423 Roo.apply(Roo.bootstrap.ComboBox,  {
13424     
13425     header : {
13426         tag: 'div',
13427         cls: 'modal-header',
13428         cn: [
13429             {
13430                 tag: 'h4',
13431                 cls: 'modal-title'
13432             }
13433         ]
13434     },
13435     
13436     body : {
13437         tag: 'div',
13438         cls: 'modal-body',
13439         cn: [
13440             {
13441                 tag: 'ul',
13442                 cls: 'list-group'
13443             }
13444         ]
13445     },
13446     
13447     listItemRadio : {
13448         tag: 'li',
13449         cls: 'list-group-item',
13450         cn: [
13451             {
13452                 tag: 'span',
13453                 cls: 'roo-combobox-list-group-item-value'
13454             },
13455             {
13456                 tag: 'div',
13457                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13458                 cn: [
13459                     {
13460                         tag: 'input',
13461                         type: 'radio'
13462                     },
13463                     {
13464                         tag: 'label'
13465                     }
13466                 ]
13467             }
13468         ]
13469     },
13470     
13471     listItemCheckbox : {
13472         tag: 'li',
13473         cls: 'list-group-item',
13474         cn: [
13475             {
13476                 tag: 'span',
13477                 cls: 'roo-combobox-list-group-item-value'
13478             },
13479             {
13480                 tag: 'div',
13481                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13482                 cn: [
13483                     {
13484                         tag: 'input',
13485                         type: 'checkbox'
13486                     },
13487                     {
13488                         tag: 'label'
13489                     }
13490                 ]
13491             }
13492         ]
13493     },
13494     
13495     emptyResult : {
13496         tag: 'div',
13497         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13498     },
13499     
13500     footer : {
13501         tag: 'div',
13502         cls: 'modal-footer',
13503         cn: [
13504             {
13505                 tag: 'div',
13506                 cls: 'row',
13507                 cn: [
13508                     {
13509                         tag: 'div',
13510                         cls: 'col-xs-6 text-left',
13511                         cn: {
13512                             tag: 'button',
13513                             cls: 'btn btn-danger roo-touch-view-cancel',
13514                             html: 'Cancel'
13515                         }
13516                     },
13517                     {
13518                         tag: 'div',
13519                         cls: 'col-xs-6 text-right',
13520                         cn: {
13521                             tag: 'button',
13522                             cls: 'btn btn-success roo-touch-view-ok',
13523                             html: 'OK'
13524                         }
13525                     }
13526                 ]
13527             }
13528         ]
13529         
13530     }
13531 });
13532
13533 Roo.apply(Roo.bootstrap.ComboBox,  {
13534     
13535     touchViewTemplate : {
13536         tag: 'div',
13537         cls: 'modal fade roo-combobox-touch-view',
13538         cn: [
13539             {
13540                 tag: 'div',
13541                 cls: 'modal-dialog',
13542                 cn: [
13543                     {
13544                         tag: 'div',
13545                         cls: 'modal-content',
13546                         cn: [
13547                             Roo.bootstrap.ComboBox.header,
13548                             Roo.bootstrap.ComboBox.body,
13549                             Roo.bootstrap.ComboBox.footer
13550                         ]
13551                     }
13552                 ]
13553             }
13554         ]
13555     }
13556 });/*
13557  * Based on:
13558  * Ext JS Library 1.1.1
13559  * Copyright(c) 2006-2007, Ext JS, LLC.
13560  *
13561  * Originally Released Under LGPL - original licence link has changed is not relivant.
13562  *
13563  * Fork - LGPL
13564  * <script type="text/javascript">
13565  */
13566
13567 /**
13568  * @class Roo.View
13569  * @extends Roo.util.Observable
13570  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13571  * This class also supports single and multi selection modes. <br>
13572  * Create a data model bound view:
13573  <pre><code>
13574  var store = new Roo.data.Store(...);
13575
13576  var view = new Roo.View({
13577     el : "my-element",
13578     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13579  
13580     singleSelect: true,
13581     selectedClass: "ydataview-selected",
13582     store: store
13583  });
13584
13585  // listen for node click?
13586  view.on("click", function(vw, index, node, e){
13587  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13588  });
13589
13590  // load XML data
13591  dataModel.load("foobar.xml");
13592  </code></pre>
13593  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13594  * <br><br>
13595  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13596  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13597  * 
13598  * Note: old style constructor is still suported (container, template, config)
13599  * 
13600  * @constructor
13601  * Create a new View
13602  * @param {Object} config The config object
13603  * 
13604  */
13605 Roo.View = function(config, depreciated_tpl, depreciated_config){
13606     
13607     this.parent = false;
13608     
13609     if (typeof(depreciated_tpl) == 'undefined') {
13610         // new way.. - universal constructor.
13611         Roo.apply(this, config);
13612         this.el  = Roo.get(this.el);
13613     } else {
13614         // old format..
13615         this.el  = Roo.get(config);
13616         this.tpl = depreciated_tpl;
13617         Roo.apply(this, depreciated_config);
13618     }
13619     this.wrapEl  = this.el.wrap().wrap();
13620     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13621     
13622     
13623     if(typeof(this.tpl) == "string"){
13624         this.tpl = new Roo.Template(this.tpl);
13625     } else {
13626         // support xtype ctors..
13627         this.tpl = new Roo.factory(this.tpl, Roo);
13628     }
13629     
13630     
13631     this.tpl.compile();
13632     
13633     /** @private */
13634     this.addEvents({
13635         /**
13636          * @event beforeclick
13637          * Fires before a click is processed. Returns false to cancel the default action.
13638          * @param {Roo.View} this
13639          * @param {Number} index The index of the target node
13640          * @param {HTMLElement} node The target node
13641          * @param {Roo.EventObject} e The raw event object
13642          */
13643             "beforeclick" : true,
13644         /**
13645          * @event click
13646          * Fires when a template node is clicked.
13647          * @param {Roo.View} this
13648          * @param {Number} index The index of the target node
13649          * @param {HTMLElement} node The target node
13650          * @param {Roo.EventObject} e The raw event object
13651          */
13652             "click" : true,
13653         /**
13654          * @event dblclick
13655          * Fires when a template node is double clicked.
13656          * @param {Roo.View} this
13657          * @param {Number} index The index of the target node
13658          * @param {HTMLElement} node The target node
13659          * @param {Roo.EventObject} e The raw event object
13660          */
13661             "dblclick" : true,
13662         /**
13663          * @event contextmenu
13664          * Fires when a template node is right clicked.
13665          * @param {Roo.View} this
13666          * @param {Number} index The index of the target node
13667          * @param {HTMLElement} node The target node
13668          * @param {Roo.EventObject} e The raw event object
13669          */
13670             "contextmenu" : true,
13671         /**
13672          * @event selectionchange
13673          * Fires when the selected nodes change.
13674          * @param {Roo.View} this
13675          * @param {Array} selections Array of the selected nodes
13676          */
13677             "selectionchange" : true,
13678     
13679         /**
13680          * @event beforeselect
13681          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13682          * @param {Roo.View} this
13683          * @param {HTMLElement} node The node to be selected
13684          * @param {Array} selections Array of currently selected nodes
13685          */
13686             "beforeselect" : true,
13687         /**
13688          * @event preparedata
13689          * Fires on every row to render, to allow you to change the data.
13690          * @param {Roo.View} this
13691          * @param {Object} data to be rendered (change this)
13692          */
13693           "preparedata" : true
13694           
13695           
13696         });
13697
13698
13699
13700     this.el.on({
13701         "click": this.onClick,
13702         "dblclick": this.onDblClick,
13703         "contextmenu": this.onContextMenu,
13704         scope:this
13705     });
13706
13707     this.selections = [];
13708     this.nodes = [];
13709     this.cmp = new Roo.CompositeElementLite([]);
13710     if(this.store){
13711         this.store = Roo.factory(this.store, Roo.data);
13712         this.setStore(this.store, true);
13713     }
13714     
13715     if ( this.footer && this.footer.xtype) {
13716            
13717          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13718         
13719         this.footer.dataSource = this.store
13720         this.footer.container = fctr;
13721         this.footer = Roo.factory(this.footer, Roo);
13722         fctr.insertFirst(this.el);
13723         
13724         // this is a bit insane - as the paging toolbar seems to detach the el..
13725 //        dom.parentNode.parentNode.parentNode
13726          // they get detached?
13727     }
13728     
13729     
13730     Roo.View.superclass.constructor.call(this);
13731     
13732     
13733 };
13734
13735 Roo.extend(Roo.View, Roo.util.Observable, {
13736     
13737      /**
13738      * @cfg {Roo.data.Store} store Data store to load data from.
13739      */
13740     store : false,
13741     
13742     /**
13743      * @cfg {String|Roo.Element} el The container element.
13744      */
13745     el : '',
13746     
13747     /**
13748      * @cfg {String|Roo.Template} tpl The template used by this View 
13749      */
13750     tpl : false,
13751     /**
13752      * @cfg {String} dataName the named area of the template to use as the data area
13753      *                          Works with domtemplates roo-name="name"
13754      */
13755     dataName: false,
13756     /**
13757      * @cfg {String} selectedClass The css class to add to selected nodes
13758      */
13759     selectedClass : "x-view-selected",
13760      /**
13761      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13762      */
13763     emptyText : "",
13764     
13765     /**
13766      * @cfg {String} text to display on mask (default Loading)
13767      */
13768     mask : false,
13769     /**
13770      * @cfg {Boolean} multiSelect Allow multiple selection
13771      */
13772     multiSelect : false,
13773     /**
13774      * @cfg {Boolean} singleSelect Allow single selection
13775      */
13776     singleSelect:  false,
13777     
13778     /**
13779      * @cfg {Boolean} toggleSelect - selecting 
13780      */
13781     toggleSelect : false,
13782     
13783     /**
13784      * @cfg {Boolean} tickable - selecting 
13785      */
13786     tickable : false,
13787     
13788     /**
13789      * Returns the element this view is bound to.
13790      * @return {Roo.Element}
13791      */
13792     getEl : function(){
13793         return this.wrapEl;
13794     },
13795     
13796     
13797
13798     /**
13799      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13800      */
13801     refresh : function(){
13802         //Roo.log('refresh');
13803         var t = this.tpl;
13804         
13805         // if we are using something like 'domtemplate', then
13806         // the what gets used is:
13807         // t.applySubtemplate(NAME, data, wrapping data..)
13808         // the outer template then get' applied with
13809         //     the store 'extra data'
13810         // and the body get's added to the
13811         //      roo-name="data" node?
13812         //      <span class='roo-tpl-{name}'></span> ?????
13813         
13814         
13815         
13816         this.clearSelections();
13817         this.el.update("");
13818         var html = [];
13819         var records = this.store.getRange();
13820         if(records.length < 1) {
13821             
13822             // is this valid??  = should it render a template??
13823             
13824             this.el.update(this.emptyText);
13825             return;
13826         }
13827         var el = this.el;
13828         if (this.dataName) {
13829             this.el.update(t.apply(this.store.meta)); //????
13830             el = this.el.child('.roo-tpl-' + this.dataName);
13831         }
13832         
13833         for(var i = 0, len = records.length; i < len; i++){
13834             var data = this.prepareData(records[i].data, i, records[i]);
13835             this.fireEvent("preparedata", this, data, i, records[i]);
13836             
13837             var d = Roo.apply({}, data);
13838             
13839             if(this.tickable){
13840                 Roo.apply(d, {'roo-id' : Roo.id()});
13841                 
13842                 var _this = this;
13843             
13844                 Roo.each(this.parent.item, function(item){
13845                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13846                         return;
13847                     }
13848                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13849                 });
13850             }
13851             
13852             html[html.length] = Roo.util.Format.trim(
13853                 this.dataName ?
13854                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13855                     t.apply(d)
13856             );
13857         }
13858         
13859         
13860         
13861         el.update(html.join(""));
13862         this.nodes = el.dom.childNodes;
13863         this.updateIndexes(0);
13864     },
13865     
13866
13867     /**
13868      * Function to override to reformat the data that is sent to
13869      * the template for each node.
13870      * DEPRICATED - use the preparedata event handler.
13871      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13872      * a JSON object for an UpdateManager bound view).
13873      */
13874     prepareData : function(data, index, record)
13875     {
13876         this.fireEvent("preparedata", this, data, index, record);
13877         return data;
13878     },
13879
13880     onUpdate : function(ds, record){
13881         // Roo.log('on update');   
13882         this.clearSelections();
13883         var index = this.store.indexOf(record);
13884         var n = this.nodes[index];
13885         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13886         n.parentNode.removeChild(n);
13887         this.updateIndexes(index, index);
13888     },
13889
13890     
13891     
13892 // --------- FIXME     
13893     onAdd : function(ds, records, index)
13894     {
13895         //Roo.log(['on Add', ds, records, index] );        
13896         this.clearSelections();
13897         if(this.nodes.length == 0){
13898             this.refresh();
13899             return;
13900         }
13901         var n = this.nodes[index];
13902         for(var i = 0, len = records.length; i < len; i++){
13903             var d = this.prepareData(records[i].data, i, records[i]);
13904             if(n){
13905                 this.tpl.insertBefore(n, d);
13906             }else{
13907                 
13908                 this.tpl.append(this.el, d);
13909             }
13910         }
13911         this.updateIndexes(index);
13912     },
13913
13914     onRemove : function(ds, record, index){
13915        // Roo.log('onRemove');
13916         this.clearSelections();
13917         var el = this.dataName  ?
13918             this.el.child('.roo-tpl-' + this.dataName) :
13919             this.el; 
13920         
13921         el.dom.removeChild(this.nodes[index]);
13922         this.updateIndexes(index);
13923     },
13924
13925     /**
13926      * Refresh an individual node.
13927      * @param {Number} index
13928      */
13929     refreshNode : function(index){
13930         this.onUpdate(this.store, this.store.getAt(index));
13931     },
13932
13933     updateIndexes : function(startIndex, endIndex){
13934         var ns = this.nodes;
13935         startIndex = startIndex || 0;
13936         endIndex = endIndex || ns.length - 1;
13937         for(var i = startIndex; i <= endIndex; i++){
13938             ns[i].nodeIndex = i;
13939         }
13940     },
13941
13942     /**
13943      * Changes the data store this view uses and refresh the view.
13944      * @param {Store} store
13945      */
13946     setStore : function(store, initial){
13947         if(!initial && this.store){
13948             this.store.un("datachanged", this.refresh);
13949             this.store.un("add", this.onAdd);
13950             this.store.un("remove", this.onRemove);
13951             this.store.un("update", this.onUpdate);
13952             this.store.un("clear", this.refresh);
13953             this.store.un("beforeload", this.onBeforeLoad);
13954             this.store.un("load", this.onLoad);
13955             this.store.un("loadexception", this.onLoad);
13956         }
13957         if(store){
13958           
13959             store.on("datachanged", this.refresh, this);
13960             store.on("add", this.onAdd, this);
13961             store.on("remove", this.onRemove, this);
13962             store.on("update", this.onUpdate, this);
13963             store.on("clear", this.refresh, this);
13964             store.on("beforeload", this.onBeforeLoad, this);
13965             store.on("load", this.onLoad, this);
13966             store.on("loadexception", this.onLoad, this);
13967         }
13968         
13969         if(store){
13970             this.refresh();
13971         }
13972     },
13973     /**
13974      * onbeforeLoad - masks the loading area.
13975      *
13976      */
13977     onBeforeLoad : function(store,opts)
13978     {
13979          //Roo.log('onBeforeLoad');   
13980         if (!opts.add) {
13981             this.el.update("");
13982         }
13983         this.el.mask(this.mask ? this.mask : "Loading" ); 
13984     },
13985     onLoad : function ()
13986     {
13987         this.el.unmask();
13988     },
13989     
13990
13991     /**
13992      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13993      * @param {HTMLElement} node
13994      * @return {HTMLElement} The template node
13995      */
13996     findItemFromChild : function(node){
13997         var el = this.dataName  ?
13998             this.el.child('.roo-tpl-' + this.dataName,true) :
13999             this.el.dom; 
14000         
14001         if(!node || node.parentNode == el){
14002                     return node;
14003             }
14004             var p = node.parentNode;
14005             while(p && p != el){
14006             if(p.parentNode == el){
14007                 return p;
14008             }
14009             p = p.parentNode;
14010         }
14011             return null;
14012     },
14013
14014     /** @ignore */
14015     onClick : function(e){
14016         var item = this.findItemFromChild(e.getTarget());
14017         if(item){
14018             var index = this.indexOf(item);
14019             if(this.onItemClick(item, index, e) !== false){
14020                 this.fireEvent("click", this, index, item, e);
14021             }
14022         }else{
14023             this.clearSelections();
14024         }
14025     },
14026
14027     /** @ignore */
14028     onContextMenu : function(e){
14029         var item = this.findItemFromChild(e.getTarget());
14030         if(item){
14031             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14032         }
14033     },
14034
14035     /** @ignore */
14036     onDblClick : function(e){
14037         var item = this.findItemFromChild(e.getTarget());
14038         if(item){
14039             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14040         }
14041     },
14042
14043     onItemClick : function(item, index, e)
14044     {
14045         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14046             return false;
14047         }
14048         if (this.toggleSelect) {
14049             var m = this.isSelected(item) ? 'unselect' : 'select';
14050             //Roo.log(m);
14051             var _t = this;
14052             _t[m](item, true, false);
14053             return true;
14054         }
14055         if(this.multiSelect || this.singleSelect){
14056             if(this.multiSelect && e.shiftKey && this.lastSelection){
14057                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14058             }else{
14059                 this.select(item, this.multiSelect && e.ctrlKey);
14060                 this.lastSelection = item;
14061             }
14062             
14063             if(!this.tickable){
14064                 e.preventDefault();
14065             }
14066             
14067         }
14068         return true;
14069     },
14070
14071     /**
14072      * Get the number of selected nodes.
14073      * @return {Number}
14074      */
14075     getSelectionCount : function(){
14076         return this.selections.length;
14077     },
14078
14079     /**
14080      * Get the currently selected nodes.
14081      * @return {Array} An array of HTMLElements
14082      */
14083     getSelectedNodes : function(){
14084         return this.selections;
14085     },
14086
14087     /**
14088      * Get the indexes of the selected nodes.
14089      * @return {Array}
14090      */
14091     getSelectedIndexes : function(){
14092         var indexes = [], s = this.selections;
14093         for(var i = 0, len = s.length; i < len; i++){
14094             indexes.push(s[i].nodeIndex);
14095         }
14096         return indexes;
14097     },
14098
14099     /**
14100      * Clear all selections
14101      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14102      */
14103     clearSelections : function(suppressEvent){
14104         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14105             this.cmp.elements = this.selections;
14106             this.cmp.removeClass(this.selectedClass);
14107             this.selections = [];
14108             if(!suppressEvent){
14109                 this.fireEvent("selectionchange", this, this.selections);
14110             }
14111         }
14112     },
14113
14114     /**
14115      * Returns true if the passed node is selected
14116      * @param {HTMLElement/Number} node The node or node index
14117      * @return {Boolean}
14118      */
14119     isSelected : function(node){
14120         var s = this.selections;
14121         if(s.length < 1){
14122             return false;
14123         }
14124         node = this.getNode(node);
14125         return s.indexOf(node) !== -1;
14126     },
14127
14128     /**
14129      * Selects nodes.
14130      * @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
14131      * @param {Boolean} keepExisting (optional) true to keep existing selections
14132      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14133      */
14134     select : function(nodeInfo, keepExisting, suppressEvent){
14135         if(nodeInfo instanceof Array){
14136             if(!keepExisting){
14137                 this.clearSelections(true);
14138             }
14139             for(var i = 0, len = nodeInfo.length; i < len; i++){
14140                 this.select(nodeInfo[i], true, true);
14141             }
14142             return;
14143         } 
14144         var node = this.getNode(nodeInfo);
14145         if(!node || this.isSelected(node)){
14146             return; // already selected.
14147         }
14148         if(!keepExisting){
14149             this.clearSelections(true);
14150         }
14151         
14152         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14153             Roo.fly(node).addClass(this.selectedClass);
14154             this.selections.push(node);
14155             if(!suppressEvent){
14156                 this.fireEvent("selectionchange", this, this.selections);
14157             }
14158         }
14159         
14160         
14161     },
14162       /**
14163      * Unselects nodes.
14164      * @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
14165      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14166      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14167      */
14168     unselect : function(nodeInfo, keepExisting, suppressEvent)
14169     {
14170         if(nodeInfo instanceof Array){
14171             Roo.each(this.selections, function(s) {
14172                 this.unselect(s, nodeInfo);
14173             }, this);
14174             return;
14175         }
14176         var node = this.getNode(nodeInfo);
14177         if(!node || !this.isSelected(node)){
14178             //Roo.log("not selected");
14179             return; // not selected.
14180         }
14181         // fireevent???
14182         var ns = [];
14183         Roo.each(this.selections, function(s) {
14184             if (s == node ) {
14185                 Roo.fly(node).removeClass(this.selectedClass);
14186
14187                 return;
14188             }
14189             ns.push(s);
14190         },this);
14191         
14192         this.selections= ns;
14193         this.fireEvent("selectionchange", this, this.selections);
14194     },
14195
14196     /**
14197      * Gets a template node.
14198      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14199      * @return {HTMLElement} The node or null if it wasn't found
14200      */
14201     getNode : function(nodeInfo){
14202         if(typeof nodeInfo == "string"){
14203             return document.getElementById(nodeInfo);
14204         }else if(typeof nodeInfo == "number"){
14205             return this.nodes[nodeInfo];
14206         }
14207         return nodeInfo;
14208     },
14209
14210     /**
14211      * Gets a range template nodes.
14212      * @param {Number} startIndex
14213      * @param {Number} endIndex
14214      * @return {Array} An array of nodes
14215      */
14216     getNodes : function(start, end){
14217         var ns = this.nodes;
14218         start = start || 0;
14219         end = typeof end == "undefined" ? ns.length - 1 : end;
14220         var nodes = [];
14221         if(start <= end){
14222             for(var i = start; i <= end; i++){
14223                 nodes.push(ns[i]);
14224             }
14225         } else{
14226             for(var i = start; i >= end; i--){
14227                 nodes.push(ns[i]);
14228             }
14229         }
14230         return nodes;
14231     },
14232
14233     /**
14234      * Finds the index of the passed node
14235      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14236      * @return {Number} The index of the node or -1
14237      */
14238     indexOf : function(node){
14239         node = this.getNode(node);
14240         if(typeof node.nodeIndex == "number"){
14241             return node.nodeIndex;
14242         }
14243         var ns = this.nodes;
14244         for(var i = 0, len = ns.length; i < len; i++){
14245             if(ns[i] == node){
14246                 return i;
14247             }
14248         }
14249         return -1;
14250     }
14251 });
14252 /*
14253  * - LGPL
14254  *
14255  * based on jquery fullcalendar
14256  * 
14257  */
14258
14259 Roo.bootstrap = Roo.bootstrap || {};
14260 /**
14261  * @class Roo.bootstrap.Calendar
14262  * @extends Roo.bootstrap.Component
14263  * Bootstrap Calendar class
14264  * @cfg {Boolean} loadMask (true|false) default false
14265  * @cfg {Object} header generate the user specific header of the calendar, default false
14266
14267  * @constructor
14268  * Create a new Container
14269  * @param {Object} config The config object
14270  */
14271
14272
14273
14274 Roo.bootstrap.Calendar = function(config){
14275     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14276      this.addEvents({
14277         /**
14278              * @event select
14279              * Fires when a date is selected
14280              * @param {DatePicker} this
14281              * @param {Date} date The selected date
14282              */
14283         'select': true,
14284         /**
14285              * @event monthchange
14286              * Fires when the displayed month changes 
14287              * @param {DatePicker} this
14288              * @param {Date} date The selected month
14289              */
14290         'monthchange': true,
14291         /**
14292              * @event evententer
14293              * Fires when mouse over an event
14294              * @param {Calendar} this
14295              * @param {event} Event
14296              */
14297         'evententer': true,
14298         /**
14299              * @event eventleave
14300              * Fires when the mouse leaves an
14301              * @param {Calendar} this
14302              * @param {event}
14303              */
14304         'eventleave': true,
14305         /**
14306              * @event eventclick
14307              * Fires when the mouse click an
14308              * @param {Calendar} this
14309              * @param {event}
14310              */
14311         'eventclick': true
14312         
14313     });
14314
14315 };
14316
14317 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14318     
14319      /**
14320      * @cfg {Number} startDay
14321      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14322      */
14323     startDay : 0,
14324     
14325     loadMask : false,
14326     
14327     header : false,
14328       
14329     getAutoCreate : function(){
14330         
14331         
14332         var fc_button = function(name, corner, style, content ) {
14333             return Roo.apply({},{
14334                 tag : 'span',
14335                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14336                          (corner.length ?
14337                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14338                             ''
14339                         ),
14340                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14341                 unselectable: 'on'
14342             });
14343         };
14344         
14345         var header = {};
14346         
14347         if(!this.header){
14348             header = {
14349                 tag : 'table',
14350                 cls : 'fc-header',
14351                 style : 'width:100%',
14352                 cn : [
14353                     {
14354                         tag: 'tr',
14355                         cn : [
14356                             {
14357                                 tag : 'td',
14358                                 cls : 'fc-header-left',
14359                                 cn : [
14360                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14361                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14362                                     { tag: 'span', cls: 'fc-header-space' },
14363                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14364
14365
14366                                 ]
14367                             },
14368
14369                             {
14370                                 tag : 'td',
14371                                 cls : 'fc-header-center',
14372                                 cn : [
14373                                     {
14374                                         tag: 'span',
14375                                         cls: 'fc-header-title',
14376                                         cn : {
14377                                             tag: 'H2',
14378                                             html : 'month / year'
14379                                         }
14380                                     }
14381
14382                                 ]
14383                             },
14384                             {
14385                                 tag : 'td',
14386                                 cls : 'fc-header-right',
14387                                 cn : [
14388                               /*      fc_button('month', 'left', '', 'month' ),
14389                                     fc_button('week', '', '', 'week' ),
14390                                     fc_button('day', 'right', '', 'day' )
14391                                 */    
14392
14393                                 ]
14394                             }
14395
14396                         ]
14397                     }
14398                 ]
14399             };
14400         }
14401         
14402         header = this.header;
14403         
14404        
14405         var cal_heads = function() {
14406             var ret = [];
14407             // fixme - handle this.
14408             
14409             for (var i =0; i < Date.dayNames.length; i++) {
14410                 var d = Date.dayNames[i];
14411                 ret.push({
14412                     tag: 'th',
14413                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14414                     html : d.substring(0,3)
14415                 });
14416                 
14417             }
14418             ret[0].cls += ' fc-first';
14419             ret[6].cls += ' fc-last';
14420             return ret;
14421         };
14422         var cal_cell = function(n) {
14423             return  {
14424                 tag: 'td',
14425                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14426                 cn : [
14427                     {
14428                         cn : [
14429                             {
14430                                 cls: 'fc-day-number',
14431                                 html: 'D'
14432                             },
14433                             {
14434                                 cls: 'fc-day-content',
14435                              
14436                                 cn : [
14437                                      {
14438                                         style: 'position: relative;' // height: 17px;
14439                                     }
14440                                 ]
14441                             }
14442                             
14443                             
14444                         ]
14445                     }
14446                 ]
14447                 
14448             }
14449         };
14450         var cal_rows = function() {
14451             
14452             var ret = [];
14453             for (var r = 0; r < 6; r++) {
14454                 var row= {
14455                     tag : 'tr',
14456                     cls : 'fc-week',
14457                     cn : []
14458                 };
14459                 
14460                 for (var i =0; i < Date.dayNames.length; i++) {
14461                     var d = Date.dayNames[i];
14462                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14463
14464                 }
14465                 row.cn[0].cls+=' fc-first';
14466                 row.cn[0].cn[0].style = 'min-height:90px';
14467                 row.cn[6].cls+=' fc-last';
14468                 ret.push(row);
14469                 
14470             }
14471             ret[0].cls += ' fc-first';
14472             ret[4].cls += ' fc-prev-last';
14473             ret[5].cls += ' fc-last';
14474             return ret;
14475             
14476         };
14477         
14478         var cal_table = {
14479             tag: 'table',
14480             cls: 'fc-border-separate',
14481             style : 'width:100%',
14482             cellspacing  : 0,
14483             cn : [
14484                 { 
14485                     tag: 'thead',
14486                     cn : [
14487                         { 
14488                             tag: 'tr',
14489                             cls : 'fc-first fc-last',
14490                             cn : cal_heads()
14491                         }
14492                     ]
14493                 },
14494                 { 
14495                     tag: 'tbody',
14496                     cn : cal_rows()
14497                 }
14498                   
14499             ]
14500         };
14501          
14502          var cfg = {
14503             cls : 'fc fc-ltr',
14504             cn : [
14505                 header,
14506                 {
14507                     cls : 'fc-content',
14508                     style : "position: relative;",
14509                     cn : [
14510                         {
14511                             cls : 'fc-view fc-view-month fc-grid',
14512                             style : 'position: relative',
14513                             unselectable : 'on',
14514                             cn : [
14515                                 {
14516                                     cls : 'fc-event-container',
14517                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14518                                 },
14519                                 cal_table
14520                             ]
14521                         }
14522                     ]
14523     
14524                 }
14525            ] 
14526             
14527         };
14528         
14529          
14530         
14531         return cfg;
14532     },
14533     
14534     
14535     initEvents : function()
14536     {
14537         if(!this.store){
14538             throw "can not find store for calendar";
14539         }
14540         
14541         var mark = {
14542             tag: "div",
14543             cls:"x-dlg-mask",
14544             style: "text-align:center",
14545             cn: [
14546                 {
14547                     tag: "div",
14548                     style: "background-color:white;width:50%;margin:250 auto",
14549                     cn: [
14550                         {
14551                             tag: "img",
14552                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14553                         },
14554                         {
14555                             tag: "span",
14556                             html: "Loading"
14557                         }
14558                         
14559                     ]
14560                 }
14561             ]
14562         }
14563         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14564         
14565         var size = this.el.select('.fc-content', true).first().getSize();
14566         this.maskEl.setSize(size.width, size.height);
14567         this.maskEl.enableDisplayMode("block");
14568         if(!this.loadMask){
14569             this.maskEl.hide();
14570         }
14571         
14572         this.store = Roo.factory(this.store, Roo.data);
14573         this.store.on('load', this.onLoad, this);
14574         this.store.on('beforeload', this.onBeforeLoad, this);
14575         
14576         this.resize();
14577         
14578         this.cells = this.el.select('.fc-day',true);
14579         //Roo.log(this.cells);
14580         this.textNodes = this.el.query('.fc-day-number');
14581         this.cells.addClassOnOver('fc-state-hover');
14582         
14583         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14584         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14585         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14586         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14587         
14588         this.on('monthchange', this.onMonthChange, this);
14589         
14590         this.update(new Date().clearTime());
14591     },
14592     
14593     resize : function() {
14594         var sz  = this.el.getSize();
14595         
14596         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14597         this.el.select('.fc-day-content div',true).setHeight(34);
14598     },
14599     
14600     
14601     // private
14602     showPrevMonth : function(e){
14603         this.update(this.activeDate.add("mo", -1));
14604     },
14605     showToday : function(e){
14606         this.update(new Date().clearTime());
14607     },
14608     // private
14609     showNextMonth : function(e){
14610         this.update(this.activeDate.add("mo", 1));
14611     },
14612
14613     // private
14614     showPrevYear : function(){
14615         this.update(this.activeDate.add("y", -1));
14616     },
14617
14618     // private
14619     showNextYear : function(){
14620         this.update(this.activeDate.add("y", 1));
14621     },
14622
14623     
14624    // private
14625     update : function(date)
14626     {
14627         var vd = this.activeDate;
14628         this.activeDate = date;
14629 //        if(vd && this.el){
14630 //            var t = date.getTime();
14631 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14632 //                Roo.log('using add remove');
14633 //                
14634 //                this.fireEvent('monthchange', this, date);
14635 //                
14636 //                this.cells.removeClass("fc-state-highlight");
14637 //                this.cells.each(function(c){
14638 //                   if(c.dateValue == t){
14639 //                       c.addClass("fc-state-highlight");
14640 //                       setTimeout(function(){
14641 //                            try{c.dom.firstChild.focus();}catch(e){}
14642 //                       }, 50);
14643 //                       return false;
14644 //                   }
14645 //                   return true;
14646 //                });
14647 //                return;
14648 //            }
14649 //        }
14650         
14651         var days = date.getDaysInMonth();
14652         
14653         var firstOfMonth = date.getFirstDateOfMonth();
14654         var startingPos = firstOfMonth.getDay()-this.startDay;
14655         
14656         if(startingPos < this.startDay){
14657             startingPos += 7;
14658         }
14659         
14660         var pm = date.add(Date.MONTH, -1);
14661         var prevStart = pm.getDaysInMonth()-startingPos;
14662 //        
14663         this.cells = this.el.select('.fc-day',true);
14664         this.textNodes = this.el.query('.fc-day-number');
14665         this.cells.addClassOnOver('fc-state-hover');
14666         
14667         var cells = this.cells.elements;
14668         var textEls = this.textNodes;
14669         
14670         Roo.each(cells, function(cell){
14671             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14672         });
14673         
14674         days += startingPos;
14675
14676         // convert everything to numbers so it's fast
14677         var day = 86400000;
14678         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14679         //Roo.log(d);
14680         //Roo.log(pm);
14681         //Roo.log(prevStart);
14682         
14683         var today = new Date().clearTime().getTime();
14684         var sel = date.clearTime().getTime();
14685         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14686         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14687         var ddMatch = this.disabledDatesRE;
14688         var ddText = this.disabledDatesText;
14689         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14690         var ddaysText = this.disabledDaysText;
14691         var format = this.format;
14692         
14693         var setCellClass = function(cal, cell){
14694             cell.row = 0;
14695             cell.events = [];
14696             cell.more = [];
14697             //Roo.log('set Cell Class');
14698             cell.title = "";
14699             var t = d.getTime();
14700             
14701             //Roo.log(d);
14702             
14703             cell.dateValue = t;
14704             if(t == today){
14705                 cell.className += " fc-today";
14706                 cell.className += " fc-state-highlight";
14707                 cell.title = cal.todayText;
14708             }
14709             if(t == sel){
14710                 // disable highlight in other month..
14711                 //cell.className += " fc-state-highlight";
14712                 
14713             }
14714             // disabling
14715             if(t < min) {
14716                 cell.className = " fc-state-disabled";
14717                 cell.title = cal.minText;
14718                 return;
14719             }
14720             if(t > max) {
14721                 cell.className = " fc-state-disabled";
14722                 cell.title = cal.maxText;
14723                 return;
14724             }
14725             if(ddays){
14726                 if(ddays.indexOf(d.getDay()) != -1){
14727                     cell.title = ddaysText;
14728                     cell.className = " fc-state-disabled";
14729                 }
14730             }
14731             if(ddMatch && format){
14732                 var fvalue = d.dateFormat(format);
14733                 if(ddMatch.test(fvalue)){
14734                     cell.title = ddText.replace("%0", fvalue);
14735                     cell.className = " fc-state-disabled";
14736                 }
14737             }
14738             
14739             if (!cell.initialClassName) {
14740                 cell.initialClassName = cell.dom.className;
14741             }
14742             
14743             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14744         };
14745
14746         var i = 0;
14747         
14748         for(; i < startingPos; i++) {
14749             textEls[i].innerHTML = (++prevStart);
14750             d.setDate(d.getDate()+1);
14751             
14752             cells[i].className = "fc-past fc-other-month";
14753             setCellClass(this, cells[i]);
14754         }
14755         
14756         var intDay = 0;
14757         
14758         for(; i < days; i++){
14759             intDay = i - startingPos + 1;
14760             textEls[i].innerHTML = (intDay);
14761             d.setDate(d.getDate()+1);
14762             
14763             cells[i].className = ''; // "x-date-active";
14764             setCellClass(this, cells[i]);
14765         }
14766         var extraDays = 0;
14767         
14768         for(; i < 42; i++) {
14769             textEls[i].innerHTML = (++extraDays);
14770             d.setDate(d.getDate()+1);
14771             
14772             cells[i].className = "fc-future fc-other-month";
14773             setCellClass(this, cells[i]);
14774         }
14775         
14776         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14777         
14778         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14779         
14780         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14781         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14782         
14783         if(totalRows != 6){
14784             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14785             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14786         }
14787         
14788         this.fireEvent('monthchange', this, date);
14789         
14790         
14791         /*
14792         if(!this.internalRender){
14793             var main = this.el.dom.firstChild;
14794             var w = main.offsetWidth;
14795             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14796             Roo.fly(main).setWidth(w);
14797             this.internalRender = true;
14798             // opera does not respect the auto grow header center column
14799             // then, after it gets a width opera refuses to recalculate
14800             // without a second pass
14801             if(Roo.isOpera && !this.secondPass){
14802                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14803                 this.secondPass = true;
14804                 this.update.defer(10, this, [date]);
14805             }
14806         }
14807         */
14808         
14809     },
14810     
14811     findCell : function(dt) {
14812         dt = dt.clearTime().getTime();
14813         var ret = false;
14814         this.cells.each(function(c){
14815             //Roo.log("check " +c.dateValue + '?=' + dt);
14816             if(c.dateValue == dt){
14817                 ret = c;
14818                 return false;
14819             }
14820             return true;
14821         });
14822         
14823         return ret;
14824     },
14825     
14826     findCells : function(ev) {
14827         var s = ev.start.clone().clearTime().getTime();
14828        // Roo.log(s);
14829         var e= ev.end.clone().clearTime().getTime();
14830        // Roo.log(e);
14831         var ret = [];
14832         this.cells.each(function(c){
14833              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14834             
14835             if(c.dateValue > e){
14836                 return ;
14837             }
14838             if(c.dateValue < s){
14839                 return ;
14840             }
14841             ret.push(c);
14842         });
14843         
14844         return ret;    
14845     },
14846     
14847 //    findBestRow: function(cells)
14848 //    {
14849 //        var ret = 0;
14850 //        
14851 //        for (var i =0 ; i < cells.length;i++) {
14852 //            ret  = Math.max(cells[i].rows || 0,ret);
14853 //        }
14854 //        return ret;
14855 //        
14856 //    },
14857     
14858     
14859     addItem : function(ev)
14860     {
14861         // look for vertical location slot in
14862         var cells = this.findCells(ev);
14863         
14864 //        ev.row = this.findBestRow(cells);
14865         
14866         // work out the location.
14867         
14868         var crow = false;
14869         var rows = [];
14870         for(var i =0; i < cells.length; i++) {
14871             
14872             cells[i].row = cells[0].row;
14873             
14874             if(i == 0){
14875                 cells[i].row = cells[i].row + 1;
14876             }
14877             
14878             if (!crow) {
14879                 crow = {
14880                     start : cells[i],
14881                     end :  cells[i]
14882                 };
14883                 continue;
14884             }
14885             if (crow.start.getY() == cells[i].getY()) {
14886                 // on same row.
14887                 crow.end = cells[i];
14888                 continue;
14889             }
14890             // different row.
14891             rows.push(crow);
14892             crow = {
14893                 start: cells[i],
14894                 end : cells[i]
14895             };
14896             
14897         }
14898         
14899         rows.push(crow);
14900         ev.els = [];
14901         ev.rows = rows;
14902         ev.cells = cells;
14903         
14904         cells[0].events.push(ev);
14905         
14906         this.calevents.push(ev);
14907     },
14908     
14909     clearEvents: function() {
14910         
14911         if(!this.calevents){
14912             return;
14913         }
14914         
14915         Roo.each(this.cells.elements, function(c){
14916             c.row = 0;
14917             c.events = [];
14918             c.more = [];
14919         });
14920         
14921         Roo.each(this.calevents, function(e) {
14922             Roo.each(e.els, function(el) {
14923                 el.un('mouseenter' ,this.onEventEnter, this);
14924                 el.un('mouseleave' ,this.onEventLeave, this);
14925                 el.remove();
14926             },this);
14927         },this);
14928         
14929         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14930             e.remove();
14931         });
14932         
14933     },
14934     
14935     renderEvents: function()
14936     {   
14937         var _this = this;
14938         
14939         this.cells.each(function(c) {
14940             
14941             if(c.row < 5){
14942                 return;
14943             }
14944             
14945             var ev = c.events;
14946             
14947             var r = 4;
14948             if(c.row != c.events.length){
14949                 r = 4 - (4 - (c.row - c.events.length));
14950             }
14951             
14952             c.events = ev.slice(0, r);
14953             c.more = ev.slice(r);
14954             
14955             if(c.more.length && c.more.length == 1){
14956                 c.events.push(c.more.pop());
14957             }
14958             
14959             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14960             
14961         });
14962             
14963         this.cells.each(function(c) {
14964             
14965             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14966             
14967             
14968             for (var e = 0; e < c.events.length; e++){
14969                 var ev = c.events[e];
14970                 var rows = ev.rows;
14971                 
14972                 for(var i = 0; i < rows.length; i++) {
14973                 
14974                     // how many rows should it span..
14975
14976                     var  cfg = {
14977                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14978                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14979
14980                         unselectable : "on",
14981                         cn : [
14982                             {
14983                                 cls: 'fc-event-inner',
14984                                 cn : [
14985     //                                {
14986     //                                  tag:'span',
14987     //                                  cls: 'fc-event-time',
14988     //                                  html : cells.length > 1 ? '' : ev.time
14989     //                                },
14990                                     {
14991                                       tag:'span',
14992                                       cls: 'fc-event-title',
14993                                       html : String.format('{0}', ev.title)
14994                                     }
14995
14996
14997                                 ]
14998                             },
14999                             {
15000                                 cls: 'ui-resizable-handle ui-resizable-e',
15001                                 html : '&nbsp;&nbsp;&nbsp'
15002                             }
15003
15004                         ]
15005                     };
15006
15007                     if (i == 0) {
15008                         cfg.cls += ' fc-event-start';
15009                     }
15010                     if ((i+1) == rows.length) {
15011                         cfg.cls += ' fc-event-end';
15012                     }
15013
15014                     var ctr = _this.el.select('.fc-event-container',true).first();
15015                     var cg = ctr.createChild(cfg);
15016
15017                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15018                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15019
15020                     var r = (c.more.length) ? 1 : 0;
15021                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15022                     cg.setWidth(ebox.right - sbox.x -2);
15023
15024                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15025                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15026                     cg.on('click', _this.onEventClick, _this, ev);
15027
15028                     ev.els.push(cg);
15029                     
15030                 }
15031                 
15032             }
15033             
15034             
15035             if(c.more.length){
15036                 var  cfg = {
15037                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15038                     style : 'position: absolute',
15039                     unselectable : "on",
15040                     cn : [
15041                         {
15042                             cls: 'fc-event-inner',
15043                             cn : [
15044                                 {
15045                                   tag:'span',
15046                                   cls: 'fc-event-title',
15047                                   html : 'More'
15048                                 }
15049
15050
15051                             ]
15052                         },
15053                         {
15054                             cls: 'ui-resizable-handle ui-resizable-e',
15055                             html : '&nbsp;&nbsp;&nbsp'
15056                         }
15057
15058                     ]
15059                 };
15060
15061                 var ctr = _this.el.select('.fc-event-container',true).first();
15062                 var cg = ctr.createChild(cfg);
15063
15064                 var sbox = c.select('.fc-day-content',true).first().getBox();
15065                 var ebox = c.select('.fc-day-content',true).first().getBox();
15066                 //Roo.log(cg);
15067                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15068                 cg.setWidth(ebox.right - sbox.x -2);
15069
15070                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15071                 
15072             }
15073             
15074         });
15075         
15076         
15077         
15078     },
15079     
15080     onEventEnter: function (e, el,event,d) {
15081         this.fireEvent('evententer', this, el, event);
15082     },
15083     
15084     onEventLeave: function (e, el,event,d) {
15085         this.fireEvent('eventleave', this, el, event);
15086     },
15087     
15088     onEventClick: function (e, el,event,d) {
15089         this.fireEvent('eventclick', this, el, event);
15090     },
15091     
15092     onMonthChange: function () {
15093         this.store.load();
15094     },
15095     
15096     onMoreEventClick: function(e, el, more)
15097     {
15098         var _this = this;
15099         
15100         this.calpopover.placement = 'right';
15101         this.calpopover.setTitle('More');
15102         
15103         this.calpopover.setContent('');
15104         
15105         var ctr = this.calpopover.el.select('.popover-content', true).first();
15106         
15107         Roo.each(more, function(m){
15108             var cfg = {
15109                 cls : 'fc-event-hori fc-event-draggable',
15110                 html : m.title
15111             }
15112             var cg = ctr.createChild(cfg);
15113             
15114             cg.on('click', _this.onEventClick, _this, m);
15115         });
15116         
15117         this.calpopover.show(el);
15118         
15119         
15120     },
15121     
15122     onLoad: function () 
15123     {   
15124         this.calevents = [];
15125         var cal = this;
15126         
15127         if(this.store.getCount() > 0){
15128             this.store.data.each(function(d){
15129                cal.addItem({
15130                     id : d.data.id,
15131                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15132                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15133                     time : d.data.start_time,
15134                     title : d.data.title,
15135                     description : d.data.description,
15136                     venue : d.data.venue
15137                 });
15138             });
15139         }
15140         
15141         this.renderEvents();
15142         
15143         if(this.calevents.length && this.loadMask){
15144             this.maskEl.hide();
15145         }
15146     },
15147     
15148     onBeforeLoad: function()
15149     {
15150         this.clearEvents();
15151         if(this.loadMask){
15152             this.maskEl.show();
15153         }
15154     }
15155 });
15156
15157  
15158  /*
15159  * - LGPL
15160  *
15161  * element
15162  * 
15163  */
15164
15165 /**
15166  * @class Roo.bootstrap.Popover
15167  * @extends Roo.bootstrap.Component
15168  * Bootstrap Popover class
15169  * @cfg {String} html contents of the popover   (or false to use children..)
15170  * @cfg {String} title of popover (or false to hide)
15171  * @cfg {String} placement how it is placed
15172  * @cfg {String} trigger click || hover (or false to trigger manually)
15173  * @cfg {String} over what (parent or false to trigger manually.)
15174  * @cfg {Number} delay - delay before showing
15175  
15176  * @constructor
15177  * Create a new Popover
15178  * @param {Object} config The config object
15179  */
15180
15181 Roo.bootstrap.Popover = function(config){
15182     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15183 };
15184
15185 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15186     
15187     title: 'Fill in a title',
15188     html: false,
15189     
15190     placement : 'right',
15191     trigger : 'hover', // hover
15192     
15193     delay : 0,
15194     
15195     over: 'parent',
15196     
15197     can_build_overlaid : false,
15198     
15199     getChildContainer : function()
15200     {
15201         return this.el.select('.popover-content',true).first();
15202     },
15203     
15204     getAutoCreate : function(){
15205          Roo.log('make popover?');
15206         var cfg = {
15207            cls : 'popover roo-dynamic',
15208            style: 'display:block',
15209            cn : [
15210                 {
15211                     cls : 'arrow'
15212                 },
15213                 {
15214                     cls : 'popover-inner',
15215                     cn : [
15216                         {
15217                             tag: 'h3',
15218                             cls: 'popover-title',
15219                             html : this.title
15220                         },
15221                         {
15222                             cls : 'popover-content',
15223                             html : this.html
15224                         }
15225                     ]
15226                     
15227                 }
15228            ]
15229         };
15230         
15231         return cfg;
15232     },
15233     setTitle: function(str)
15234     {
15235         this.title = str;
15236         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15237     },
15238     setContent: function(str)
15239     {
15240         this.html = str;
15241         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15242     },
15243     // as it get's added to the bottom of the page.
15244     onRender : function(ct, position)
15245     {
15246         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15247         if(!this.el){
15248             var cfg = Roo.apply({},  this.getAutoCreate());
15249             cfg.id = Roo.id();
15250             
15251             if (this.cls) {
15252                 cfg.cls += ' ' + this.cls;
15253             }
15254             if (this.style) {
15255                 cfg.style = this.style;
15256             }
15257             Roo.log("adding to ")
15258             this.el = Roo.get(document.body).createChild(cfg, position);
15259             Roo.log(this.el);
15260         }
15261         this.initEvents();
15262     },
15263     
15264     initEvents : function()
15265     {
15266         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15267         this.el.enableDisplayMode('block');
15268         this.el.hide();
15269         if (this.over === false) {
15270             return; 
15271         }
15272         if (this.triggers === false) {
15273             return;
15274         }
15275         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15276         var triggers = this.trigger ? this.trigger.split(' ') : [];
15277         Roo.each(triggers, function(trigger) {
15278         
15279             if (trigger == 'click') {
15280                 on_el.on('click', this.toggle, this);
15281             } else if (trigger != 'manual') {
15282                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15283                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15284       
15285                 on_el.on(eventIn  ,this.enter, this);
15286                 on_el.on(eventOut, this.leave, this);
15287             }
15288         }, this);
15289         
15290     },
15291     
15292     
15293     // private
15294     timeout : null,
15295     hoverState : null,
15296     
15297     toggle : function () {
15298         this.hoverState == 'in' ? this.leave() : this.enter();
15299     },
15300     
15301     enter : function () {
15302        
15303     
15304         clearTimeout(this.timeout);
15305     
15306         this.hoverState = 'in';
15307     
15308         if (!this.delay || !this.delay.show) {
15309             this.show();
15310             return;
15311         }
15312         var _t = this;
15313         this.timeout = setTimeout(function () {
15314             if (_t.hoverState == 'in') {
15315                 _t.show();
15316             }
15317         }, this.delay.show)
15318     },
15319     leave : function() {
15320         clearTimeout(this.timeout);
15321     
15322         this.hoverState = 'out';
15323     
15324         if (!this.delay || !this.delay.hide) {
15325             this.hide();
15326             return;
15327         }
15328         var _t = this;
15329         this.timeout = setTimeout(function () {
15330             if (_t.hoverState == 'out') {
15331                 _t.hide();
15332             }
15333         }, this.delay.hide)
15334     },
15335     
15336     show : function (on_el)
15337     {
15338         if (!on_el) {
15339             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15340         }
15341         // set content.
15342         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15343         if (this.html !== false) {
15344             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15345         }
15346         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15347         if (!this.title.length) {
15348             this.el.select('.popover-title',true).hide();
15349         }
15350         
15351         var placement = typeof this.placement == 'function' ?
15352             this.placement.call(this, this.el, on_el) :
15353             this.placement;
15354             
15355         var autoToken = /\s?auto?\s?/i;
15356         var autoPlace = autoToken.test(placement);
15357         if (autoPlace) {
15358             placement = placement.replace(autoToken, '') || 'top';
15359         }
15360         
15361         //this.el.detach()
15362         //this.el.setXY([0,0]);
15363         this.el.show();
15364         this.el.dom.style.display='block';
15365         this.el.addClass(placement);
15366         
15367         //this.el.appendTo(on_el);
15368         
15369         var p = this.getPosition();
15370         var box = this.el.getBox();
15371         
15372         if (autoPlace) {
15373             // fixme..
15374         }
15375         var align = Roo.bootstrap.Popover.alignment[placement];
15376         this.el.alignTo(on_el, align[0],align[1]);
15377         //var arrow = this.el.select('.arrow',true).first();
15378         //arrow.set(align[2], 
15379         
15380         this.el.addClass('in');
15381         
15382         
15383         if (this.el.hasClass('fade')) {
15384             // fade it?
15385         }
15386         
15387     },
15388     hide : function()
15389     {
15390         this.el.setXY([0,0]);
15391         this.el.removeClass('in');
15392         this.el.hide();
15393         this.hoverState = null;
15394         
15395     }
15396     
15397 });
15398
15399 Roo.bootstrap.Popover.alignment = {
15400     'left' : ['r-l', [-10,0], 'right'],
15401     'right' : ['l-r', [10,0], 'left'],
15402     'bottom' : ['t-b', [0,10], 'top'],
15403     'top' : [ 'b-t', [0,-10], 'bottom']
15404 };
15405
15406  /*
15407  * - LGPL
15408  *
15409  * Progress
15410  * 
15411  */
15412
15413 /**
15414  * @class Roo.bootstrap.Progress
15415  * @extends Roo.bootstrap.Component
15416  * Bootstrap Progress class
15417  * @cfg {Boolean} striped striped of the progress bar
15418  * @cfg {Boolean} active animated of the progress bar
15419  * 
15420  * 
15421  * @constructor
15422  * Create a new Progress
15423  * @param {Object} config The config object
15424  */
15425
15426 Roo.bootstrap.Progress = function(config){
15427     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15428 };
15429
15430 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15431     
15432     striped : false,
15433     active: false,
15434     
15435     getAutoCreate : function(){
15436         var cfg = {
15437             tag: 'div',
15438             cls: 'progress'
15439         };
15440         
15441         
15442         if(this.striped){
15443             cfg.cls += ' progress-striped';
15444         }
15445       
15446         if(this.active){
15447             cfg.cls += ' active';
15448         }
15449         
15450         
15451         return cfg;
15452     }
15453    
15454 });
15455
15456  
15457
15458  /*
15459  * - LGPL
15460  *
15461  * ProgressBar
15462  * 
15463  */
15464
15465 /**
15466  * @class Roo.bootstrap.ProgressBar
15467  * @extends Roo.bootstrap.Component
15468  * Bootstrap ProgressBar class
15469  * @cfg {Number} aria_valuenow aria-value now
15470  * @cfg {Number} aria_valuemin aria-value min
15471  * @cfg {Number} aria_valuemax aria-value max
15472  * @cfg {String} label label for the progress bar
15473  * @cfg {String} panel (success | info | warning | danger )
15474  * @cfg {String} role role of the progress bar
15475  * @cfg {String} sr_only text
15476  * 
15477  * 
15478  * @constructor
15479  * Create a new ProgressBar
15480  * @param {Object} config The config object
15481  */
15482
15483 Roo.bootstrap.ProgressBar = function(config){
15484     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15485 };
15486
15487 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15488     
15489     aria_valuenow : 0,
15490     aria_valuemin : 0,
15491     aria_valuemax : 100,
15492     label : false,
15493     panel : false,
15494     role : false,
15495     sr_only: false,
15496     
15497     getAutoCreate : function()
15498     {
15499         
15500         var cfg = {
15501             tag: 'div',
15502             cls: 'progress-bar',
15503             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15504         };
15505         
15506         if(this.sr_only){
15507             cfg.cn = {
15508                 tag: 'span',
15509                 cls: 'sr-only',
15510                 html: this.sr_only
15511             }
15512         }
15513         
15514         if(this.role){
15515             cfg.role = this.role;
15516         }
15517         
15518         if(this.aria_valuenow){
15519             cfg['aria-valuenow'] = this.aria_valuenow;
15520         }
15521         
15522         if(this.aria_valuemin){
15523             cfg['aria-valuemin'] = this.aria_valuemin;
15524         }
15525         
15526         if(this.aria_valuemax){
15527             cfg['aria-valuemax'] = this.aria_valuemax;
15528         }
15529         
15530         if(this.label && !this.sr_only){
15531             cfg.html = this.label;
15532         }
15533         
15534         if(this.panel){
15535             cfg.cls += ' progress-bar-' + this.panel;
15536         }
15537         
15538         return cfg;
15539     },
15540     
15541     update : function(aria_valuenow)
15542     {
15543         this.aria_valuenow = aria_valuenow;
15544         
15545         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15546     }
15547    
15548 });
15549
15550  
15551
15552  /*
15553  * - LGPL
15554  *
15555  * column
15556  * 
15557  */
15558
15559 /**
15560  * @class Roo.bootstrap.TabGroup
15561  * @extends Roo.bootstrap.Column
15562  * Bootstrap Column class
15563  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15564  * @cfg {Boolean} carousel true to make the group behave like a carousel
15565  * @cfg {Number} bullets show the panel pointer.. default 0
15566  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15567  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15568  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15569  * 
15570  * @constructor
15571  * Create a new TabGroup
15572  * @param {Object} config The config object
15573  */
15574
15575 Roo.bootstrap.TabGroup = function(config){
15576     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15577     if (!this.navId) {
15578         this.navId = Roo.id();
15579     }
15580     this.tabs = [];
15581     Roo.bootstrap.TabGroup.register(this);
15582     
15583 };
15584
15585 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15586     
15587     carousel : false,
15588     transition : false,
15589     bullets : 0,
15590     timer : 0,
15591     autoslide : false,
15592     slideFn : false,
15593     slideOnTouch : false,
15594     
15595     getAutoCreate : function()
15596     {
15597         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15598         
15599         cfg.cls += ' tab-content';
15600         
15601         Roo.log('get auto create...............');
15602         
15603         if (this.carousel) {
15604             cfg.cls += ' carousel slide';
15605             
15606             cfg.cn = [{
15607                cls : 'carousel-inner'
15608             }];
15609         
15610             if(this.bullets > 0 && !Roo.isTouch){
15611                 
15612                 var bullets = {
15613                     cls : 'carousel-bullets',
15614                     cn : []
15615                 };
15616                 
15617                 if(this.bullets_cls){
15618                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15619                 }
15620                 
15621                 for (var i = 0; i < this.bullets; i++){
15622                     bullets.cn.push({
15623                         cls : 'bullet bullet-' + i
15624                     });
15625                 }
15626                 
15627                 bullets.cn.push({
15628                     cls : 'clear'
15629                 });
15630                 
15631                 cfg.cn[0].cn = bullets;
15632             }
15633         }
15634         
15635         return cfg;
15636     },
15637     
15638     initEvents:  function()
15639     {
15640         Roo.log('-------- init events on tab group ---------');
15641         
15642         if(this.bullets > 0 && !Roo.isTouch){
15643             this.initBullet();
15644         }
15645         
15646         Roo.log(this);
15647         
15648         if(Roo.isTouch && this.slideOnTouch){
15649             this.el.on("touchstart", this.onTouchStart, this);
15650         }
15651         
15652         if(this.autoslide){
15653             var _this = this;
15654             
15655             this.slideFn = window.setInterval(function() {
15656                 _this.showPanelNext();
15657             }, this.timer);
15658         }
15659         
15660     },
15661     
15662     onTouchStart : function(e, el, o)
15663     {
15664         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15665             return;
15666         }
15667         
15668         this.showPanelNext();
15669     },
15670     
15671     getChildContainer : function()
15672     {
15673         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15674     },
15675     
15676     /**
15677     * register a Navigation item
15678     * @param {Roo.bootstrap.NavItem} the navitem to add
15679     */
15680     register : function(item)
15681     {
15682         this.tabs.push( item);
15683         item.navId = this.navId; // not really needed..
15684     
15685     },
15686     
15687     getActivePanel : function()
15688     {
15689         var r = false;
15690         Roo.each(this.tabs, function(t) {
15691             if (t.active) {
15692                 r = t;
15693                 return false;
15694             }
15695             return null;
15696         });
15697         return r;
15698         
15699     },
15700     getPanelByName : function(n)
15701     {
15702         var r = false;
15703         Roo.each(this.tabs, function(t) {
15704             if (t.tabId == n) {
15705                 r = t;
15706                 return false;
15707             }
15708             return null;
15709         });
15710         return r;
15711     },
15712     indexOfPanel : function(p)
15713     {
15714         var r = false;
15715         Roo.each(this.tabs, function(t,i) {
15716             if (t.tabId == p.tabId) {
15717                 r = i;
15718                 return false;
15719             }
15720             return null;
15721         });
15722         return r;
15723     },
15724     /**
15725      * show a specific panel
15726      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15727      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15728      */
15729     showPanel : function (pan)
15730     {
15731         if(this.transition){
15732             Roo.log("waiting for the transitionend");
15733             return;
15734         }
15735         
15736         if (typeof(pan) == 'number') {
15737             pan = this.tabs[pan];
15738         }
15739         if (typeof(pan) == 'string') {
15740             pan = this.getPanelByName(pan);
15741         }
15742         if (pan.tabId == this.getActivePanel().tabId) {
15743             return true;
15744         }
15745         var cur = this.getActivePanel();
15746         
15747         if (false === cur.fireEvent('beforedeactivate')) {
15748             return false;
15749         }
15750         
15751         if(this.bullets > 0 && !Roo.isTouch){
15752             this.setActiveBullet(this.indexOfPanel(pan));
15753         }
15754         
15755         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15756             
15757             this.transition = true;
15758             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15759             var lr = dir == 'next' ? 'left' : 'right';
15760             pan.el.addClass(dir); // or prev
15761             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15762             cur.el.addClass(lr); // or right
15763             pan.el.addClass(lr);
15764             
15765             var _this = this;
15766             cur.el.on('transitionend', function() {
15767                 Roo.log("trans end?");
15768                 
15769                 pan.el.removeClass([lr,dir]);
15770                 pan.setActive(true);
15771                 
15772                 cur.el.removeClass([lr]);
15773                 cur.setActive(false);
15774                 
15775                 _this.transition = false;
15776                 
15777             }, this, { single:  true } );
15778             
15779             return true;
15780         }
15781         
15782         cur.setActive(false);
15783         pan.setActive(true);
15784         
15785         return true;
15786         
15787     },
15788     showPanelNext : function()
15789     {
15790         var i = this.indexOfPanel(this.getActivePanel());
15791         
15792         if (i >= this.tabs.length - 1 && !this.autoslide) {
15793             return;
15794         }
15795         
15796         if (i >= this.tabs.length - 1 && this.autoslide) {
15797             i = -1;
15798         }
15799         
15800         this.showPanel(this.tabs[i+1]);
15801     },
15802     
15803     showPanelPrev : function()
15804     {
15805         var i = this.indexOfPanel(this.getActivePanel());
15806         
15807         if (i  < 1 && !this.autoslide) {
15808             return;
15809         }
15810         
15811         if (i < 1 && this.autoslide) {
15812             i = this.tabs.length;
15813         }
15814         
15815         this.showPanel(this.tabs[i-1]);
15816     },
15817     
15818     initBullet : function()
15819     {
15820         if(Roo.isTouch){
15821             return;
15822         }
15823         
15824         var _this = this;
15825         
15826         for (var i = 0; i < this.bullets; i++){
15827             var bullet = this.el.select('.bullet-' + i, true).first();
15828
15829             if(!bullet){
15830                 continue;
15831             }
15832
15833             bullet.on('click', (function(e, el, o, ii, t){
15834
15835                 e.preventDefault();
15836
15837                 _this.showPanel(ii);
15838
15839                 if(_this.autoslide && _this.slideFn){
15840                     clearInterval(_this.slideFn);
15841                     _this.slideFn = window.setInterval(function() {
15842                         _this.showPanelNext();
15843                     }, _this.timer);
15844                 }
15845
15846             }).createDelegate(this, [i, bullet], true));
15847         }
15848     },
15849     
15850     setActiveBullet : function(i)
15851     {
15852         if(Roo.isTouch){
15853             return;
15854         }
15855         
15856         Roo.each(this.el.select('.bullet', true).elements, function(el){
15857             el.removeClass('selected');
15858         });
15859
15860         var bullet = this.el.select('.bullet-' + i, true).first();
15861         
15862         if(!bullet){
15863             return;
15864         }
15865         
15866         bullet.addClass('selected');
15867     }
15868     
15869     
15870   
15871 });
15872
15873  
15874
15875  
15876  
15877 Roo.apply(Roo.bootstrap.TabGroup, {
15878     
15879     groups: {},
15880      /**
15881     * register a Navigation Group
15882     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15883     */
15884     register : function(navgrp)
15885     {
15886         this.groups[navgrp.navId] = navgrp;
15887         
15888     },
15889     /**
15890     * fetch a Navigation Group based on the navigation ID
15891     * if one does not exist , it will get created.
15892     * @param {string} the navgroup to add
15893     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15894     */
15895     get: function(navId) {
15896         if (typeof(this.groups[navId]) == 'undefined') {
15897             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15898         }
15899         return this.groups[navId] ;
15900     }
15901     
15902     
15903     
15904 });
15905
15906  /*
15907  * - LGPL
15908  *
15909  * TabPanel
15910  * 
15911  */
15912
15913 /**
15914  * @class Roo.bootstrap.TabPanel
15915  * @extends Roo.bootstrap.Component
15916  * Bootstrap TabPanel class
15917  * @cfg {Boolean} active panel active
15918  * @cfg {String} html panel content
15919  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15920  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15921  * 
15922  * 
15923  * @constructor
15924  * Create a new TabPanel
15925  * @param {Object} config The config object
15926  */
15927
15928 Roo.bootstrap.TabPanel = function(config){
15929     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15930     this.addEvents({
15931         /**
15932              * @event changed
15933              * Fires when the active status changes
15934              * @param {Roo.bootstrap.TabPanel} this
15935              * @param {Boolean} state the new state
15936             
15937          */
15938         'changed': true,
15939         /**
15940              * @event beforedeactivate
15941              * Fires before a tab is de-activated - can be used to do validation on a form.
15942              * @param {Roo.bootstrap.TabPanel} this
15943              * @return {Boolean} false if there is an error
15944             
15945          */
15946         'beforedeactivate': true
15947      });
15948     
15949     this.tabId = this.tabId || Roo.id();
15950   
15951 };
15952
15953 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15954     
15955     active: false,
15956     html: false,
15957     tabId: false,
15958     navId : false,
15959     
15960     getAutoCreate : function(){
15961         var cfg = {
15962             tag: 'div',
15963             // item is needed for carousel - not sure if it has any effect otherwise
15964             cls: 'tab-pane item',
15965             html: this.html || ''
15966         };
15967         
15968         if(this.active){
15969             cfg.cls += ' active';
15970         }
15971         
15972         if(this.tabId){
15973             cfg.tabId = this.tabId;
15974         }
15975         
15976         
15977         return cfg;
15978     },
15979     
15980     initEvents:  function()
15981     {
15982         Roo.log('-------- init events on tab panel ---------');
15983         
15984         var p = this.parent();
15985         this.navId = this.navId || p.navId;
15986         
15987         if (typeof(this.navId) != 'undefined') {
15988             // not really needed.. but just in case.. parent should be a NavGroup.
15989             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15990             Roo.log(['register', tg, this]);
15991             tg.register(this);
15992             
15993             var i = tg.tabs.length - 1;
15994             
15995             if(this.active && tg.bullets > 0 && i < tg.bullets){
15996                 tg.setActiveBullet(i);
15997             }
15998         }
15999         
16000     },
16001     
16002     
16003     onRender : function(ct, position)
16004     {
16005        // Roo.log("Call onRender: " + this.xtype);
16006         
16007         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16008         
16009         
16010         
16011         
16012         
16013     },
16014     
16015     setActive: function(state)
16016     {
16017         Roo.log("panel - set active " + this.tabId + "=" + state);
16018         
16019         this.active = state;
16020         if (!state) {
16021             this.el.removeClass('active');
16022             
16023         } else  if (!this.el.hasClass('active')) {
16024             this.el.addClass('active');
16025         }
16026         
16027         this.fireEvent('changed', this, state);
16028     }
16029     
16030     
16031 });
16032  
16033
16034  
16035
16036  /*
16037  * - LGPL
16038  *
16039  * DateField
16040  * 
16041  */
16042
16043 /**
16044  * @class Roo.bootstrap.DateField
16045  * @extends Roo.bootstrap.Input
16046  * Bootstrap DateField class
16047  * @cfg {Number} weekStart default 0
16048  * @cfg {String} viewMode default empty, (months|years)
16049  * @cfg {String} minViewMode default empty, (months|years)
16050  * @cfg {Number} startDate default -Infinity
16051  * @cfg {Number} endDate default Infinity
16052  * @cfg {Boolean} todayHighlight default false
16053  * @cfg {Boolean} todayBtn default false
16054  * @cfg {Boolean} calendarWeeks default false
16055  * @cfg {Object} daysOfWeekDisabled default empty
16056  * @cfg {Boolean} singleMode default false (true | false)
16057  * 
16058  * @cfg {Boolean} keyboardNavigation default true
16059  * @cfg {String} language default en
16060  * 
16061  * @constructor
16062  * Create a new DateField
16063  * @param {Object} config The config object
16064  */
16065
16066 Roo.bootstrap.DateField = function(config){
16067     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16068      this.addEvents({
16069             /**
16070              * @event show
16071              * Fires when this field show.
16072              * @param {Roo.bootstrap.DateField} this
16073              * @param {Mixed} date The date value
16074              */
16075             show : true,
16076             /**
16077              * @event show
16078              * Fires when this field hide.
16079              * @param {Roo.bootstrap.DateField} this
16080              * @param {Mixed} date The date value
16081              */
16082             hide : true,
16083             /**
16084              * @event select
16085              * Fires when select a date.
16086              * @param {Roo.bootstrap.DateField} this
16087              * @param {Mixed} date The date value
16088              */
16089             select : true
16090         });
16091 };
16092
16093 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16094     
16095     /**
16096      * @cfg {String} format
16097      * The default date format string which can be overriden for localization support.  The format must be
16098      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16099      */
16100     format : "m/d/y",
16101     /**
16102      * @cfg {String} altFormats
16103      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16104      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16105      */
16106     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16107     
16108     weekStart : 0,
16109     
16110     viewMode : '',
16111     
16112     minViewMode : '',
16113     
16114     todayHighlight : false,
16115     
16116     todayBtn: false,
16117     
16118     language: 'en',
16119     
16120     keyboardNavigation: true,
16121     
16122     calendarWeeks: false,
16123     
16124     startDate: -Infinity,
16125     
16126     endDate: Infinity,
16127     
16128     daysOfWeekDisabled: [],
16129     
16130     _events: [],
16131     
16132     singleMode : false,
16133     
16134     UTCDate: function()
16135     {
16136         return new Date(Date.UTC.apply(Date, arguments));
16137     },
16138     
16139     UTCToday: function()
16140     {
16141         var today = new Date();
16142         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16143     },
16144     
16145     getDate: function() {
16146             var d = this.getUTCDate();
16147             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16148     },
16149     
16150     getUTCDate: function() {
16151             return this.date;
16152     },
16153     
16154     setDate: function(d) {
16155             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16156     },
16157     
16158     setUTCDate: function(d) {
16159             this.date = d;
16160             this.setValue(this.formatDate(this.date));
16161     },
16162         
16163     onRender: function(ct, position)
16164     {
16165         
16166         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16167         
16168         this.language = this.language || 'en';
16169         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16170         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16171         
16172         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16173         this.format = this.format || 'm/d/y';
16174         this.isInline = false;
16175         this.isInput = true;
16176         this.component = this.el.select('.add-on', true).first() || false;
16177         this.component = (this.component && this.component.length === 0) ? false : this.component;
16178         this.hasInput = this.component && this.inputEL().length;
16179         
16180         if (typeof(this.minViewMode === 'string')) {
16181             switch (this.minViewMode) {
16182                 case 'months':
16183                     this.minViewMode = 1;
16184                     break;
16185                 case 'years':
16186                     this.minViewMode = 2;
16187                     break;
16188                 default:
16189                     this.minViewMode = 0;
16190                     break;
16191             }
16192         }
16193         
16194         if (typeof(this.viewMode === 'string')) {
16195             switch (this.viewMode) {
16196                 case 'months':
16197                     this.viewMode = 1;
16198                     break;
16199                 case 'years':
16200                     this.viewMode = 2;
16201                     break;
16202                 default:
16203                     this.viewMode = 0;
16204                     break;
16205             }
16206         }
16207                 
16208         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16209         
16210 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16211         
16212         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16213         
16214         this.picker().on('mousedown', this.onMousedown, this);
16215         this.picker().on('click', this.onClick, this);
16216         
16217         this.picker().addClass('datepicker-dropdown');
16218         
16219         this.startViewMode = this.viewMode;
16220         
16221         if(this.singleMode){
16222             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16223                 v.setVisibilityMode(Roo.Element.DISPLAY)
16224                 v.hide();
16225             });
16226             
16227             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16228                 v.setStyle('width', '189px');
16229             });
16230         }
16231         
16232         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16233             if(!this.calendarWeeks){
16234                 v.remove();
16235                 return;
16236             }
16237             
16238             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16239             v.attr('colspan', function(i, val){
16240                 return parseInt(val) + 1;
16241             });
16242         })
16243                         
16244         
16245         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16246         
16247         this.setStartDate(this.startDate);
16248         this.setEndDate(this.endDate);
16249         
16250         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16251         
16252         this.fillDow();
16253         this.fillMonths();
16254         this.update();
16255         this.showMode();
16256         
16257         if(this.isInline) {
16258             this.show();
16259         }
16260     },
16261     
16262     picker : function()
16263     {
16264         return this.pickerEl;
16265 //        return this.el.select('.datepicker', true).first();
16266     },
16267     
16268     fillDow: function()
16269     {
16270         var dowCnt = this.weekStart;
16271         
16272         var dow = {
16273             tag: 'tr',
16274             cn: [
16275                 
16276             ]
16277         };
16278         
16279         if(this.calendarWeeks){
16280             dow.cn.push({
16281                 tag: 'th',
16282                 cls: 'cw',
16283                 html: '&nbsp;'
16284             })
16285         }
16286         
16287         while (dowCnt < this.weekStart + 7) {
16288             dow.cn.push({
16289                 tag: 'th',
16290                 cls: 'dow',
16291                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16292             });
16293         }
16294         
16295         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16296     },
16297     
16298     fillMonths: function()
16299     {    
16300         var i = 0;
16301         var months = this.picker().select('>.datepicker-months td', true).first();
16302         
16303         months.dom.innerHTML = '';
16304         
16305         while (i < 12) {
16306             var month = {
16307                 tag: 'span',
16308                 cls: 'month',
16309                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16310             }
16311             
16312             months.createChild(month);
16313         }
16314         
16315     },
16316     
16317     update: function()
16318     {
16319         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;
16320         
16321         if (this.date < this.startDate) {
16322             this.viewDate = new Date(this.startDate);
16323         } else if (this.date > this.endDate) {
16324             this.viewDate = new Date(this.endDate);
16325         } else {
16326             this.viewDate = new Date(this.date);
16327         }
16328         
16329         this.fill();
16330     },
16331     
16332     fill: function() 
16333     {
16334         var d = new Date(this.viewDate),
16335                 year = d.getUTCFullYear(),
16336                 month = d.getUTCMonth(),
16337                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16338                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16339                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16340                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16341                 currentDate = this.date && this.date.valueOf(),
16342                 today = this.UTCToday();
16343         
16344         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16345         
16346 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16347         
16348 //        this.picker.select('>tfoot th.today').
16349 //                                              .text(dates[this.language].today)
16350 //                                              .toggle(this.todayBtn !== false);
16351     
16352         this.updateNavArrows();
16353         this.fillMonths();
16354                                                 
16355         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16356         
16357         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16358          
16359         prevMonth.setUTCDate(day);
16360         
16361         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16362         
16363         var nextMonth = new Date(prevMonth);
16364         
16365         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16366         
16367         nextMonth = nextMonth.valueOf();
16368         
16369         var fillMonths = false;
16370         
16371         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16372         
16373         while(prevMonth.valueOf() < nextMonth) {
16374             var clsName = '';
16375             
16376             if (prevMonth.getUTCDay() === this.weekStart) {
16377                 if(fillMonths){
16378                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16379                 }
16380                     
16381                 fillMonths = {
16382                     tag: 'tr',
16383                     cn: []
16384                 };
16385                 
16386                 if(this.calendarWeeks){
16387                     // ISO 8601: First week contains first thursday.
16388                     // ISO also states week starts on Monday, but we can be more abstract here.
16389                     var
16390                     // Start of current week: based on weekstart/current date
16391                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16392                     // Thursday of this week
16393                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16394                     // First Thursday of year, year from thursday
16395                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16396                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16397                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16398                     
16399                     fillMonths.cn.push({
16400                         tag: 'td',
16401                         cls: 'cw',
16402                         html: calWeek
16403                     });
16404                 }
16405             }
16406             
16407             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16408                 clsName += ' old';
16409             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16410                 clsName += ' new';
16411             }
16412             if (this.todayHighlight &&
16413                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16414                 prevMonth.getUTCMonth() == today.getMonth() &&
16415                 prevMonth.getUTCDate() == today.getDate()) {
16416                 clsName += ' today';
16417             }
16418             
16419             if (currentDate && prevMonth.valueOf() === currentDate) {
16420                 clsName += ' active';
16421             }
16422             
16423             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16424                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16425                     clsName += ' disabled';
16426             }
16427             
16428             fillMonths.cn.push({
16429                 tag: 'td',
16430                 cls: 'day ' + clsName,
16431                 html: prevMonth.getDate()
16432             })
16433             
16434             prevMonth.setDate(prevMonth.getDate()+1);
16435         }
16436           
16437         var currentYear = this.date && this.date.getUTCFullYear();
16438         var currentMonth = this.date && this.date.getUTCMonth();
16439         
16440         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16441         
16442         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16443             v.removeClass('active');
16444             
16445             if(currentYear === year && k === currentMonth){
16446                 v.addClass('active');
16447             }
16448             
16449             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16450                 v.addClass('disabled');
16451             }
16452             
16453         });
16454         
16455         
16456         year = parseInt(year/10, 10) * 10;
16457         
16458         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16459         
16460         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16461         
16462         year -= 1;
16463         for (var i = -1; i < 11; i++) {
16464             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16465                 tag: 'span',
16466                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16467                 html: year
16468             })
16469             
16470             year += 1;
16471         }
16472     },
16473     
16474     showMode: function(dir) 
16475     {
16476         if (dir) {
16477             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16478         }
16479         
16480         Roo.each(this.picker().select('>div',true).elements, function(v){
16481             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16482             v.hide();
16483         });
16484         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16485     },
16486     
16487     place: function()
16488     {
16489         if(this.isInline) return;
16490         
16491         this.picker().removeClass(['bottom', 'top']);
16492         
16493         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16494             /*
16495              * place to the top of element!
16496              *
16497              */
16498             
16499             this.picker().addClass('top');
16500             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16501             
16502             return;
16503         }
16504         
16505         this.picker().addClass('bottom');
16506         
16507         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16508     },
16509     
16510     parseDate : function(value)
16511     {
16512         if(!value || value instanceof Date){
16513             return value;
16514         }
16515         var v = Date.parseDate(value, this.format);
16516         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16517             v = Date.parseDate(value, 'Y-m-d');
16518         }
16519         if(!v && this.altFormats){
16520             if(!this.altFormatsArray){
16521                 this.altFormatsArray = this.altFormats.split("|");
16522             }
16523             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16524                 v = Date.parseDate(value, this.altFormatsArray[i]);
16525             }
16526         }
16527         return v;
16528     },
16529     
16530     formatDate : function(date, fmt)
16531     {   
16532         return (!date || !(date instanceof Date)) ?
16533         date : date.dateFormat(fmt || this.format);
16534     },
16535     
16536     onFocus : function()
16537     {
16538         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16539         this.show();
16540     },
16541     
16542     onBlur : function()
16543     {
16544         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16545         
16546         var d = this.inputEl().getValue();
16547         
16548         this.setValue(d);
16549                 
16550         this.hide();
16551     },
16552     
16553     show : function()
16554     {
16555         this.picker().show();
16556         this.update();
16557         this.place();
16558         
16559         this.fireEvent('show', this, this.date);
16560     },
16561     
16562     hide : function()
16563     {
16564         if(this.isInline) return;
16565         this.picker().hide();
16566         this.viewMode = this.startViewMode;
16567         this.showMode();
16568         
16569         this.fireEvent('hide', this, this.date);
16570         
16571     },
16572     
16573     onMousedown: function(e)
16574     {
16575         e.stopPropagation();
16576         e.preventDefault();
16577     },
16578     
16579     keyup: function(e)
16580     {
16581         Roo.bootstrap.DateField.superclass.keyup.call(this);
16582         this.update();
16583     },
16584
16585     setValue: function(v)
16586     {
16587         
16588         // v can be a string or a date..
16589         
16590         
16591         var d = new Date(this.parseDate(v) ).clearTime();
16592         
16593         if(isNaN(d.getTime())){
16594             this.date = this.viewDate = '';
16595             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16596             return;
16597         }
16598         
16599         v = this.formatDate(d);
16600         
16601         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16602         
16603         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16604      
16605         this.update();
16606
16607         this.fireEvent('select', this, this.date);
16608         
16609     },
16610     
16611     getValue: function()
16612     {
16613         return this.formatDate(this.date);
16614     },
16615     
16616     fireKey: function(e)
16617     {
16618         if (!this.picker().isVisible()){
16619             if (e.keyCode == 27) // allow escape to hide and re-show picker
16620                 this.show();
16621             return;
16622         }
16623         
16624         var dateChanged = false,
16625         dir, day, month,
16626         newDate, newViewDate;
16627         
16628         switch(e.keyCode){
16629             case 27: // escape
16630                 this.hide();
16631                 e.preventDefault();
16632                 break;
16633             case 37: // left
16634             case 39: // right
16635                 if (!this.keyboardNavigation) break;
16636                 dir = e.keyCode == 37 ? -1 : 1;
16637                 
16638                 if (e.ctrlKey){
16639                     newDate = this.moveYear(this.date, dir);
16640                     newViewDate = this.moveYear(this.viewDate, dir);
16641                 } else if (e.shiftKey){
16642                     newDate = this.moveMonth(this.date, dir);
16643                     newViewDate = this.moveMonth(this.viewDate, dir);
16644                 } else {
16645                     newDate = new Date(this.date);
16646                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16647                     newViewDate = new Date(this.viewDate);
16648                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16649                 }
16650                 if (this.dateWithinRange(newDate)){
16651                     this.date = newDate;
16652                     this.viewDate = newViewDate;
16653                     this.setValue(this.formatDate(this.date));
16654 //                    this.update();
16655                     e.preventDefault();
16656                     dateChanged = true;
16657                 }
16658                 break;
16659             case 38: // up
16660             case 40: // down
16661                 if (!this.keyboardNavigation) break;
16662                 dir = e.keyCode == 38 ? -1 : 1;
16663                 if (e.ctrlKey){
16664                     newDate = this.moveYear(this.date, dir);
16665                     newViewDate = this.moveYear(this.viewDate, dir);
16666                 } else if (e.shiftKey){
16667                     newDate = this.moveMonth(this.date, dir);
16668                     newViewDate = this.moveMonth(this.viewDate, dir);
16669                 } else {
16670                     newDate = new Date(this.date);
16671                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16672                     newViewDate = new Date(this.viewDate);
16673                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16674                 }
16675                 if (this.dateWithinRange(newDate)){
16676                     this.date = newDate;
16677                     this.viewDate = newViewDate;
16678                     this.setValue(this.formatDate(this.date));
16679 //                    this.update();
16680                     e.preventDefault();
16681                     dateChanged = true;
16682                 }
16683                 break;
16684             case 13: // enter
16685                 this.setValue(this.formatDate(this.date));
16686                 this.hide();
16687                 e.preventDefault();
16688                 break;
16689             case 9: // tab
16690                 this.setValue(this.formatDate(this.date));
16691                 this.hide();
16692                 break;
16693             case 16: // shift
16694             case 17: // ctrl
16695             case 18: // alt
16696                 break;
16697             default :
16698                 this.hide();
16699                 
16700         }
16701     },
16702     
16703     
16704     onClick: function(e) 
16705     {
16706         e.stopPropagation();
16707         e.preventDefault();
16708         
16709         var target = e.getTarget();
16710         
16711         if(target.nodeName.toLowerCase() === 'i'){
16712             target = Roo.get(target).dom.parentNode;
16713         }
16714         
16715         var nodeName = target.nodeName;
16716         var className = target.className;
16717         var html = target.innerHTML;
16718         //Roo.log(nodeName);
16719         
16720         switch(nodeName.toLowerCase()) {
16721             case 'th':
16722                 switch(className) {
16723                     case 'switch':
16724                         this.showMode(1);
16725                         break;
16726                     case 'prev':
16727                     case 'next':
16728                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16729                         switch(this.viewMode){
16730                                 case 0:
16731                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16732                                         break;
16733                                 case 1:
16734                                 case 2:
16735                                         this.viewDate = this.moveYear(this.viewDate, dir);
16736                                         break;
16737                         }
16738                         this.fill();
16739                         break;
16740                     case 'today':
16741                         var date = new Date();
16742                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16743 //                        this.fill()
16744                         this.setValue(this.formatDate(this.date));
16745                         
16746                         this.hide();
16747                         break;
16748                 }
16749                 break;
16750             case 'span':
16751                 if (className.indexOf('disabled') < 0) {
16752                     this.viewDate.setUTCDate(1);
16753                     if (className.indexOf('month') > -1) {
16754                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16755                     } else {
16756                         var year = parseInt(html, 10) || 0;
16757                         this.viewDate.setUTCFullYear(year);
16758                         
16759                     }
16760                     
16761                     if(this.singleMode){
16762                         this.setValue(this.formatDate(this.viewDate));
16763                         this.hide();
16764                         return;
16765                     }
16766                     
16767                     this.showMode(-1);
16768                     this.fill();
16769                 }
16770                 break;
16771                 
16772             case 'td':
16773                 //Roo.log(className);
16774                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16775                     var day = parseInt(html, 10) || 1;
16776                     var year = this.viewDate.getUTCFullYear(),
16777                         month = this.viewDate.getUTCMonth();
16778
16779                     if (className.indexOf('old') > -1) {
16780                         if(month === 0 ){
16781                             month = 11;
16782                             year -= 1;
16783                         }else{
16784                             month -= 1;
16785                         }
16786                     } else if (className.indexOf('new') > -1) {
16787                         if (month == 11) {
16788                             month = 0;
16789                             year += 1;
16790                         } else {
16791                             month += 1;
16792                         }
16793                     }
16794                     //Roo.log([year,month,day]);
16795                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16796                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16797 //                    this.fill();
16798                     //Roo.log(this.formatDate(this.date));
16799                     this.setValue(this.formatDate(this.date));
16800                     this.hide();
16801                 }
16802                 break;
16803         }
16804     },
16805     
16806     setStartDate: function(startDate)
16807     {
16808         this.startDate = startDate || -Infinity;
16809         if (this.startDate !== -Infinity) {
16810             this.startDate = this.parseDate(this.startDate);
16811         }
16812         this.update();
16813         this.updateNavArrows();
16814     },
16815
16816     setEndDate: function(endDate)
16817     {
16818         this.endDate = endDate || Infinity;
16819         if (this.endDate !== Infinity) {
16820             this.endDate = this.parseDate(this.endDate);
16821         }
16822         this.update();
16823         this.updateNavArrows();
16824     },
16825     
16826     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16827     {
16828         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16829         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16830             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16831         }
16832         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16833             return parseInt(d, 10);
16834         });
16835         this.update();
16836         this.updateNavArrows();
16837     },
16838     
16839     updateNavArrows: function() 
16840     {
16841         if(this.singleMode){
16842             return;
16843         }
16844         
16845         var d = new Date(this.viewDate),
16846         year = d.getUTCFullYear(),
16847         month = d.getUTCMonth();
16848         
16849         Roo.each(this.picker().select('.prev', true).elements, function(v){
16850             v.show();
16851             switch (this.viewMode) {
16852                 case 0:
16853
16854                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16855                         v.hide();
16856                     }
16857                     break;
16858                 case 1:
16859                 case 2:
16860                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16861                         v.hide();
16862                     }
16863                     break;
16864             }
16865         });
16866         
16867         Roo.each(this.picker().select('.next', true).elements, function(v){
16868             v.show();
16869             switch (this.viewMode) {
16870                 case 0:
16871
16872                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16873                         v.hide();
16874                     }
16875                     break;
16876                 case 1:
16877                 case 2:
16878                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16879                         v.hide();
16880                     }
16881                     break;
16882             }
16883         })
16884     },
16885     
16886     moveMonth: function(date, dir)
16887     {
16888         if (!dir) return date;
16889         var new_date = new Date(date.valueOf()),
16890         day = new_date.getUTCDate(),
16891         month = new_date.getUTCMonth(),
16892         mag = Math.abs(dir),
16893         new_month, test;
16894         dir = dir > 0 ? 1 : -1;
16895         if (mag == 1){
16896             test = dir == -1
16897             // If going back one month, make sure month is not current month
16898             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16899             ? function(){
16900                 return new_date.getUTCMonth() == month;
16901             }
16902             // If going forward one month, make sure month is as expected
16903             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16904             : function(){
16905                 return new_date.getUTCMonth() != new_month;
16906             };
16907             new_month = month + dir;
16908             new_date.setUTCMonth(new_month);
16909             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16910             if (new_month < 0 || new_month > 11)
16911                 new_month = (new_month + 12) % 12;
16912         } else {
16913             // For magnitudes >1, move one month at a time...
16914             for (var i=0; i<mag; i++)
16915                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16916                 new_date = this.moveMonth(new_date, dir);
16917             // ...then reset the day, keeping it in the new month
16918             new_month = new_date.getUTCMonth();
16919             new_date.setUTCDate(day);
16920             test = function(){
16921                 return new_month != new_date.getUTCMonth();
16922             };
16923         }
16924         // Common date-resetting loop -- if date is beyond end of month, make it
16925         // end of month
16926         while (test()){
16927             new_date.setUTCDate(--day);
16928             new_date.setUTCMonth(new_month);
16929         }
16930         return new_date;
16931     },
16932
16933     moveYear: function(date, dir)
16934     {
16935         return this.moveMonth(date, dir*12);
16936     },
16937
16938     dateWithinRange: function(date)
16939     {
16940         return date >= this.startDate && date <= this.endDate;
16941     },
16942
16943     
16944     remove: function() 
16945     {
16946         this.picker().remove();
16947     }
16948    
16949 });
16950
16951 Roo.apply(Roo.bootstrap.DateField,  {
16952     
16953     head : {
16954         tag: 'thead',
16955         cn: [
16956         {
16957             tag: 'tr',
16958             cn: [
16959             {
16960                 tag: 'th',
16961                 cls: 'prev',
16962                 html: '<i class="fa fa-arrow-left"/>'
16963             },
16964             {
16965                 tag: 'th',
16966                 cls: 'switch',
16967                 colspan: '5'
16968             },
16969             {
16970                 tag: 'th',
16971                 cls: 'next',
16972                 html: '<i class="fa fa-arrow-right"/>'
16973             }
16974
16975             ]
16976         }
16977         ]
16978     },
16979     
16980     content : {
16981         tag: 'tbody',
16982         cn: [
16983         {
16984             tag: 'tr',
16985             cn: [
16986             {
16987                 tag: 'td',
16988                 colspan: '7'
16989             }
16990             ]
16991         }
16992         ]
16993     },
16994     
16995     footer : {
16996         tag: 'tfoot',
16997         cn: [
16998         {
16999             tag: 'tr',
17000             cn: [
17001             {
17002                 tag: 'th',
17003                 colspan: '7',
17004                 cls: 'today'
17005             }
17006                     
17007             ]
17008         }
17009         ]
17010     },
17011     
17012     dates:{
17013         en: {
17014             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17015             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17016             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17017             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17018             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17019             today: "Today"
17020         }
17021     },
17022     
17023     modes: [
17024     {
17025         clsName: 'days',
17026         navFnc: 'Month',
17027         navStep: 1
17028     },
17029     {
17030         clsName: 'months',
17031         navFnc: 'FullYear',
17032         navStep: 1
17033     },
17034     {
17035         clsName: 'years',
17036         navFnc: 'FullYear',
17037         navStep: 10
17038     }]
17039 });
17040
17041 Roo.apply(Roo.bootstrap.DateField,  {
17042   
17043     template : {
17044         tag: 'div',
17045         cls: 'datepicker dropdown-menu roo-dynamic',
17046         cn: [
17047         {
17048             tag: 'div',
17049             cls: 'datepicker-days',
17050             cn: [
17051             {
17052                 tag: 'table',
17053                 cls: 'table-condensed',
17054                 cn:[
17055                 Roo.bootstrap.DateField.head,
17056                 {
17057                     tag: 'tbody'
17058                 },
17059                 Roo.bootstrap.DateField.footer
17060                 ]
17061             }
17062             ]
17063         },
17064         {
17065             tag: 'div',
17066             cls: 'datepicker-months',
17067             cn: [
17068             {
17069                 tag: 'table',
17070                 cls: 'table-condensed',
17071                 cn:[
17072                 Roo.bootstrap.DateField.head,
17073                 Roo.bootstrap.DateField.content,
17074                 Roo.bootstrap.DateField.footer
17075                 ]
17076             }
17077             ]
17078         },
17079         {
17080             tag: 'div',
17081             cls: 'datepicker-years',
17082             cn: [
17083             {
17084                 tag: 'table',
17085                 cls: 'table-condensed',
17086                 cn:[
17087                 Roo.bootstrap.DateField.head,
17088                 Roo.bootstrap.DateField.content,
17089                 Roo.bootstrap.DateField.footer
17090                 ]
17091             }
17092             ]
17093         }
17094         ]
17095     }
17096 });
17097
17098  
17099
17100  /*
17101  * - LGPL
17102  *
17103  * TimeField
17104  * 
17105  */
17106
17107 /**
17108  * @class Roo.bootstrap.TimeField
17109  * @extends Roo.bootstrap.Input
17110  * Bootstrap DateField class
17111  * 
17112  * 
17113  * @constructor
17114  * Create a new TimeField
17115  * @param {Object} config The config object
17116  */
17117
17118 Roo.bootstrap.TimeField = function(config){
17119     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17120     this.addEvents({
17121             /**
17122              * @event show
17123              * Fires when this field show.
17124              * @param {Roo.bootstrap.DateField} thisthis
17125              * @param {Mixed} date The date value
17126              */
17127             show : true,
17128             /**
17129              * @event show
17130              * Fires when this field hide.
17131              * @param {Roo.bootstrap.DateField} this
17132              * @param {Mixed} date The date value
17133              */
17134             hide : true,
17135             /**
17136              * @event select
17137              * Fires when select a date.
17138              * @param {Roo.bootstrap.DateField} this
17139              * @param {Mixed} date The date value
17140              */
17141             select : true
17142         });
17143 };
17144
17145 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17146     
17147     /**
17148      * @cfg {String} format
17149      * The default time format string which can be overriden for localization support.  The format must be
17150      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17151      */
17152     format : "H:i",
17153        
17154     onRender: function(ct, position)
17155     {
17156         
17157         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17158                 
17159         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17160         
17161         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17162         
17163         this.pop = this.picker().select('>.datepicker-time',true).first();
17164         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17165         
17166         this.picker().on('mousedown', this.onMousedown, this);
17167         this.picker().on('click', this.onClick, this);
17168         
17169         this.picker().addClass('datepicker-dropdown');
17170     
17171         this.fillTime();
17172         this.update();
17173             
17174         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17175         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17176         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17177         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17178         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17179         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17180
17181     },
17182     
17183     fireKey: function(e){
17184         if (!this.picker().isVisible()){
17185             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17186                 this.show();
17187             }
17188             return;
17189         }
17190
17191         e.preventDefault();
17192         
17193         switch(e.keyCode){
17194             case 27: // escape
17195                 this.hide();
17196                 break;
17197             case 37: // left
17198             case 39: // right
17199                 this.onTogglePeriod();
17200                 break;
17201             case 38: // up
17202                 this.onIncrementMinutes();
17203                 break;
17204             case 40: // down
17205                 this.onDecrementMinutes();
17206                 break;
17207             case 13: // enter
17208             case 9: // tab
17209                 this.setTime();
17210                 break;
17211         }
17212     },
17213     
17214     onClick: function(e) {
17215         e.stopPropagation();
17216         e.preventDefault();
17217     },
17218     
17219     picker : function()
17220     {
17221         return this.el.select('.datepicker', true).first();
17222     },
17223     
17224     fillTime: function()
17225     {    
17226         var time = this.pop.select('tbody', true).first();
17227         
17228         time.dom.innerHTML = '';
17229         
17230         time.createChild({
17231             tag: 'tr',
17232             cn: [
17233                 {
17234                     tag: 'td',
17235                     cn: [
17236                         {
17237                             tag: 'a',
17238                             href: '#',
17239                             cls: 'btn',
17240                             cn: [
17241                                 {
17242                                     tag: 'span',
17243                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17244                                 }
17245                             ]
17246                         } 
17247                     ]
17248                 },
17249                 {
17250                     tag: 'td',
17251                     cls: 'separator'
17252                 },
17253                 {
17254                     tag: 'td',
17255                     cn: [
17256                         {
17257                             tag: 'a',
17258                             href: '#',
17259                             cls: 'btn',
17260                             cn: [
17261                                 {
17262                                     tag: 'span',
17263                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17264                                 }
17265                             ]
17266                         }
17267                     ]
17268                 },
17269                 {
17270                     tag: 'td',
17271                     cls: 'separator'
17272                 }
17273             ]
17274         });
17275         
17276         time.createChild({
17277             tag: 'tr',
17278             cn: [
17279                 {
17280                     tag: 'td',
17281                     cn: [
17282                         {
17283                             tag: 'span',
17284                             cls: 'timepicker-hour',
17285                             html: '00'
17286                         }  
17287                     ]
17288                 },
17289                 {
17290                     tag: 'td',
17291                     cls: 'separator',
17292                     html: ':'
17293                 },
17294                 {
17295                     tag: 'td',
17296                     cn: [
17297                         {
17298                             tag: 'span',
17299                             cls: 'timepicker-minute',
17300                             html: '00'
17301                         }  
17302                     ]
17303                 },
17304                 {
17305                     tag: 'td',
17306                     cls: 'separator'
17307                 },
17308                 {
17309                     tag: 'td',
17310                     cn: [
17311                         {
17312                             tag: 'button',
17313                             type: 'button',
17314                             cls: 'btn btn-primary period',
17315                             html: 'AM'
17316                             
17317                         }
17318                     ]
17319                 }
17320             ]
17321         });
17322         
17323         time.createChild({
17324             tag: 'tr',
17325             cn: [
17326                 {
17327                     tag: 'td',
17328                     cn: [
17329                         {
17330                             tag: 'a',
17331                             href: '#',
17332                             cls: 'btn',
17333                             cn: [
17334                                 {
17335                                     tag: 'span',
17336                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17337                                 }
17338                             ]
17339                         }
17340                     ]
17341                 },
17342                 {
17343                     tag: 'td',
17344                     cls: 'separator'
17345                 },
17346                 {
17347                     tag: 'td',
17348                     cn: [
17349                         {
17350                             tag: 'a',
17351                             href: '#',
17352                             cls: 'btn',
17353                             cn: [
17354                                 {
17355                                     tag: 'span',
17356                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17357                                 }
17358                             ]
17359                         }
17360                     ]
17361                 },
17362                 {
17363                     tag: 'td',
17364                     cls: 'separator'
17365                 }
17366             ]
17367         });
17368         
17369     },
17370     
17371     update: function()
17372     {
17373         
17374         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17375         
17376         this.fill();
17377     },
17378     
17379     fill: function() 
17380     {
17381         var hours = this.time.getHours();
17382         var minutes = this.time.getMinutes();
17383         var period = 'AM';
17384         
17385         if(hours > 11){
17386             period = 'PM';
17387         }
17388         
17389         if(hours == 0){
17390             hours = 12;
17391         }
17392         
17393         
17394         if(hours > 12){
17395             hours = hours - 12;
17396         }
17397         
17398         if(hours < 10){
17399             hours = '0' + hours;
17400         }
17401         
17402         if(minutes < 10){
17403             minutes = '0' + minutes;
17404         }
17405         
17406         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17407         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17408         this.pop.select('button', true).first().dom.innerHTML = period;
17409         
17410     },
17411     
17412     place: function()
17413     {   
17414         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17415         
17416         var cls = ['bottom'];
17417         
17418         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17419             cls.pop();
17420             cls.push('top');
17421         }
17422         
17423         cls.push('right');
17424         
17425         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17426             cls.pop();
17427             cls.push('left');
17428         }
17429         
17430         this.picker().addClass(cls.join('-'));
17431         
17432         var _this = this;
17433         
17434         Roo.each(cls, function(c){
17435             if(c == 'bottom'){
17436                 _this.picker().setTop(_this.inputEl().getHeight());
17437                 return;
17438             }
17439             if(c == 'top'){
17440                 _this.picker().setTop(0 - _this.picker().getHeight());
17441                 return;
17442             }
17443             
17444             if(c == 'left'){
17445                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17446                 return;
17447             }
17448             if(c == 'right'){
17449                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17450                 return;
17451             }
17452         });
17453         
17454     },
17455   
17456     onFocus : function()
17457     {
17458         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17459         this.show();
17460     },
17461     
17462     onBlur : function()
17463     {
17464         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17465         this.hide();
17466     },
17467     
17468     show : function()
17469     {
17470         this.picker().show();
17471         this.pop.show();
17472         this.update();
17473         this.place();
17474         
17475         this.fireEvent('show', this, this.date);
17476     },
17477     
17478     hide : function()
17479     {
17480         this.picker().hide();
17481         this.pop.hide();
17482         
17483         this.fireEvent('hide', this, this.date);
17484     },
17485     
17486     setTime : function()
17487     {
17488         this.hide();
17489         this.setValue(this.time.format(this.format));
17490         
17491         this.fireEvent('select', this, this.date);
17492         
17493         
17494     },
17495     
17496     onMousedown: function(e){
17497         e.stopPropagation();
17498         e.preventDefault();
17499     },
17500     
17501     onIncrementHours: function()
17502     {
17503         Roo.log('onIncrementHours');
17504         this.time = this.time.add(Date.HOUR, 1);
17505         this.update();
17506         
17507     },
17508     
17509     onDecrementHours: function()
17510     {
17511         Roo.log('onDecrementHours');
17512         this.time = this.time.add(Date.HOUR, -1);
17513         this.update();
17514     },
17515     
17516     onIncrementMinutes: function()
17517     {
17518         Roo.log('onIncrementMinutes');
17519         this.time = this.time.add(Date.MINUTE, 1);
17520         this.update();
17521     },
17522     
17523     onDecrementMinutes: function()
17524     {
17525         Roo.log('onDecrementMinutes');
17526         this.time = this.time.add(Date.MINUTE, -1);
17527         this.update();
17528     },
17529     
17530     onTogglePeriod: function()
17531     {
17532         Roo.log('onTogglePeriod');
17533         this.time = this.time.add(Date.HOUR, 12);
17534         this.update();
17535     }
17536     
17537    
17538 });
17539
17540 Roo.apply(Roo.bootstrap.TimeField,  {
17541     
17542     content : {
17543         tag: 'tbody',
17544         cn: [
17545             {
17546                 tag: 'tr',
17547                 cn: [
17548                 {
17549                     tag: 'td',
17550                     colspan: '7'
17551                 }
17552                 ]
17553             }
17554         ]
17555     },
17556     
17557     footer : {
17558         tag: 'tfoot',
17559         cn: [
17560             {
17561                 tag: 'tr',
17562                 cn: [
17563                 {
17564                     tag: 'th',
17565                     colspan: '7',
17566                     cls: '',
17567                     cn: [
17568                         {
17569                             tag: 'button',
17570                             cls: 'btn btn-info ok',
17571                             html: 'OK'
17572                         }
17573                     ]
17574                 }
17575
17576                 ]
17577             }
17578         ]
17579     }
17580 });
17581
17582 Roo.apply(Roo.bootstrap.TimeField,  {
17583   
17584     template : {
17585         tag: 'div',
17586         cls: 'datepicker dropdown-menu',
17587         cn: [
17588             {
17589                 tag: 'div',
17590                 cls: 'datepicker-time',
17591                 cn: [
17592                 {
17593                     tag: 'table',
17594                     cls: 'table-condensed',
17595                     cn:[
17596                     Roo.bootstrap.TimeField.content,
17597                     Roo.bootstrap.TimeField.footer
17598                     ]
17599                 }
17600                 ]
17601             }
17602         ]
17603     }
17604 });
17605
17606  
17607
17608  /*
17609  * - LGPL
17610  *
17611  * MonthField
17612  * 
17613  */
17614
17615 /**
17616  * @class Roo.bootstrap.MonthField
17617  * @extends Roo.bootstrap.Input
17618  * Bootstrap MonthField class
17619  * 
17620  * @cfg {String} language default en
17621  * 
17622  * @constructor
17623  * Create a new MonthField
17624  * @param {Object} config The config object
17625  */
17626
17627 Roo.bootstrap.MonthField = function(config){
17628     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17629     
17630     this.addEvents({
17631         /**
17632          * @event show
17633          * Fires when this field show.
17634          * @param {Roo.bootstrap.MonthField} this
17635          * @param {Mixed} date The date value
17636          */
17637         show : true,
17638         /**
17639          * @event show
17640          * Fires when this field hide.
17641          * @param {Roo.bootstrap.MonthField} this
17642          * @param {Mixed} date The date value
17643          */
17644         hide : true,
17645         /**
17646          * @event select
17647          * Fires when select a date.
17648          * @param {Roo.bootstrap.MonthField} this
17649          * @param {String} oldvalue The old value
17650          * @param {String} newvalue The new value
17651          */
17652         select : true
17653     });
17654 };
17655
17656 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17657     
17658     onRender: function(ct, position)
17659     {
17660         
17661         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17662         
17663         this.language = this.language || 'en';
17664         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17665         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17666         
17667         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17668         this.isInline = false;
17669         this.isInput = true;
17670         this.component = this.el.select('.add-on', true).first() || false;
17671         this.component = (this.component && this.component.length === 0) ? false : this.component;
17672         this.hasInput = this.component && this.inputEL().length;
17673         
17674         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17675         
17676         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17677         
17678         this.picker().on('mousedown', this.onMousedown, this);
17679         this.picker().on('click', this.onClick, this);
17680         
17681         this.picker().addClass('datepicker-dropdown');
17682         
17683         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17684             v.setStyle('width', '189px');
17685         });
17686         
17687         this.fillMonths();
17688         
17689         this.update();
17690         
17691         if(this.isInline) {
17692             this.show();
17693         }
17694         
17695     },
17696     
17697     setValue: function(v, suppressEvent)
17698     {   
17699         var o = this.getValue();
17700         
17701         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17702         
17703         this.update();
17704
17705         if(suppressEvent !== true){
17706             this.fireEvent('select', this, o, v);
17707         }
17708         
17709     },
17710     
17711     getValue: function()
17712     {
17713         return this.value;
17714     },
17715     
17716     onClick: function(e) 
17717     {
17718         e.stopPropagation();
17719         e.preventDefault();
17720         
17721         var target = e.getTarget();
17722         
17723         if(target.nodeName.toLowerCase() === 'i'){
17724             target = Roo.get(target).dom.parentNode;
17725         }
17726         
17727         var nodeName = target.nodeName;
17728         var className = target.className;
17729         var html = target.innerHTML;
17730         
17731         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17732             return;
17733         }
17734         
17735         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17736         
17737         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17738         
17739         this.hide();
17740                         
17741     },
17742     
17743     picker : function()
17744     {
17745         return this.pickerEl;
17746     },
17747     
17748     fillMonths: function()
17749     {    
17750         var i = 0;
17751         var months = this.picker().select('>.datepicker-months td', true).first();
17752         
17753         months.dom.innerHTML = '';
17754         
17755         while (i < 12) {
17756             var month = {
17757                 tag: 'span',
17758                 cls: 'month',
17759                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17760             }
17761             
17762             months.createChild(month);
17763         }
17764         
17765     },
17766     
17767     update: function()
17768     {
17769         var _this = this;
17770         
17771         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17772             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17773         }
17774         
17775         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17776             e.removeClass('active');
17777             
17778             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17779                 e.addClass('active');
17780             }
17781         })
17782     },
17783     
17784     place: function()
17785     {
17786         if(this.isInline) return;
17787         
17788         this.picker().removeClass(['bottom', 'top']);
17789         
17790         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17791             /*
17792              * place to the top of element!
17793              *
17794              */
17795             
17796             this.picker().addClass('top');
17797             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17798             
17799             return;
17800         }
17801         
17802         this.picker().addClass('bottom');
17803         
17804         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17805     },
17806     
17807     onFocus : function()
17808     {
17809         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17810         this.show();
17811     },
17812     
17813     onBlur : function()
17814     {
17815         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17816         
17817         var d = this.inputEl().getValue();
17818         
17819         this.setValue(d);
17820                 
17821         this.hide();
17822     },
17823     
17824     show : function()
17825     {
17826         this.picker().show();
17827         this.picker().select('>.datepicker-months', true).first().show();
17828         this.update();
17829         this.place();
17830         
17831         this.fireEvent('show', this, this.date);
17832     },
17833     
17834     hide : function()
17835     {
17836         if(this.isInline) return;
17837         this.picker().hide();
17838         this.fireEvent('hide', this, this.date);
17839         
17840     },
17841     
17842     onMousedown: function(e)
17843     {
17844         e.stopPropagation();
17845         e.preventDefault();
17846     },
17847     
17848     keyup: function(e)
17849     {
17850         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17851         this.update();
17852     },
17853
17854     fireKey: function(e)
17855     {
17856         if (!this.picker().isVisible()){
17857             if (e.keyCode == 27) // allow escape to hide and re-show picker
17858                 this.show();
17859             return;
17860         }
17861         
17862         var dir;
17863         
17864         switch(e.keyCode){
17865             case 27: // escape
17866                 this.hide();
17867                 e.preventDefault();
17868                 break;
17869             case 37: // left
17870             case 39: // right
17871                 dir = e.keyCode == 37 ? -1 : 1;
17872                 
17873                 this.vIndex = this.vIndex + dir;
17874                 
17875                 if(this.vIndex < 0){
17876                     this.vIndex = 0;
17877                 }
17878                 
17879                 if(this.vIndex > 11){
17880                     this.vIndex = 11;
17881                 }
17882                 
17883                 if(isNaN(this.vIndex)){
17884                     this.vIndex = 0;
17885                 }
17886                 
17887                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17888                 
17889                 break;
17890             case 38: // up
17891             case 40: // down
17892                 
17893                 dir = e.keyCode == 38 ? -1 : 1;
17894                 
17895                 this.vIndex = this.vIndex + dir * 4;
17896                 
17897                 if(this.vIndex < 0){
17898                     this.vIndex = 0;
17899                 }
17900                 
17901                 if(this.vIndex > 11){
17902                     this.vIndex = 11;
17903                 }
17904                 
17905                 if(isNaN(this.vIndex)){
17906                     this.vIndex = 0;
17907                 }
17908                 
17909                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17910                 break;
17911                 
17912             case 13: // enter
17913                 
17914                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17915                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17916                 }
17917                 
17918                 this.hide();
17919                 e.preventDefault();
17920                 break;
17921             case 9: // tab
17922                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17923                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17924                 }
17925                 this.hide();
17926                 break;
17927             case 16: // shift
17928             case 17: // ctrl
17929             case 18: // alt
17930                 break;
17931             default :
17932                 this.hide();
17933                 
17934         }
17935     },
17936     
17937     remove: function() 
17938     {
17939         this.picker().remove();
17940     }
17941    
17942 });
17943
17944 Roo.apply(Roo.bootstrap.MonthField,  {
17945     
17946     content : {
17947         tag: 'tbody',
17948         cn: [
17949         {
17950             tag: 'tr',
17951             cn: [
17952             {
17953                 tag: 'td',
17954                 colspan: '7'
17955             }
17956             ]
17957         }
17958         ]
17959     },
17960     
17961     dates:{
17962         en: {
17963             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17964             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17965         }
17966     }
17967 });
17968
17969 Roo.apply(Roo.bootstrap.MonthField,  {
17970   
17971     template : {
17972         tag: 'div',
17973         cls: 'datepicker dropdown-menu roo-dynamic',
17974         cn: [
17975             {
17976                 tag: 'div',
17977                 cls: 'datepicker-months',
17978                 cn: [
17979                 {
17980                     tag: 'table',
17981                     cls: 'table-condensed',
17982                     cn:[
17983                         Roo.bootstrap.DateField.content
17984                     ]
17985                 }
17986                 ]
17987             }
17988         ]
17989     }
17990 });
17991
17992  
17993
17994  
17995  /*
17996  * - LGPL
17997  *
17998  * CheckBox
17999  * 
18000  */
18001
18002 /**
18003  * @class Roo.bootstrap.CheckBox
18004  * @extends Roo.bootstrap.Input
18005  * Bootstrap CheckBox class
18006  * 
18007  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18008  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18009  * @cfg {String} boxLabel The text that appears beside the checkbox
18010  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18011  * @cfg {Boolean} checked initnal the element
18012  * @cfg {Boolean} inline inline the element (default false)
18013  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18014  * 
18015  * @constructor
18016  * Create a new CheckBox
18017  * @param {Object} config The config object
18018  */
18019
18020 Roo.bootstrap.CheckBox = function(config){
18021     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18022    
18023     this.addEvents({
18024         /**
18025         * @event check
18026         * Fires when the element is checked or unchecked.
18027         * @param {Roo.bootstrap.CheckBox} this This input
18028         * @param {Boolean} checked The new checked value
18029         */
18030        check : true
18031     });
18032     
18033 };
18034
18035 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18036   
18037     inputType: 'checkbox',
18038     inputValue: 1,
18039     valueOff: 0,
18040     boxLabel: false,
18041     checked: false,
18042     weight : false,
18043     inline: false,
18044     
18045     getAutoCreate : function()
18046     {
18047         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18048         
18049         var id = Roo.id();
18050         
18051         var cfg = {};
18052         
18053         cfg.cls = 'form-group ' + this.inputType; //input-group
18054         
18055         if(this.inline){
18056             cfg.cls += ' ' + this.inputType + '-inline';
18057         }
18058         
18059         var input =  {
18060             tag: 'input',
18061             id : id,
18062             type : this.inputType,
18063             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18064             cls : 'roo-' + this.inputType, //'form-box',
18065             placeholder : this.placeholder || ''
18066             
18067         };
18068         
18069         if (this.weight) { // Validity check?
18070             cfg.cls += " " + this.inputType + "-" + this.weight;
18071         }
18072         
18073         if (this.disabled) {
18074             input.disabled=true;
18075         }
18076         
18077         if(this.checked){
18078             input.checked = this.checked;
18079         }
18080         
18081         if (this.name) {
18082             input.name = this.name;
18083         }
18084         
18085         if (this.size) {
18086             input.cls += ' input-' + this.size;
18087         }
18088         
18089         var settings=this;
18090         
18091         ['xs','sm','md','lg'].map(function(size){
18092             if (settings[size]) {
18093                 cfg.cls += ' col-' + size + '-' + settings[size];
18094             }
18095         });
18096         
18097         var inputblock = input;
18098          
18099         if (this.before || this.after) {
18100             
18101             inputblock = {
18102                 cls : 'input-group',
18103                 cn :  [] 
18104             };
18105             
18106             if (this.before) {
18107                 inputblock.cn.push({
18108                     tag :'span',
18109                     cls : 'input-group-addon',
18110                     html : this.before
18111                 });
18112             }
18113             
18114             inputblock.cn.push(input);
18115             
18116             if (this.after) {
18117                 inputblock.cn.push({
18118                     tag :'span',
18119                     cls : 'input-group-addon',
18120                     html : this.after
18121                 });
18122             }
18123             
18124         }
18125         
18126         if (align ==='left' && this.fieldLabel.length) {
18127                 Roo.log("left and has label");
18128                 cfg.cn = [
18129                     
18130                     {
18131                         tag: 'label',
18132                         'for' :  id,
18133                         cls : 'control-label col-md-' + this.labelWidth,
18134                         html : this.fieldLabel
18135                         
18136                     },
18137                     {
18138                         cls : "col-md-" + (12 - this.labelWidth), 
18139                         cn: [
18140                             inputblock
18141                         ]
18142                     }
18143                     
18144                 ];
18145         } else if ( this.fieldLabel.length) {
18146                 Roo.log(" label");
18147                 cfg.cn = [
18148                    
18149                     {
18150                         tag: this.boxLabel ? 'span' : 'label',
18151                         'for': id,
18152                         cls: 'control-label box-input-label',
18153                         //cls : 'input-group-addon',
18154                         html : this.fieldLabel
18155                         
18156                     },
18157                     
18158                     inputblock
18159                     
18160                 ];
18161
18162         } else {
18163             
18164                 Roo.log(" no label && no align");
18165                 cfg.cn = [  inputblock ] ;
18166                 
18167                 
18168         }
18169         if(this.boxLabel){
18170              var boxLabelCfg = {
18171                 tag: 'label',
18172                 //'for': id, // box label is handled by onclick - so no for...
18173                 cls: 'box-label',
18174                 html: this.boxLabel
18175             }
18176             
18177             if(this.tooltip){
18178                 boxLabelCfg.tooltip = this.tooltip;
18179             }
18180              
18181             cfg.cn.push(boxLabelCfg);
18182         }
18183         
18184         
18185        
18186         return cfg;
18187         
18188     },
18189     
18190     /**
18191      * return the real input element.
18192      */
18193     inputEl: function ()
18194     {
18195         return this.el.select('input.roo-' + this.inputType,true).first();
18196     },
18197     
18198     labelEl: function()
18199     {
18200         return this.el.select('label.control-label',true).first();
18201     },
18202     /* depricated... */
18203     
18204     label: function()
18205     {
18206         return this.labelEl();
18207     },
18208     
18209     initEvents : function()
18210     {
18211 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18212         
18213         this.inputEl().on('click', this.onClick,  this);
18214         
18215         if (this.boxLabel) { 
18216             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18217         }
18218         
18219         this.startValue = this.getValue();
18220         
18221         if(this.groupId){
18222             Roo.bootstrap.CheckBox.register(this);
18223         }
18224     },
18225     
18226     onClick : function()
18227     {   
18228         this.setChecked(!this.checked);
18229     },
18230     
18231     setChecked : function(state,suppressEvent)
18232     {
18233         this.startValue = this.getValue();
18234         
18235         if(this.inputType == 'radio'){
18236             
18237             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18238                 e.dom.checked = false;
18239             });
18240             
18241             this.inputEl().dom.checked = true;
18242             
18243             this.inputEl().dom.value = this.inputValue;
18244             
18245             if(suppressEvent !== true){
18246                 this.fireEvent('check', this, true);
18247             }
18248             
18249             this.validate();
18250             
18251             return;
18252         }
18253         
18254         this.checked = state;
18255         
18256         this.inputEl().dom.checked = state;
18257         
18258         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18259         
18260         if(suppressEvent !== true){
18261             this.fireEvent('check', this, state);
18262         }
18263         
18264         this.validate();
18265     },
18266     
18267     getValue : function()
18268     {
18269         if(this.inputType == 'radio'){
18270             return this.getGroupValue();
18271         }
18272         
18273         return this.inputEl().getValue();
18274         
18275     },
18276     
18277     getGroupValue : function()
18278     {
18279         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18280             return '';
18281         }
18282         
18283         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18284     },
18285     
18286     setValue : function(v,suppressEvent)
18287     {
18288         if(this.inputType == 'radio'){
18289             this.setGroupValue(v, suppressEvent);
18290             return;
18291         }
18292         
18293         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18294         
18295         this.validate();
18296     },
18297     
18298     setGroupValue : function(v, suppressEvent)
18299     {
18300         this.startValue = this.getValue();
18301         
18302         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18303             e.dom.checked = false;
18304             
18305             if(e.dom.value == v){
18306                 e.dom.checked = true;
18307             }
18308         });
18309         
18310         if(suppressEvent !== true){
18311             this.fireEvent('check', this, true);
18312         }
18313
18314         this.validate();
18315         
18316         return;
18317     },
18318     
18319     validate : function()
18320     {
18321         if(
18322                 this.disabled || 
18323                 (this.inputType == 'radio' && this.validateRadio()) ||
18324                 (this.inputType == 'checkbox' && this.validateCheckbox())
18325         ){
18326             this.markValid();
18327             return true;
18328         }
18329         
18330         this.markInvalid();
18331         return false;
18332     },
18333     
18334     validateRadio : function()
18335     {
18336         var valid = false;
18337         
18338         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18339             if(!e.dom.checked){
18340                 return;
18341             }
18342             
18343             valid = true;
18344             
18345             return false;
18346         });
18347         
18348         return valid;
18349     },
18350     
18351     validateCheckbox : function()
18352     {
18353         if(!this.groupId){
18354             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18355         }
18356         
18357         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18358         
18359         if(!group){
18360             return false;
18361         }
18362         
18363         var r = false;
18364         
18365         for(var i in group){
18366             if(r){
18367                 break;
18368             }
18369             
18370             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18371         }
18372         
18373         return r;
18374     },
18375     
18376     /**
18377      * Mark this field as valid
18378      */
18379     markValid : function()
18380     {
18381         if(this.allowBlank){
18382             return;
18383         }
18384         
18385         var _this = this;
18386         
18387         this.fireEvent('valid', this);
18388         
18389         if(this.inputType == 'radio'){
18390             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18391                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18392                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18393             });
18394             
18395             return;
18396         }
18397         
18398         if(!this.groupId){
18399             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18400             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18401             return;
18402         }
18403         
18404         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18405             
18406         if(!group){
18407             return;
18408         }
18409         
18410         for(var i in group){
18411             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18412             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18413         }
18414     },
18415     
18416      /**
18417      * Mark this field as invalid
18418      * @param {String} msg The validation message
18419      */
18420     markInvalid : function(msg)
18421     {
18422         if(this.allowBlank){
18423             return;
18424         }
18425         
18426         var _this = this;
18427         
18428         this.fireEvent('invalid', this, msg);
18429         
18430         if(this.inputType == 'radio'){
18431             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18432                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18433                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18434             });
18435             
18436             return;
18437         }
18438         
18439         if(!this.groupId){
18440             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18441             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18442             return;
18443         }
18444         
18445         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18446             
18447         if(!group){
18448             return;
18449         }
18450         
18451         for(var i in group){
18452             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18453             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18454         }
18455         
18456     }
18457     
18458 });
18459
18460 Roo.apply(Roo.bootstrap.CheckBox, {
18461     
18462     groups: {},
18463     
18464      /**
18465     * register a CheckBox Group
18466     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18467     */
18468     register : function(checkbox)
18469     {
18470         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18471             this.groups[checkbox.groupId] = {};
18472         }
18473         
18474         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18475             return;
18476         }
18477         
18478         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18479         
18480     },
18481     /**
18482     * fetch a CheckBox Group based on the group ID
18483     * @param {string} the group ID
18484     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18485     */
18486     get: function(groupId) {
18487         if (typeof(this.groups[groupId]) == 'undefined') {
18488             return false;
18489         }
18490         
18491         return this.groups[groupId] ;
18492     }
18493     
18494     
18495 });
18496 /*
18497  * - LGPL
18498  *
18499  * Radio
18500  *
18501  *
18502  * not inline
18503  *<div class="radio">
18504   <label>
18505     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18506     Option one is this and that&mdash;be sure to include why it's great
18507   </label>
18508 </div>
18509  *
18510  *
18511  *inline
18512  *<span>
18513  *<label class="radio-inline">fieldLabel</label>
18514  *<label class="radio-inline">
18515   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18516 </label>
18517 <span>
18518  * 
18519  * 
18520  */
18521
18522 /**
18523  * @class Roo.bootstrap.Radio
18524  * @extends Roo.bootstrap.CheckBox
18525  * Bootstrap Radio class
18526
18527  * @constructor
18528  * Create a new Radio
18529  * @param {Object} config The config object
18530  */
18531
18532 Roo.bootstrap.Radio = function(config){
18533     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18534    
18535 };
18536
18537 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18538     
18539     inputType: 'radio',
18540     inputValue: '',
18541     valueOff: '',
18542     
18543     getAutoCreate : function()
18544     {
18545         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18546         align = align || 'left'; // default...
18547         
18548         
18549         
18550         var id = Roo.id();
18551         
18552         var cfg = {
18553                 tag : this.inline ? 'span' : 'div',
18554                 cls : '',
18555                 cn : []
18556         };
18557         
18558         var inline = this.inline ? ' radio-inline' : '';
18559         
18560         var lbl = {
18561                 tag: 'label' ,
18562                 // does not need for, as we wrap the input with it..
18563                 'for' : id,
18564                 cls : 'control-label box-label' + inline,
18565                 cn : []
18566         };
18567         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18568         
18569         var fieldLabel = {
18570             tag: 'label' ,
18571             //cls : 'control-label' + inline,
18572             html : this.fieldLabel,
18573             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18574         };
18575         
18576  
18577         
18578         
18579         var input =  {
18580             tag: 'input',
18581             id : id,
18582             type : this.inputType,
18583             //value : (!this.checked) ? this.valueOff : this.inputValue,
18584             value : this.inputValue,
18585             cls : 'roo-radio',
18586             placeholder : this.placeholder || '' // ?? needed????
18587             
18588         };
18589         if (this.weight) { // Validity check?
18590             input.cls += " radio-" + this.weight;
18591         }
18592         if (this.disabled) {
18593             input.disabled=true;
18594         }
18595         
18596         if(this.checked){
18597             input.checked = this.checked;
18598         }
18599         
18600         if (this.name) {
18601             input.name = this.name;
18602         }
18603         
18604         if (this.size) {
18605             input.cls += ' input-' + this.size;
18606         }
18607         
18608         //?? can span's inline have a width??
18609         
18610         var settings=this;
18611         ['xs','sm','md','lg'].map(function(size){
18612             if (settings[size]) {
18613                 cfg.cls += ' col-' + size + '-' + settings[size];
18614             }
18615         });
18616         
18617         var inputblock = input;
18618         
18619         if (this.before || this.after) {
18620             
18621             inputblock = {
18622                 cls : 'input-group',
18623                 tag : 'span',
18624                 cn :  [] 
18625             };
18626             if (this.before) {
18627                 inputblock.cn.push({
18628                     tag :'span',
18629                     cls : 'input-group-addon',
18630                     html : this.before
18631                 });
18632             }
18633             inputblock.cn.push(input);
18634             if (this.after) {
18635                 inputblock.cn.push({
18636                     tag :'span',
18637                     cls : 'input-group-addon',
18638                     html : this.after
18639                 });
18640             }
18641             
18642         };
18643         
18644         
18645         if (this.fieldLabel && this.fieldLabel.length) {
18646             cfg.cn.push(fieldLabel);
18647         }
18648        
18649         // normal bootstrap puts the input inside the label.
18650         // however with our styled version - it has to go after the input.
18651        
18652         //lbl.cn.push(inputblock);
18653         
18654         var lblwrap =  {
18655             tag: 'span',
18656             cls: 'radio' + inline,
18657             cn: [
18658                 inputblock,
18659                 lbl
18660             ]
18661         };
18662         
18663         cfg.cn.push( lblwrap);
18664         
18665         if(this.boxLabel){
18666             lbl.cn.push({
18667                 tag: 'span',
18668                 html: this.boxLabel
18669             })
18670         }
18671          
18672         
18673         return cfg;
18674         
18675     },
18676     
18677     initEvents : function()
18678     {
18679 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18680         
18681         this.inputEl().on('click', this.onClick,  this);
18682         if (this.boxLabel) {
18683             Roo.log('find label')
18684             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18685         }
18686         
18687     },
18688     
18689     inputEl: function ()
18690     {
18691         return this.el.select('input.roo-radio',true).first();
18692     },
18693     onClick : function()
18694     {   
18695         Roo.log("click");
18696         this.setChecked(true);
18697     },
18698     
18699     setChecked : function(state,suppressEvent)
18700     {
18701         if(state){
18702             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18703                 v.dom.checked = false;
18704             });
18705         }
18706         Roo.log(this.inputEl().dom);
18707         this.checked = state;
18708         this.inputEl().dom.checked = state;
18709         
18710         if(suppressEvent !== true){
18711             this.fireEvent('check', this, state);
18712         }
18713         
18714         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18715         
18716     },
18717     
18718     getGroupValue : function()
18719     {
18720         var value = '';
18721         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18722             if(v.dom.checked == true){
18723                 value = v.dom.value;
18724             }
18725         });
18726         
18727         return value;
18728     },
18729     
18730     /**
18731      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18732      * @return {Mixed} value The field value
18733      */
18734     getValue : function(){
18735         return this.getGroupValue();
18736     }
18737     
18738 });
18739
18740  
18741 //<script type="text/javascript">
18742
18743 /*
18744  * Based  Ext JS Library 1.1.1
18745  * Copyright(c) 2006-2007, Ext JS, LLC.
18746  * LGPL
18747  *
18748  */
18749  
18750 /**
18751  * @class Roo.HtmlEditorCore
18752  * @extends Roo.Component
18753  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18754  *
18755  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18756  */
18757
18758 Roo.HtmlEditorCore = function(config){
18759     
18760     
18761     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18762     
18763     
18764     this.addEvents({
18765         /**
18766          * @event initialize
18767          * Fires when the editor is fully initialized (including the iframe)
18768          * @param {Roo.HtmlEditorCore} this
18769          */
18770         initialize: true,
18771         /**
18772          * @event activate
18773          * Fires when the editor is first receives the focus. Any insertion must wait
18774          * until after this event.
18775          * @param {Roo.HtmlEditorCore} this
18776          */
18777         activate: true,
18778          /**
18779          * @event beforesync
18780          * Fires before the textarea is updated with content from the editor iframe. Return false
18781          * to cancel the sync.
18782          * @param {Roo.HtmlEditorCore} this
18783          * @param {String} html
18784          */
18785         beforesync: true,
18786          /**
18787          * @event beforepush
18788          * Fires before the iframe editor is updated with content from the textarea. Return false
18789          * to cancel the push.
18790          * @param {Roo.HtmlEditorCore} this
18791          * @param {String} html
18792          */
18793         beforepush: true,
18794          /**
18795          * @event sync
18796          * Fires when the textarea is updated with content from the editor iframe.
18797          * @param {Roo.HtmlEditorCore} this
18798          * @param {String} html
18799          */
18800         sync: true,
18801          /**
18802          * @event push
18803          * Fires when the iframe editor is updated with content from the textarea.
18804          * @param {Roo.HtmlEditorCore} this
18805          * @param {String} html
18806          */
18807         push: true,
18808         
18809         /**
18810          * @event editorevent
18811          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18812          * @param {Roo.HtmlEditorCore} this
18813          */
18814         editorevent: true
18815         
18816     });
18817     
18818     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18819     
18820     // defaults : white / black...
18821     this.applyBlacklists();
18822     
18823     
18824     
18825 };
18826
18827
18828 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18829
18830
18831      /**
18832      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18833      */
18834     
18835     owner : false,
18836     
18837      /**
18838      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18839      *                        Roo.resizable.
18840      */
18841     resizable : false,
18842      /**
18843      * @cfg {Number} height (in pixels)
18844      */   
18845     height: 300,
18846    /**
18847      * @cfg {Number} width (in pixels)
18848      */   
18849     width: 500,
18850     
18851     /**
18852      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18853      * 
18854      */
18855     stylesheets: false,
18856     
18857     // id of frame..
18858     frameId: false,
18859     
18860     // private properties
18861     validationEvent : false,
18862     deferHeight: true,
18863     initialized : false,
18864     activated : false,
18865     sourceEditMode : false,
18866     onFocus : Roo.emptyFn,
18867     iframePad:3,
18868     hideMode:'offsets',
18869     
18870     clearUp: true,
18871     
18872     // blacklist + whitelisted elements..
18873     black: false,
18874     white: false,
18875      
18876     
18877
18878     /**
18879      * Protected method that will not generally be called directly. It
18880      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18881      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18882      */
18883     getDocMarkup : function(){
18884         // body styles..
18885         var st = '';
18886         
18887         // inherit styels from page...?? 
18888         if (this.stylesheets === false) {
18889             
18890             Roo.get(document.head).select('style').each(function(node) {
18891                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18892             });
18893             
18894             Roo.get(document.head).select('link').each(function(node) { 
18895                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18896             });
18897             
18898         } else if (!this.stylesheets.length) {
18899                 // simple..
18900                 st = '<style type="text/css">' +
18901                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18902                    '</style>';
18903         } else { 
18904             
18905         }
18906         
18907         st +=  '<style type="text/css">' +
18908             'IMG { cursor: pointer } ' +
18909         '</style>';
18910
18911         
18912         return '<html><head>' + st  +
18913             //<style type="text/css">' +
18914             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18915             //'</style>' +
18916             ' </head><body class="roo-htmleditor-body"></body></html>';
18917     },
18918
18919     // private
18920     onRender : function(ct, position)
18921     {
18922         var _t = this;
18923         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18924         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18925         
18926         
18927         this.el.dom.style.border = '0 none';
18928         this.el.dom.setAttribute('tabIndex', -1);
18929         this.el.addClass('x-hidden hide');
18930         
18931         
18932         
18933         if(Roo.isIE){ // fix IE 1px bogus margin
18934             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18935         }
18936        
18937         
18938         this.frameId = Roo.id();
18939         
18940          
18941         
18942         var iframe = this.owner.wrap.createChild({
18943             tag: 'iframe',
18944             cls: 'form-control', // bootstrap..
18945             id: this.frameId,
18946             name: this.frameId,
18947             frameBorder : 'no',
18948             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18949         }, this.el
18950         );
18951         
18952         
18953         this.iframe = iframe.dom;
18954
18955          this.assignDocWin();
18956         
18957         this.doc.designMode = 'on';
18958        
18959         this.doc.open();
18960         this.doc.write(this.getDocMarkup());
18961         this.doc.close();
18962
18963         
18964         var task = { // must defer to wait for browser to be ready
18965             run : function(){
18966                 //console.log("run task?" + this.doc.readyState);
18967                 this.assignDocWin();
18968                 if(this.doc.body || this.doc.readyState == 'complete'){
18969                     try {
18970                         this.doc.designMode="on";
18971                     } catch (e) {
18972                         return;
18973                     }
18974                     Roo.TaskMgr.stop(task);
18975                     this.initEditor.defer(10, this);
18976                 }
18977             },
18978             interval : 10,
18979             duration: 10000,
18980             scope: this
18981         };
18982         Roo.TaskMgr.start(task);
18983
18984     },
18985
18986     // private
18987     onResize : function(w, h)
18988     {
18989          Roo.log('resize: ' +w + ',' + h );
18990         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18991         if(!this.iframe){
18992             return;
18993         }
18994         if(typeof w == 'number'){
18995             
18996             this.iframe.style.width = w + 'px';
18997         }
18998         if(typeof h == 'number'){
18999             
19000             this.iframe.style.height = h + 'px';
19001             if(this.doc){
19002                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19003             }
19004         }
19005         
19006     },
19007
19008     /**
19009      * Toggles the editor between standard and source edit mode.
19010      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19011      */
19012     toggleSourceEdit : function(sourceEditMode){
19013         
19014         this.sourceEditMode = sourceEditMode === true;
19015         
19016         if(this.sourceEditMode){
19017  
19018             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19019             
19020         }else{
19021             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19022             //this.iframe.className = '';
19023             this.deferFocus();
19024         }
19025         //this.setSize(this.owner.wrap.getSize());
19026         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19027     },
19028
19029     
19030   
19031
19032     /**
19033      * Protected method that will not generally be called directly. If you need/want
19034      * custom HTML cleanup, this is the method you should override.
19035      * @param {String} html The HTML to be cleaned
19036      * return {String} The cleaned HTML
19037      */
19038     cleanHtml : function(html){
19039         html = String(html);
19040         if(html.length > 5){
19041             if(Roo.isSafari){ // strip safari nonsense
19042                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19043             }
19044         }
19045         if(html == '&nbsp;'){
19046             html = '';
19047         }
19048         return html;
19049     },
19050
19051     /**
19052      * HTML Editor -> Textarea
19053      * Protected method that will not generally be called directly. Syncs the contents
19054      * of the editor iframe with the textarea.
19055      */
19056     syncValue : function(){
19057         if(this.initialized){
19058             var bd = (this.doc.body || this.doc.documentElement);
19059             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19060             var html = bd.innerHTML;
19061             if(Roo.isSafari){
19062                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19063                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19064                 if(m && m[1]){
19065                     html = '<div style="'+m[0]+'">' + html + '</div>';
19066                 }
19067             }
19068             html = this.cleanHtml(html);
19069             // fix up the special chars.. normaly like back quotes in word...
19070             // however we do not want to do this with chinese..
19071             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19072                 var cc = b.charCodeAt();
19073                 if (
19074                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19075                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19076                     (cc >= 0xf900 && cc < 0xfb00 )
19077                 ) {
19078                         return b;
19079                 }
19080                 return "&#"+cc+";" 
19081             });
19082             if(this.owner.fireEvent('beforesync', this, html) !== false){
19083                 this.el.dom.value = html;
19084                 this.owner.fireEvent('sync', this, html);
19085             }
19086         }
19087     },
19088
19089     /**
19090      * Protected method that will not generally be called directly. Pushes the value of the textarea
19091      * into the iframe editor.
19092      */
19093     pushValue : function(){
19094         if(this.initialized){
19095             var v = this.el.dom.value.trim();
19096             
19097 //            if(v.length < 1){
19098 //                v = '&#160;';
19099 //            }
19100             
19101             if(this.owner.fireEvent('beforepush', this, v) !== false){
19102                 var d = (this.doc.body || this.doc.documentElement);
19103                 d.innerHTML = v;
19104                 this.cleanUpPaste();
19105                 this.el.dom.value = d.innerHTML;
19106                 this.owner.fireEvent('push', this, v);
19107             }
19108         }
19109     },
19110
19111     // private
19112     deferFocus : function(){
19113         this.focus.defer(10, this);
19114     },
19115
19116     // doc'ed in Field
19117     focus : function(){
19118         if(this.win && !this.sourceEditMode){
19119             this.win.focus();
19120         }else{
19121             this.el.focus();
19122         }
19123     },
19124     
19125     assignDocWin: function()
19126     {
19127         var iframe = this.iframe;
19128         
19129          if(Roo.isIE){
19130             this.doc = iframe.contentWindow.document;
19131             this.win = iframe.contentWindow;
19132         } else {
19133 //            if (!Roo.get(this.frameId)) {
19134 //                return;
19135 //            }
19136 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19137 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19138             
19139             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19140                 return;
19141             }
19142             
19143             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19144             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19145         }
19146     },
19147     
19148     // private
19149     initEditor : function(){
19150         //console.log("INIT EDITOR");
19151         this.assignDocWin();
19152         
19153         
19154         
19155         this.doc.designMode="on";
19156         this.doc.open();
19157         this.doc.write(this.getDocMarkup());
19158         this.doc.close();
19159         
19160         var dbody = (this.doc.body || this.doc.documentElement);
19161         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19162         // this copies styles from the containing element into thsi one..
19163         // not sure why we need all of this..
19164         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19165         
19166         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19167         //ss['background-attachment'] = 'fixed'; // w3c
19168         dbody.bgProperties = 'fixed'; // ie
19169         //Roo.DomHelper.applyStyles(dbody, ss);
19170         Roo.EventManager.on(this.doc, {
19171             //'mousedown': this.onEditorEvent,
19172             'mouseup': this.onEditorEvent,
19173             'dblclick': this.onEditorEvent,
19174             'click': this.onEditorEvent,
19175             'keyup': this.onEditorEvent,
19176             buffer:100,
19177             scope: this
19178         });
19179         if(Roo.isGecko){
19180             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19181         }
19182         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19183             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19184         }
19185         this.initialized = true;
19186
19187         this.owner.fireEvent('initialize', this);
19188         this.pushValue();
19189     },
19190
19191     // private
19192     onDestroy : function(){
19193         
19194         
19195         
19196         if(this.rendered){
19197             
19198             //for (var i =0; i < this.toolbars.length;i++) {
19199             //    // fixme - ask toolbars for heights?
19200             //    this.toolbars[i].onDestroy();
19201            // }
19202             
19203             //this.wrap.dom.innerHTML = '';
19204             //this.wrap.remove();
19205         }
19206     },
19207
19208     // private
19209     onFirstFocus : function(){
19210         
19211         this.assignDocWin();
19212         
19213         
19214         this.activated = true;
19215          
19216     
19217         if(Roo.isGecko){ // prevent silly gecko errors
19218             this.win.focus();
19219             var s = this.win.getSelection();
19220             if(!s.focusNode || s.focusNode.nodeType != 3){
19221                 var r = s.getRangeAt(0);
19222                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19223                 r.collapse(true);
19224                 this.deferFocus();
19225             }
19226             try{
19227                 this.execCmd('useCSS', true);
19228                 this.execCmd('styleWithCSS', false);
19229             }catch(e){}
19230         }
19231         this.owner.fireEvent('activate', this);
19232     },
19233
19234     // private
19235     adjustFont: function(btn){
19236         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19237         //if(Roo.isSafari){ // safari
19238         //    adjust *= 2;
19239        // }
19240         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19241         if(Roo.isSafari){ // safari
19242             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19243             v =  (v < 10) ? 10 : v;
19244             v =  (v > 48) ? 48 : v;
19245             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19246             
19247         }
19248         
19249         
19250         v = Math.max(1, v+adjust);
19251         
19252         this.execCmd('FontSize', v  );
19253     },
19254
19255     onEditorEvent : function(e)
19256     {
19257         this.owner.fireEvent('editorevent', this, e);
19258       //  this.updateToolbar();
19259         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19260     },
19261
19262     insertTag : function(tg)
19263     {
19264         // could be a bit smarter... -> wrap the current selected tRoo..
19265         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19266             
19267             range = this.createRange(this.getSelection());
19268             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19269             wrappingNode.appendChild(range.extractContents());
19270             range.insertNode(wrappingNode);
19271
19272             return;
19273             
19274             
19275             
19276         }
19277         this.execCmd("formatblock",   tg);
19278         
19279     },
19280     
19281     insertText : function(txt)
19282     {
19283         
19284         
19285         var range = this.createRange();
19286         range.deleteContents();
19287                //alert(Sender.getAttribute('label'));
19288                
19289         range.insertNode(this.doc.createTextNode(txt));
19290     } ,
19291     
19292      
19293
19294     /**
19295      * Executes a Midas editor command on the editor document and performs necessary focus and
19296      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19297      * @param {String} cmd The Midas command
19298      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19299      */
19300     relayCmd : function(cmd, value){
19301         this.win.focus();
19302         this.execCmd(cmd, value);
19303         this.owner.fireEvent('editorevent', this);
19304         //this.updateToolbar();
19305         this.owner.deferFocus();
19306     },
19307
19308     /**
19309      * Executes a Midas editor command directly on the editor document.
19310      * For visual commands, you should use {@link #relayCmd} instead.
19311      * <b>This should only be called after the editor is initialized.</b>
19312      * @param {String} cmd The Midas command
19313      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19314      */
19315     execCmd : function(cmd, value){
19316         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19317         this.syncValue();
19318     },
19319  
19320  
19321    
19322     /**
19323      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19324      * to insert tRoo.
19325      * @param {String} text | dom node.. 
19326      */
19327     insertAtCursor : function(text)
19328     {
19329         
19330         
19331         
19332         if(!this.activated){
19333             return;
19334         }
19335         /*
19336         if(Roo.isIE){
19337             this.win.focus();
19338             var r = this.doc.selection.createRange();
19339             if(r){
19340                 r.collapse(true);
19341                 r.pasteHTML(text);
19342                 this.syncValue();
19343                 this.deferFocus();
19344             
19345             }
19346             return;
19347         }
19348         */
19349         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19350             this.win.focus();
19351             
19352             
19353             // from jquery ui (MIT licenced)
19354             var range, node;
19355             var win = this.win;
19356             
19357             if (win.getSelection && win.getSelection().getRangeAt) {
19358                 range = win.getSelection().getRangeAt(0);
19359                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19360                 range.insertNode(node);
19361             } else if (win.document.selection && win.document.selection.createRange) {
19362                 // no firefox support
19363                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19364                 win.document.selection.createRange().pasteHTML(txt);
19365             } else {
19366                 // no firefox support
19367                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19368                 this.execCmd('InsertHTML', txt);
19369             } 
19370             
19371             this.syncValue();
19372             
19373             this.deferFocus();
19374         }
19375     },
19376  // private
19377     mozKeyPress : function(e){
19378         if(e.ctrlKey){
19379             var c = e.getCharCode(), cmd;
19380           
19381             if(c > 0){
19382                 c = String.fromCharCode(c).toLowerCase();
19383                 switch(c){
19384                     case 'b':
19385                         cmd = 'bold';
19386                         break;
19387                     case 'i':
19388                         cmd = 'italic';
19389                         break;
19390                     
19391                     case 'u':
19392                         cmd = 'underline';
19393                         break;
19394                     
19395                     case 'v':
19396                         this.cleanUpPaste.defer(100, this);
19397                         return;
19398                         
19399                 }
19400                 if(cmd){
19401                     this.win.focus();
19402                     this.execCmd(cmd);
19403                     this.deferFocus();
19404                     e.preventDefault();
19405                 }
19406                 
19407             }
19408         }
19409     },
19410
19411     // private
19412     fixKeys : function(){ // load time branching for fastest keydown performance
19413         if(Roo.isIE){
19414             return function(e){
19415                 var k = e.getKey(), r;
19416                 if(k == e.TAB){
19417                     e.stopEvent();
19418                     r = this.doc.selection.createRange();
19419                     if(r){
19420                         r.collapse(true);
19421                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19422                         this.deferFocus();
19423                     }
19424                     return;
19425                 }
19426                 
19427                 if(k == e.ENTER){
19428                     r = this.doc.selection.createRange();
19429                     if(r){
19430                         var target = r.parentElement();
19431                         if(!target || target.tagName.toLowerCase() != 'li'){
19432                             e.stopEvent();
19433                             r.pasteHTML('<br />');
19434                             r.collapse(false);
19435                             r.select();
19436                         }
19437                     }
19438                 }
19439                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19440                     this.cleanUpPaste.defer(100, this);
19441                     return;
19442                 }
19443                 
19444                 
19445             };
19446         }else if(Roo.isOpera){
19447             return function(e){
19448                 var k = e.getKey();
19449                 if(k == e.TAB){
19450                     e.stopEvent();
19451                     this.win.focus();
19452                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19453                     this.deferFocus();
19454                 }
19455                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19456                     this.cleanUpPaste.defer(100, this);
19457                     return;
19458                 }
19459                 
19460             };
19461         }else if(Roo.isSafari){
19462             return function(e){
19463                 var k = e.getKey();
19464                 
19465                 if(k == e.TAB){
19466                     e.stopEvent();
19467                     this.execCmd('InsertText','\t');
19468                     this.deferFocus();
19469                     return;
19470                 }
19471                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19472                     this.cleanUpPaste.defer(100, this);
19473                     return;
19474                 }
19475                 
19476              };
19477         }
19478     }(),
19479     
19480     getAllAncestors: function()
19481     {
19482         var p = this.getSelectedNode();
19483         var a = [];
19484         if (!p) {
19485             a.push(p); // push blank onto stack..
19486             p = this.getParentElement();
19487         }
19488         
19489         
19490         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19491             a.push(p);
19492             p = p.parentNode;
19493         }
19494         a.push(this.doc.body);
19495         return a;
19496     },
19497     lastSel : false,
19498     lastSelNode : false,
19499     
19500     
19501     getSelection : function() 
19502     {
19503         this.assignDocWin();
19504         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19505     },
19506     
19507     getSelectedNode: function() 
19508     {
19509         // this may only work on Gecko!!!
19510         
19511         // should we cache this!!!!
19512         
19513         
19514         
19515          
19516         var range = this.createRange(this.getSelection()).cloneRange();
19517         
19518         if (Roo.isIE) {
19519             var parent = range.parentElement();
19520             while (true) {
19521                 var testRange = range.duplicate();
19522                 testRange.moveToElementText(parent);
19523                 if (testRange.inRange(range)) {
19524                     break;
19525                 }
19526                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19527                     break;
19528                 }
19529                 parent = parent.parentElement;
19530             }
19531             return parent;
19532         }
19533         
19534         // is ancestor a text element.
19535         var ac =  range.commonAncestorContainer;
19536         if (ac.nodeType == 3) {
19537             ac = ac.parentNode;
19538         }
19539         
19540         var ar = ac.childNodes;
19541          
19542         var nodes = [];
19543         var other_nodes = [];
19544         var has_other_nodes = false;
19545         for (var i=0;i<ar.length;i++) {
19546             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19547                 continue;
19548             }
19549             // fullly contained node.
19550             
19551             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19552                 nodes.push(ar[i]);
19553                 continue;
19554             }
19555             
19556             // probably selected..
19557             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19558                 other_nodes.push(ar[i]);
19559                 continue;
19560             }
19561             // outer..
19562             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19563                 continue;
19564             }
19565             
19566             
19567             has_other_nodes = true;
19568         }
19569         if (!nodes.length && other_nodes.length) {
19570             nodes= other_nodes;
19571         }
19572         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19573             return false;
19574         }
19575         
19576         return nodes[0];
19577     },
19578     createRange: function(sel)
19579     {
19580         // this has strange effects when using with 
19581         // top toolbar - not sure if it's a great idea.
19582         //this.editor.contentWindow.focus();
19583         if (typeof sel != "undefined") {
19584             try {
19585                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19586             } catch(e) {
19587                 return this.doc.createRange();
19588             }
19589         } else {
19590             return this.doc.createRange();
19591         }
19592     },
19593     getParentElement: function()
19594     {
19595         
19596         this.assignDocWin();
19597         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19598         
19599         var range = this.createRange(sel);
19600          
19601         try {
19602             var p = range.commonAncestorContainer;
19603             while (p.nodeType == 3) { // text node
19604                 p = p.parentNode;
19605             }
19606             return p;
19607         } catch (e) {
19608             return null;
19609         }
19610     
19611     },
19612     /***
19613      *
19614      * Range intersection.. the hard stuff...
19615      *  '-1' = before
19616      *  '0' = hits..
19617      *  '1' = after.
19618      *         [ -- selected range --- ]
19619      *   [fail]                        [fail]
19620      *
19621      *    basically..
19622      *      if end is before start or  hits it. fail.
19623      *      if start is after end or hits it fail.
19624      *
19625      *   if either hits (but other is outside. - then it's not 
19626      *   
19627      *    
19628      **/
19629     
19630     
19631     // @see http://www.thismuchiknow.co.uk/?p=64.
19632     rangeIntersectsNode : function(range, node)
19633     {
19634         var nodeRange = node.ownerDocument.createRange();
19635         try {
19636             nodeRange.selectNode(node);
19637         } catch (e) {
19638             nodeRange.selectNodeContents(node);
19639         }
19640     
19641         var rangeStartRange = range.cloneRange();
19642         rangeStartRange.collapse(true);
19643     
19644         var rangeEndRange = range.cloneRange();
19645         rangeEndRange.collapse(false);
19646     
19647         var nodeStartRange = nodeRange.cloneRange();
19648         nodeStartRange.collapse(true);
19649     
19650         var nodeEndRange = nodeRange.cloneRange();
19651         nodeEndRange.collapse(false);
19652     
19653         return rangeStartRange.compareBoundaryPoints(
19654                  Range.START_TO_START, nodeEndRange) == -1 &&
19655                rangeEndRange.compareBoundaryPoints(
19656                  Range.START_TO_START, nodeStartRange) == 1;
19657         
19658          
19659     },
19660     rangeCompareNode : function(range, node)
19661     {
19662         var nodeRange = node.ownerDocument.createRange();
19663         try {
19664             nodeRange.selectNode(node);
19665         } catch (e) {
19666             nodeRange.selectNodeContents(node);
19667         }
19668         
19669         
19670         range.collapse(true);
19671     
19672         nodeRange.collapse(true);
19673      
19674         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19675         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19676          
19677         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19678         
19679         var nodeIsBefore   =  ss == 1;
19680         var nodeIsAfter    = ee == -1;
19681         
19682         if (nodeIsBefore && nodeIsAfter)
19683             return 0; // outer
19684         if (!nodeIsBefore && nodeIsAfter)
19685             return 1; //right trailed.
19686         
19687         if (nodeIsBefore && !nodeIsAfter)
19688             return 2;  // left trailed.
19689         // fully contined.
19690         return 3;
19691     },
19692
19693     // private? - in a new class?
19694     cleanUpPaste :  function()
19695     {
19696         // cleans up the whole document..
19697         Roo.log('cleanuppaste');
19698         
19699         this.cleanUpChildren(this.doc.body);
19700         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19701         if (clean != this.doc.body.innerHTML) {
19702             this.doc.body.innerHTML = clean;
19703         }
19704         
19705     },
19706     
19707     cleanWordChars : function(input) {// change the chars to hex code
19708         var he = Roo.HtmlEditorCore;
19709         
19710         var output = input;
19711         Roo.each(he.swapCodes, function(sw) { 
19712             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19713             
19714             output = output.replace(swapper, sw[1]);
19715         });
19716         
19717         return output;
19718     },
19719     
19720     
19721     cleanUpChildren : function (n)
19722     {
19723         if (!n.childNodes.length) {
19724             return;
19725         }
19726         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19727            this.cleanUpChild(n.childNodes[i]);
19728         }
19729     },
19730     
19731     
19732         
19733     
19734     cleanUpChild : function (node)
19735     {
19736         var ed = this;
19737         //console.log(node);
19738         if (node.nodeName == "#text") {
19739             // clean up silly Windows -- stuff?
19740             return; 
19741         }
19742         if (node.nodeName == "#comment") {
19743             node.parentNode.removeChild(node);
19744             // clean up silly Windows -- stuff?
19745             return; 
19746         }
19747         var lcname = node.tagName.toLowerCase();
19748         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19749         // whitelist of tags..
19750         
19751         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19752             // remove node.
19753             node.parentNode.removeChild(node);
19754             return;
19755             
19756         }
19757         
19758         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19759         
19760         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19761         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19762         
19763         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19764         //    remove_keep_children = true;
19765         //}
19766         
19767         if (remove_keep_children) {
19768             this.cleanUpChildren(node);
19769             // inserts everything just before this node...
19770             while (node.childNodes.length) {
19771                 var cn = node.childNodes[0];
19772                 node.removeChild(cn);
19773                 node.parentNode.insertBefore(cn, node);
19774             }
19775             node.parentNode.removeChild(node);
19776             return;
19777         }
19778         
19779         if (!node.attributes || !node.attributes.length) {
19780             this.cleanUpChildren(node);
19781             return;
19782         }
19783         
19784         function cleanAttr(n,v)
19785         {
19786             
19787             if (v.match(/^\./) || v.match(/^\//)) {
19788                 return;
19789             }
19790             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19791                 return;
19792             }
19793             if (v.match(/^#/)) {
19794                 return;
19795             }
19796 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19797             node.removeAttribute(n);
19798             
19799         }
19800         
19801         var cwhite = this.cwhite;
19802         var cblack = this.cblack;
19803             
19804         function cleanStyle(n,v)
19805         {
19806             if (v.match(/expression/)) { //XSS?? should we even bother..
19807                 node.removeAttribute(n);
19808                 return;
19809             }
19810             
19811             var parts = v.split(/;/);
19812             var clean = [];
19813             
19814             Roo.each(parts, function(p) {
19815                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19816                 if (!p.length) {
19817                     return true;
19818                 }
19819                 var l = p.split(':').shift().replace(/\s+/g,'');
19820                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19821                 
19822                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19823 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19824                     //node.removeAttribute(n);
19825                     return true;
19826                 }
19827                 //Roo.log()
19828                 // only allow 'c whitelisted system attributes'
19829                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19830 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19831                     //node.removeAttribute(n);
19832                     return true;
19833                 }
19834                 
19835                 
19836                  
19837                 
19838                 clean.push(p);
19839                 return true;
19840             });
19841             if (clean.length) { 
19842                 node.setAttribute(n, clean.join(';'));
19843             } else {
19844                 node.removeAttribute(n);
19845             }
19846             
19847         }
19848         
19849         
19850         for (var i = node.attributes.length-1; i > -1 ; i--) {
19851             var a = node.attributes[i];
19852             //console.log(a);
19853             
19854             if (a.name.toLowerCase().substr(0,2)=='on')  {
19855                 node.removeAttribute(a.name);
19856                 continue;
19857             }
19858             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19859                 node.removeAttribute(a.name);
19860                 continue;
19861             }
19862             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19863                 cleanAttr(a.name,a.value); // fixme..
19864                 continue;
19865             }
19866             if (a.name == 'style') {
19867                 cleanStyle(a.name,a.value);
19868                 continue;
19869             }
19870             /// clean up MS crap..
19871             // tecnically this should be a list of valid class'es..
19872             
19873             
19874             if (a.name == 'class') {
19875                 if (a.value.match(/^Mso/)) {
19876                     node.className = '';
19877                 }
19878                 
19879                 if (a.value.match(/body/)) {
19880                     node.className = '';
19881                 }
19882                 continue;
19883             }
19884             
19885             // style cleanup!?
19886             // class cleanup?
19887             
19888         }
19889         
19890         
19891         this.cleanUpChildren(node);
19892         
19893         
19894     },
19895     
19896     /**
19897      * Clean up MS wordisms...
19898      */
19899     cleanWord : function(node)
19900     {
19901         
19902         
19903         if (!node) {
19904             this.cleanWord(this.doc.body);
19905             return;
19906         }
19907         if (node.nodeName == "#text") {
19908             // clean up silly Windows -- stuff?
19909             return; 
19910         }
19911         if (node.nodeName == "#comment") {
19912             node.parentNode.removeChild(node);
19913             // clean up silly Windows -- stuff?
19914             return; 
19915         }
19916         
19917         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19918             node.parentNode.removeChild(node);
19919             return;
19920         }
19921         
19922         // remove - but keep children..
19923         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19924             while (node.childNodes.length) {
19925                 var cn = node.childNodes[0];
19926                 node.removeChild(cn);
19927                 node.parentNode.insertBefore(cn, node);
19928             }
19929             node.parentNode.removeChild(node);
19930             this.iterateChildren(node, this.cleanWord);
19931             return;
19932         }
19933         // clean styles
19934         if (node.className.length) {
19935             
19936             var cn = node.className.split(/\W+/);
19937             var cna = [];
19938             Roo.each(cn, function(cls) {
19939                 if (cls.match(/Mso[a-zA-Z]+/)) {
19940                     return;
19941                 }
19942                 cna.push(cls);
19943             });
19944             node.className = cna.length ? cna.join(' ') : '';
19945             if (!cna.length) {
19946                 node.removeAttribute("class");
19947             }
19948         }
19949         
19950         if (node.hasAttribute("lang")) {
19951             node.removeAttribute("lang");
19952         }
19953         
19954         if (node.hasAttribute("style")) {
19955             
19956             var styles = node.getAttribute("style").split(";");
19957             var nstyle = [];
19958             Roo.each(styles, function(s) {
19959                 if (!s.match(/:/)) {
19960                     return;
19961                 }
19962                 var kv = s.split(":");
19963                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19964                     return;
19965                 }
19966                 // what ever is left... we allow.
19967                 nstyle.push(s);
19968             });
19969             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19970             if (!nstyle.length) {
19971                 node.removeAttribute('style');
19972             }
19973         }
19974         this.iterateChildren(node, this.cleanWord);
19975         
19976         
19977         
19978     },
19979     /**
19980      * iterateChildren of a Node, calling fn each time, using this as the scole..
19981      * @param {DomNode} node node to iterate children of.
19982      * @param {Function} fn method of this class to call on each item.
19983      */
19984     iterateChildren : function(node, fn)
19985     {
19986         if (!node.childNodes.length) {
19987                 return;
19988         }
19989         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19990            fn.call(this, node.childNodes[i])
19991         }
19992     },
19993     
19994     
19995     /**
19996      * cleanTableWidths.
19997      *
19998      * Quite often pasting from word etc.. results in tables with column and widths.
19999      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20000      *
20001      */
20002     cleanTableWidths : function(node)
20003     {
20004          
20005          
20006         if (!node) {
20007             this.cleanTableWidths(this.doc.body);
20008             return;
20009         }
20010         
20011         // ignore list...
20012         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20013             return; 
20014         }
20015         Roo.log(node.tagName);
20016         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20017             this.iterateChildren(node, this.cleanTableWidths);
20018             return;
20019         }
20020         if (node.hasAttribute('width')) {
20021             node.removeAttribute('width');
20022         }
20023         
20024          
20025         if (node.hasAttribute("style")) {
20026             // pretty basic...
20027             
20028             var styles = node.getAttribute("style").split(";");
20029             var nstyle = [];
20030             Roo.each(styles, function(s) {
20031                 if (!s.match(/:/)) {
20032                     return;
20033                 }
20034                 var kv = s.split(":");
20035                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20036                     return;
20037                 }
20038                 // what ever is left... we allow.
20039                 nstyle.push(s);
20040             });
20041             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20042             if (!nstyle.length) {
20043                 node.removeAttribute('style');
20044             }
20045         }
20046         
20047         this.iterateChildren(node, this.cleanTableWidths);
20048         
20049         
20050     },
20051     
20052     
20053     
20054     
20055     domToHTML : function(currentElement, depth, nopadtext) {
20056         
20057         depth = depth || 0;
20058         nopadtext = nopadtext || false;
20059     
20060         if (!currentElement) {
20061             return this.domToHTML(this.doc.body);
20062         }
20063         
20064         //Roo.log(currentElement);
20065         var j;
20066         var allText = false;
20067         var nodeName = currentElement.nodeName;
20068         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20069         
20070         if  (nodeName == '#text') {
20071             
20072             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20073         }
20074         
20075         
20076         var ret = '';
20077         if (nodeName != 'BODY') {
20078              
20079             var i = 0;
20080             // Prints the node tagName, such as <A>, <IMG>, etc
20081             if (tagName) {
20082                 var attr = [];
20083                 for(i = 0; i < currentElement.attributes.length;i++) {
20084                     // quoting?
20085                     var aname = currentElement.attributes.item(i).name;
20086                     if (!currentElement.attributes.item(i).value.length) {
20087                         continue;
20088                     }
20089                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20090                 }
20091                 
20092                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20093             } 
20094             else {
20095                 
20096                 // eack
20097             }
20098         } else {
20099             tagName = false;
20100         }
20101         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20102             return ret;
20103         }
20104         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20105             nopadtext = true;
20106         }
20107         
20108         
20109         // Traverse the tree
20110         i = 0;
20111         var currentElementChild = currentElement.childNodes.item(i);
20112         var allText = true;
20113         var innerHTML  = '';
20114         lastnode = '';
20115         while (currentElementChild) {
20116             // Formatting code (indent the tree so it looks nice on the screen)
20117             var nopad = nopadtext;
20118             if (lastnode == 'SPAN') {
20119                 nopad  = true;
20120             }
20121             // text
20122             if  (currentElementChild.nodeName == '#text') {
20123                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20124                 toadd = nopadtext ? toadd : toadd.trim();
20125                 if (!nopad && toadd.length > 80) {
20126                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20127                 }
20128                 innerHTML  += toadd;
20129                 
20130                 i++;
20131                 currentElementChild = currentElement.childNodes.item(i);
20132                 lastNode = '';
20133                 continue;
20134             }
20135             allText = false;
20136             
20137             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20138                 
20139             // Recursively traverse the tree structure of the child node
20140             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20141             lastnode = currentElementChild.nodeName;
20142             i++;
20143             currentElementChild=currentElement.childNodes.item(i);
20144         }
20145         
20146         ret += innerHTML;
20147         
20148         if (!allText) {
20149                 // The remaining code is mostly for formatting the tree
20150             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20151         }
20152         
20153         
20154         if (tagName) {
20155             ret+= "</"+tagName+">";
20156         }
20157         return ret;
20158         
20159     },
20160         
20161     applyBlacklists : function()
20162     {
20163         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20164         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20165         
20166         this.white = [];
20167         this.black = [];
20168         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20169             if (b.indexOf(tag) > -1) {
20170                 return;
20171             }
20172             this.white.push(tag);
20173             
20174         }, this);
20175         
20176         Roo.each(w, function(tag) {
20177             if (b.indexOf(tag) > -1) {
20178                 return;
20179             }
20180             if (this.white.indexOf(tag) > -1) {
20181                 return;
20182             }
20183             this.white.push(tag);
20184             
20185         }, this);
20186         
20187         
20188         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20189             if (w.indexOf(tag) > -1) {
20190                 return;
20191             }
20192             this.black.push(tag);
20193             
20194         }, this);
20195         
20196         Roo.each(b, function(tag) {
20197             if (w.indexOf(tag) > -1) {
20198                 return;
20199             }
20200             if (this.black.indexOf(tag) > -1) {
20201                 return;
20202             }
20203             this.black.push(tag);
20204             
20205         }, this);
20206         
20207         
20208         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20209         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20210         
20211         this.cwhite = [];
20212         this.cblack = [];
20213         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20214             if (b.indexOf(tag) > -1) {
20215                 return;
20216             }
20217             this.cwhite.push(tag);
20218             
20219         }, this);
20220         
20221         Roo.each(w, function(tag) {
20222             if (b.indexOf(tag) > -1) {
20223                 return;
20224             }
20225             if (this.cwhite.indexOf(tag) > -1) {
20226                 return;
20227             }
20228             this.cwhite.push(tag);
20229             
20230         }, this);
20231         
20232         
20233         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20234             if (w.indexOf(tag) > -1) {
20235                 return;
20236             }
20237             this.cblack.push(tag);
20238             
20239         }, this);
20240         
20241         Roo.each(b, function(tag) {
20242             if (w.indexOf(tag) > -1) {
20243                 return;
20244             }
20245             if (this.cblack.indexOf(tag) > -1) {
20246                 return;
20247             }
20248             this.cblack.push(tag);
20249             
20250         }, this);
20251     },
20252     
20253     setStylesheets : function(stylesheets)
20254     {
20255         if(typeof(stylesheets) == 'string'){
20256             Roo.get(this.iframe.contentDocument.head).createChild({
20257                 tag : 'link',
20258                 rel : 'stylesheet',
20259                 type : 'text/css',
20260                 href : stylesheets
20261             });
20262             
20263             return;
20264         }
20265         var _this = this;
20266      
20267         Roo.each(stylesheets, function(s) {
20268             if(!s.length){
20269                 return;
20270             }
20271             
20272             Roo.get(_this.iframe.contentDocument.head).createChild({
20273                 tag : 'link',
20274                 rel : 'stylesheet',
20275                 type : 'text/css',
20276                 href : s
20277             });
20278         });
20279
20280         
20281     },
20282     
20283     removeStylesheets : function()
20284     {
20285         var _this = this;
20286         
20287         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20288             s.remove();
20289         });
20290     }
20291     
20292     // hide stuff that is not compatible
20293     /**
20294      * @event blur
20295      * @hide
20296      */
20297     /**
20298      * @event change
20299      * @hide
20300      */
20301     /**
20302      * @event focus
20303      * @hide
20304      */
20305     /**
20306      * @event specialkey
20307      * @hide
20308      */
20309     /**
20310      * @cfg {String} fieldClass @hide
20311      */
20312     /**
20313      * @cfg {String} focusClass @hide
20314      */
20315     /**
20316      * @cfg {String} autoCreate @hide
20317      */
20318     /**
20319      * @cfg {String} inputType @hide
20320      */
20321     /**
20322      * @cfg {String} invalidClass @hide
20323      */
20324     /**
20325      * @cfg {String} invalidText @hide
20326      */
20327     /**
20328      * @cfg {String} msgFx @hide
20329      */
20330     /**
20331      * @cfg {String} validateOnBlur @hide
20332      */
20333 });
20334
20335 Roo.HtmlEditorCore.white = [
20336         'area', 'br', 'img', 'input', 'hr', 'wbr',
20337         
20338        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20339        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20340        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20341        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20342        'table',   'ul',         'xmp', 
20343        
20344        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20345       'thead',   'tr', 
20346      
20347       'dir', 'menu', 'ol', 'ul', 'dl',
20348        
20349       'embed',  'object'
20350 ];
20351
20352
20353 Roo.HtmlEditorCore.black = [
20354     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20355         'applet', // 
20356         'base',   'basefont', 'bgsound', 'blink',  'body', 
20357         'frame',  'frameset', 'head',    'html',   'ilayer', 
20358         'iframe', 'layer',  'link',     'meta',    'object',   
20359         'script', 'style' ,'title',  'xml' // clean later..
20360 ];
20361 Roo.HtmlEditorCore.clean = [
20362     'script', 'style', 'title', 'xml'
20363 ];
20364 Roo.HtmlEditorCore.remove = [
20365     'font'
20366 ];
20367 // attributes..
20368
20369 Roo.HtmlEditorCore.ablack = [
20370     'on'
20371 ];
20372     
20373 Roo.HtmlEditorCore.aclean = [ 
20374     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20375 ];
20376
20377 // protocols..
20378 Roo.HtmlEditorCore.pwhite= [
20379         'http',  'https',  'mailto'
20380 ];
20381
20382 // white listed style attributes.
20383 Roo.HtmlEditorCore.cwhite= [
20384       //  'text-align', /// default is to allow most things..
20385       
20386          
20387 //        'font-size'//??
20388 ];
20389
20390 // black listed style attributes.
20391 Roo.HtmlEditorCore.cblack= [
20392       //  'font-size' -- this can be set by the project 
20393 ];
20394
20395
20396 Roo.HtmlEditorCore.swapCodes   =[ 
20397     [    8211, "--" ], 
20398     [    8212, "--" ], 
20399     [    8216,  "'" ],  
20400     [    8217, "'" ],  
20401     [    8220, '"' ],  
20402     [    8221, '"' ],  
20403     [    8226, "*" ],  
20404     [    8230, "..." ]
20405 ]; 
20406
20407     /*
20408  * - LGPL
20409  *
20410  * HtmlEditor
20411  * 
20412  */
20413
20414 /**
20415  * @class Roo.bootstrap.HtmlEditor
20416  * @extends Roo.bootstrap.TextArea
20417  * Bootstrap HtmlEditor class
20418
20419  * @constructor
20420  * Create a new HtmlEditor
20421  * @param {Object} config The config object
20422  */
20423
20424 Roo.bootstrap.HtmlEditor = function(config){
20425     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20426     if (!this.toolbars) {
20427         this.toolbars = [];
20428     }
20429     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20430     this.addEvents({
20431             /**
20432              * @event initialize
20433              * Fires when the editor is fully initialized (including the iframe)
20434              * @param {HtmlEditor} this
20435              */
20436             initialize: true,
20437             /**
20438              * @event activate
20439              * Fires when the editor is first receives the focus. Any insertion must wait
20440              * until after this event.
20441              * @param {HtmlEditor} this
20442              */
20443             activate: true,
20444              /**
20445              * @event beforesync
20446              * Fires before the textarea is updated with content from the editor iframe. Return false
20447              * to cancel the sync.
20448              * @param {HtmlEditor} this
20449              * @param {String} html
20450              */
20451             beforesync: true,
20452              /**
20453              * @event beforepush
20454              * Fires before the iframe editor is updated with content from the textarea. Return false
20455              * to cancel the push.
20456              * @param {HtmlEditor} this
20457              * @param {String} html
20458              */
20459             beforepush: true,
20460              /**
20461              * @event sync
20462              * Fires when the textarea is updated with content from the editor iframe.
20463              * @param {HtmlEditor} this
20464              * @param {String} html
20465              */
20466             sync: true,
20467              /**
20468              * @event push
20469              * Fires when the iframe editor is updated with content from the textarea.
20470              * @param {HtmlEditor} this
20471              * @param {String} html
20472              */
20473             push: true,
20474              /**
20475              * @event editmodechange
20476              * Fires when the editor switches edit modes
20477              * @param {HtmlEditor} this
20478              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20479              */
20480             editmodechange: true,
20481             /**
20482              * @event editorevent
20483              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20484              * @param {HtmlEditor} this
20485              */
20486             editorevent: true,
20487             /**
20488              * @event firstfocus
20489              * Fires when on first focus - needed by toolbars..
20490              * @param {HtmlEditor} this
20491              */
20492             firstfocus: true,
20493             /**
20494              * @event autosave
20495              * Auto save the htmlEditor value as a file into Events
20496              * @param {HtmlEditor} this
20497              */
20498             autosave: true,
20499             /**
20500              * @event savedpreview
20501              * preview the saved version of htmlEditor
20502              * @param {HtmlEditor} this
20503              */
20504             savedpreview: true
20505         });
20506 };
20507
20508
20509 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20510     
20511     
20512       /**
20513      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20514      */
20515     toolbars : false,
20516    
20517      /**
20518      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20519      *                        Roo.resizable.
20520      */
20521     resizable : false,
20522      /**
20523      * @cfg {Number} height (in pixels)
20524      */   
20525     height: 300,
20526    /**
20527      * @cfg {Number} width (in pixels)
20528      */   
20529     width: false,
20530     
20531     /**
20532      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20533      * 
20534      */
20535     stylesheets: false,
20536     
20537     // id of frame..
20538     frameId: false,
20539     
20540     // private properties
20541     validationEvent : false,
20542     deferHeight: true,
20543     initialized : false,
20544     activated : false,
20545     
20546     onFocus : Roo.emptyFn,
20547     iframePad:3,
20548     hideMode:'offsets',
20549     
20550     
20551     tbContainer : false,
20552     
20553     toolbarContainer :function() {
20554         return this.wrap.select('.x-html-editor-tb',true).first();
20555     },
20556
20557     /**
20558      * Protected method that will not generally be called directly. It
20559      * is called when the editor creates its toolbar. Override this method if you need to
20560      * add custom toolbar buttons.
20561      * @param {HtmlEditor} editor
20562      */
20563     createToolbar : function(){
20564         
20565         Roo.log("create toolbars");
20566         
20567         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20568         this.toolbars[0].render(this.toolbarContainer());
20569         
20570         return;
20571         
20572 //        if (!editor.toolbars || !editor.toolbars.length) {
20573 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20574 //        }
20575 //        
20576 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20577 //            editor.toolbars[i] = Roo.factory(
20578 //                    typeof(editor.toolbars[i]) == 'string' ?
20579 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20580 //                Roo.bootstrap.HtmlEditor);
20581 //            editor.toolbars[i].init(editor);
20582 //        }
20583     },
20584
20585      
20586     // private
20587     onRender : function(ct, position)
20588     {
20589        // Roo.log("Call onRender: " + this.xtype);
20590         var _t = this;
20591         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20592       
20593         this.wrap = this.inputEl().wrap({
20594             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20595         });
20596         
20597         this.editorcore.onRender(ct, position);
20598          
20599         if (this.resizable) {
20600             this.resizeEl = new Roo.Resizable(this.wrap, {
20601                 pinned : true,
20602                 wrap: true,
20603                 dynamic : true,
20604                 minHeight : this.height,
20605                 height: this.height,
20606                 handles : this.resizable,
20607                 width: this.width,
20608                 listeners : {
20609                     resize : function(r, w, h) {
20610                         _t.onResize(w,h); // -something
20611                     }
20612                 }
20613             });
20614             
20615         }
20616         this.createToolbar(this);
20617        
20618         
20619         if(!this.width && this.resizable){
20620             this.setSize(this.wrap.getSize());
20621         }
20622         if (this.resizeEl) {
20623             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20624             // should trigger onReize..
20625         }
20626         
20627     },
20628
20629     // private
20630     onResize : function(w, h)
20631     {
20632         Roo.log('resize: ' +w + ',' + h );
20633         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20634         var ew = false;
20635         var eh = false;
20636         
20637         if(this.inputEl() ){
20638             if(typeof w == 'number'){
20639                 var aw = w - this.wrap.getFrameWidth('lr');
20640                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20641                 ew = aw;
20642             }
20643             if(typeof h == 'number'){
20644                  var tbh = -11;  // fixme it needs to tool bar size!
20645                 for (var i =0; i < this.toolbars.length;i++) {
20646                     // fixme - ask toolbars for heights?
20647                     tbh += this.toolbars[i].el.getHeight();
20648                     //if (this.toolbars[i].footer) {
20649                     //    tbh += this.toolbars[i].footer.el.getHeight();
20650                     //}
20651                 }
20652               
20653                 
20654                 
20655                 
20656                 
20657                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20658                 ah -= 5; // knock a few pixes off for look..
20659                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20660                 var eh = ah;
20661             }
20662         }
20663         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20664         this.editorcore.onResize(ew,eh);
20665         
20666     },
20667
20668     /**
20669      * Toggles the editor between standard and source edit mode.
20670      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20671      */
20672     toggleSourceEdit : function(sourceEditMode)
20673     {
20674         this.editorcore.toggleSourceEdit(sourceEditMode);
20675         
20676         if(this.editorcore.sourceEditMode){
20677             Roo.log('editor - showing textarea');
20678             
20679 //            Roo.log('in');
20680 //            Roo.log(this.syncValue());
20681             this.syncValue();
20682             this.inputEl().removeClass(['hide', 'x-hidden']);
20683             this.inputEl().dom.removeAttribute('tabIndex');
20684             this.inputEl().focus();
20685         }else{
20686             Roo.log('editor - hiding textarea');
20687 //            Roo.log('out')
20688 //            Roo.log(this.pushValue()); 
20689             this.pushValue();
20690             
20691             this.inputEl().addClass(['hide', 'x-hidden']);
20692             this.inputEl().dom.setAttribute('tabIndex', -1);
20693             //this.deferFocus();
20694         }
20695          
20696         if(this.resizable){
20697             this.setSize(this.wrap.getSize());
20698         }
20699         
20700         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20701     },
20702  
20703     // private (for BoxComponent)
20704     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20705
20706     // private (for BoxComponent)
20707     getResizeEl : function(){
20708         return this.wrap;
20709     },
20710
20711     // private (for BoxComponent)
20712     getPositionEl : function(){
20713         return this.wrap;
20714     },
20715
20716     // private
20717     initEvents : function(){
20718         this.originalValue = this.getValue();
20719     },
20720
20721 //    /**
20722 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20723 //     * @method
20724 //     */
20725 //    markInvalid : Roo.emptyFn,
20726 //    /**
20727 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20728 //     * @method
20729 //     */
20730 //    clearInvalid : Roo.emptyFn,
20731
20732     setValue : function(v){
20733         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20734         this.editorcore.pushValue();
20735     },
20736
20737      
20738     // private
20739     deferFocus : function(){
20740         this.focus.defer(10, this);
20741     },
20742
20743     // doc'ed in Field
20744     focus : function(){
20745         this.editorcore.focus();
20746         
20747     },
20748       
20749
20750     // private
20751     onDestroy : function(){
20752         
20753         
20754         
20755         if(this.rendered){
20756             
20757             for (var i =0; i < this.toolbars.length;i++) {
20758                 // fixme - ask toolbars for heights?
20759                 this.toolbars[i].onDestroy();
20760             }
20761             
20762             this.wrap.dom.innerHTML = '';
20763             this.wrap.remove();
20764         }
20765     },
20766
20767     // private
20768     onFirstFocus : function(){
20769         //Roo.log("onFirstFocus");
20770         this.editorcore.onFirstFocus();
20771          for (var i =0; i < this.toolbars.length;i++) {
20772             this.toolbars[i].onFirstFocus();
20773         }
20774         
20775     },
20776     
20777     // private
20778     syncValue : function()
20779     {   
20780         this.editorcore.syncValue();
20781     },
20782     
20783     pushValue : function()
20784     {   
20785         this.editorcore.pushValue();
20786     }
20787      
20788     
20789     // hide stuff that is not compatible
20790     /**
20791      * @event blur
20792      * @hide
20793      */
20794     /**
20795      * @event change
20796      * @hide
20797      */
20798     /**
20799      * @event focus
20800      * @hide
20801      */
20802     /**
20803      * @event specialkey
20804      * @hide
20805      */
20806     /**
20807      * @cfg {String} fieldClass @hide
20808      */
20809     /**
20810      * @cfg {String} focusClass @hide
20811      */
20812     /**
20813      * @cfg {String} autoCreate @hide
20814      */
20815     /**
20816      * @cfg {String} inputType @hide
20817      */
20818     /**
20819      * @cfg {String} invalidClass @hide
20820      */
20821     /**
20822      * @cfg {String} invalidText @hide
20823      */
20824     /**
20825      * @cfg {String} msgFx @hide
20826      */
20827     /**
20828      * @cfg {String} validateOnBlur @hide
20829      */
20830 });
20831  
20832     
20833    
20834    
20835    
20836       
20837 Roo.namespace('Roo.bootstrap.htmleditor');
20838 /**
20839  * @class Roo.bootstrap.HtmlEditorToolbar1
20840  * Basic Toolbar
20841  * 
20842  * Usage:
20843  *
20844  new Roo.bootstrap.HtmlEditor({
20845     ....
20846     toolbars : [
20847         new Roo.bootstrap.HtmlEditorToolbar1({
20848             disable : { fonts: 1 , format: 1, ..., ... , ...],
20849             btns : [ .... ]
20850         })
20851     }
20852      
20853  * 
20854  * @cfg {Object} disable List of elements to disable..
20855  * @cfg {Array} btns List of additional buttons.
20856  * 
20857  * 
20858  * NEEDS Extra CSS? 
20859  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20860  */
20861  
20862 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20863 {
20864     
20865     Roo.apply(this, config);
20866     
20867     // default disabled, based on 'good practice'..
20868     this.disable = this.disable || {};
20869     Roo.applyIf(this.disable, {
20870         fontSize : true,
20871         colors : true,
20872         specialElements : true
20873     });
20874     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20875     
20876     this.editor = config.editor;
20877     this.editorcore = config.editor.editorcore;
20878     
20879     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20880     
20881     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20882     // dont call parent... till later.
20883 }
20884 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20885      
20886     bar : true,
20887     
20888     editor : false,
20889     editorcore : false,
20890     
20891     
20892     formats : [
20893         "p" ,  
20894         "h1","h2","h3","h4","h5","h6", 
20895         "pre", "code", 
20896         "abbr", "acronym", "address", "cite", "samp", "var",
20897         'div','span'
20898     ],
20899     
20900     onRender : function(ct, position)
20901     {
20902        // Roo.log("Call onRender: " + this.xtype);
20903         
20904        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20905        Roo.log(this.el);
20906        this.el.dom.style.marginBottom = '0';
20907        var _this = this;
20908        var editorcore = this.editorcore;
20909        var editor= this.editor;
20910        
20911        var children = [];
20912        var btn = function(id,cmd , toggle, handler){
20913        
20914             var  event = toggle ? 'toggle' : 'click';
20915        
20916             var a = {
20917                 size : 'sm',
20918                 xtype: 'Button',
20919                 xns: Roo.bootstrap,
20920                 glyphicon : id,
20921                 cmd : id || cmd,
20922                 enableToggle:toggle !== false,
20923                 //html : 'submit'
20924                 pressed : toggle ? false : null,
20925                 listeners : {}
20926             };
20927             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20928                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20929             };
20930             children.push(a);
20931             return a;
20932        }
20933         
20934         var style = {
20935                 xtype: 'Button',
20936                 size : 'sm',
20937                 xns: Roo.bootstrap,
20938                 glyphicon : 'font',
20939                 //html : 'submit'
20940                 menu : {
20941                     xtype: 'Menu',
20942                     xns: Roo.bootstrap,
20943                     items:  []
20944                 }
20945         };
20946         Roo.each(this.formats, function(f) {
20947             style.menu.items.push({
20948                 xtype :'MenuItem',
20949                 xns: Roo.bootstrap,
20950                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20951                 tagname : f,
20952                 listeners : {
20953                     click : function()
20954                     {
20955                         editorcore.insertTag(this.tagname);
20956                         editor.focus();
20957                     }
20958                 }
20959                 
20960             });
20961         });
20962          children.push(style);   
20963             
20964             
20965         btn('bold',false,true);
20966         btn('italic',false,true);
20967         btn('align-left', 'justifyleft',true);
20968         btn('align-center', 'justifycenter',true);
20969         btn('align-right' , 'justifyright',true);
20970         btn('link', false, false, function(btn) {
20971             //Roo.log("create link?");
20972             var url = prompt(this.createLinkText, this.defaultLinkValue);
20973             if(url && url != 'http:/'+'/'){
20974                 this.editorcore.relayCmd('createlink', url);
20975             }
20976         }),
20977         btn('list','insertunorderedlist',true);
20978         btn('pencil', false,true, function(btn){
20979                 Roo.log(this);
20980                 
20981                 this.toggleSourceEdit(btn.pressed);
20982         });
20983         /*
20984         var cog = {
20985                 xtype: 'Button',
20986                 size : 'sm',
20987                 xns: Roo.bootstrap,
20988                 glyphicon : 'cog',
20989                 //html : 'submit'
20990                 menu : {
20991                     xtype: 'Menu',
20992                     xns: Roo.bootstrap,
20993                     items:  []
20994                 }
20995         };
20996         
20997         cog.menu.items.push({
20998             xtype :'MenuItem',
20999             xns: Roo.bootstrap,
21000             html : Clean styles,
21001             tagname : f,
21002             listeners : {
21003                 click : function()
21004                 {
21005                     editorcore.insertTag(this.tagname);
21006                     editor.focus();
21007                 }
21008             }
21009             
21010         });
21011        */
21012         
21013          
21014        this.xtype = 'NavSimplebar';
21015         
21016         for(var i=0;i< children.length;i++) {
21017             
21018             this.buttons.add(this.addxtypeChild(children[i]));
21019             
21020         }
21021         
21022         editor.on('editorevent', this.updateToolbar, this);
21023     },
21024     onBtnClick : function(id)
21025     {
21026        this.editorcore.relayCmd(id);
21027        this.editorcore.focus();
21028     },
21029     
21030     /**
21031      * Protected method that will not generally be called directly. It triggers
21032      * a toolbar update by reading the markup state of the current selection in the editor.
21033      */
21034     updateToolbar: function(){
21035
21036         if(!this.editorcore.activated){
21037             this.editor.onFirstFocus(); // is this neeed?
21038             return;
21039         }
21040
21041         var btns = this.buttons; 
21042         var doc = this.editorcore.doc;
21043         btns.get('bold').setActive(doc.queryCommandState('bold'));
21044         btns.get('italic').setActive(doc.queryCommandState('italic'));
21045         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21046         
21047         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21048         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21049         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21050         
21051         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21052         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21053          /*
21054         
21055         var ans = this.editorcore.getAllAncestors();
21056         if (this.formatCombo) {
21057             
21058             
21059             var store = this.formatCombo.store;
21060             this.formatCombo.setValue("");
21061             for (var i =0; i < ans.length;i++) {
21062                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21063                     // select it..
21064                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21065                     break;
21066                 }
21067             }
21068         }
21069         
21070         
21071         
21072         // hides menus... - so this cant be on a menu...
21073         Roo.bootstrap.MenuMgr.hideAll();
21074         */
21075         Roo.bootstrap.MenuMgr.hideAll();
21076         //this.editorsyncValue();
21077     },
21078     onFirstFocus: function() {
21079         this.buttons.each(function(item){
21080            item.enable();
21081         });
21082     },
21083     toggleSourceEdit : function(sourceEditMode){
21084         
21085           
21086         if(sourceEditMode){
21087             Roo.log("disabling buttons");
21088            this.buttons.each( function(item){
21089                 if(item.cmd != 'pencil'){
21090                     item.disable();
21091                 }
21092             });
21093           
21094         }else{
21095             Roo.log("enabling buttons");
21096             if(this.editorcore.initialized){
21097                 this.buttons.each( function(item){
21098                     item.enable();
21099                 });
21100             }
21101             
21102         }
21103         Roo.log("calling toggole on editor");
21104         // tell the editor that it's been pressed..
21105         this.editor.toggleSourceEdit(sourceEditMode);
21106        
21107     }
21108 });
21109
21110
21111
21112
21113
21114 /**
21115  * @class Roo.bootstrap.Table.AbstractSelectionModel
21116  * @extends Roo.util.Observable
21117  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21118  * implemented by descendant classes.  This class should not be directly instantiated.
21119  * @constructor
21120  */
21121 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21122     this.locked = false;
21123     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21124 };
21125
21126
21127 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21128     /** @ignore Called by the grid automatically. Do not call directly. */
21129     init : function(grid){
21130         this.grid = grid;
21131         this.initEvents();
21132     },
21133
21134     /**
21135      * Locks the selections.
21136      */
21137     lock : function(){
21138         this.locked = true;
21139     },
21140
21141     /**
21142      * Unlocks the selections.
21143      */
21144     unlock : function(){
21145         this.locked = false;
21146     },
21147
21148     /**
21149      * Returns true if the selections are locked.
21150      * @return {Boolean}
21151      */
21152     isLocked : function(){
21153         return this.locked;
21154     }
21155 });
21156 /**
21157  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21158  * @class Roo.bootstrap.Table.RowSelectionModel
21159  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21160  * It supports multiple selections and keyboard selection/navigation. 
21161  * @constructor
21162  * @param {Object} config
21163  */
21164
21165 Roo.bootstrap.Table.RowSelectionModel = function(config){
21166     Roo.apply(this, config);
21167     this.selections = new Roo.util.MixedCollection(false, function(o){
21168         return o.id;
21169     });
21170
21171     this.last = false;
21172     this.lastActive = false;
21173
21174     this.addEvents({
21175         /**
21176              * @event selectionchange
21177              * Fires when the selection changes
21178              * @param {SelectionModel} this
21179              */
21180             "selectionchange" : true,
21181         /**
21182              * @event afterselectionchange
21183              * Fires after the selection changes (eg. by key press or clicking)
21184              * @param {SelectionModel} this
21185              */
21186             "afterselectionchange" : true,
21187         /**
21188              * @event beforerowselect
21189              * Fires when a row is selected being selected, return false to cancel.
21190              * @param {SelectionModel} this
21191              * @param {Number} rowIndex The selected index
21192              * @param {Boolean} keepExisting False if other selections will be cleared
21193              */
21194             "beforerowselect" : true,
21195         /**
21196              * @event rowselect
21197              * Fires when a row is selected.
21198              * @param {SelectionModel} this
21199              * @param {Number} rowIndex The selected index
21200              * @param {Roo.data.Record} r The record
21201              */
21202             "rowselect" : true,
21203         /**
21204              * @event rowdeselect
21205              * Fires when a row is deselected.
21206              * @param {SelectionModel} this
21207              * @param {Number} rowIndex The selected index
21208              */
21209         "rowdeselect" : true
21210     });
21211     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21212     this.locked = false;
21213 };
21214
21215 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21216     /**
21217      * @cfg {Boolean} singleSelect
21218      * True to allow selection of only one row at a time (defaults to false)
21219      */
21220     singleSelect : false,
21221
21222     // private
21223     initEvents : function(){
21224
21225         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21226             this.grid.on("mousedown", this.handleMouseDown, this);
21227         }else{ // allow click to work like normal
21228             this.grid.on("rowclick", this.handleDragableRowClick, this);
21229         }
21230
21231         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21232             "up" : function(e){
21233                 if(!e.shiftKey){
21234                     this.selectPrevious(e.shiftKey);
21235                 }else if(this.last !== false && this.lastActive !== false){
21236                     var last = this.last;
21237                     this.selectRange(this.last,  this.lastActive-1);
21238                     this.grid.getView().focusRow(this.lastActive);
21239                     if(last !== false){
21240                         this.last = last;
21241                     }
21242                 }else{
21243                     this.selectFirstRow();
21244                 }
21245                 this.fireEvent("afterselectionchange", this);
21246             },
21247             "down" : function(e){
21248                 if(!e.shiftKey){
21249                     this.selectNext(e.shiftKey);
21250                 }else if(this.last !== false && this.lastActive !== false){
21251                     var last = this.last;
21252                     this.selectRange(this.last,  this.lastActive+1);
21253                     this.grid.getView().focusRow(this.lastActive);
21254                     if(last !== false){
21255                         this.last = last;
21256                     }
21257                 }else{
21258                     this.selectFirstRow();
21259                 }
21260                 this.fireEvent("afterselectionchange", this);
21261             },
21262             scope: this
21263         });
21264
21265         var view = this.grid.view;
21266         view.on("refresh", this.onRefresh, this);
21267         view.on("rowupdated", this.onRowUpdated, this);
21268         view.on("rowremoved", this.onRemove, this);
21269     },
21270
21271     // private
21272     onRefresh : function(){
21273         var ds = this.grid.dataSource, i, v = this.grid.view;
21274         var s = this.selections;
21275         s.each(function(r){
21276             if((i = ds.indexOfId(r.id)) != -1){
21277                 v.onRowSelect(i);
21278             }else{
21279                 s.remove(r);
21280             }
21281         });
21282     },
21283
21284     // private
21285     onRemove : function(v, index, r){
21286         this.selections.remove(r);
21287     },
21288
21289     // private
21290     onRowUpdated : function(v, index, r){
21291         if(this.isSelected(r)){
21292             v.onRowSelect(index);
21293         }
21294     },
21295
21296     /**
21297      * Select records.
21298      * @param {Array} records The records to select
21299      * @param {Boolean} keepExisting (optional) True to keep existing selections
21300      */
21301     selectRecords : function(records, keepExisting){
21302         if(!keepExisting){
21303             this.clearSelections();
21304         }
21305         var ds = this.grid.dataSource;
21306         for(var i = 0, len = records.length; i < len; i++){
21307             this.selectRow(ds.indexOf(records[i]), true);
21308         }
21309     },
21310
21311     /**
21312      * Gets the number of selected rows.
21313      * @return {Number}
21314      */
21315     getCount : function(){
21316         return this.selections.length;
21317     },
21318
21319     /**
21320      * Selects the first row in the grid.
21321      */
21322     selectFirstRow : function(){
21323         this.selectRow(0);
21324     },
21325
21326     /**
21327      * Select the last row.
21328      * @param {Boolean} keepExisting (optional) True to keep existing selections
21329      */
21330     selectLastRow : function(keepExisting){
21331         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21332     },
21333
21334     /**
21335      * Selects the row immediately following the last selected row.
21336      * @param {Boolean} keepExisting (optional) True to keep existing selections
21337      */
21338     selectNext : function(keepExisting){
21339         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21340             this.selectRow(this.last+1, keepExisting);
21341             this.grid.getView().focusRow(this.last);
21342         }
21343     },
21344
21345     /**
21346      * Selects the row that precedes the last selected row.
21347      * @param {Boolean} keepExisting (optional) True to keep existing selections
21348      */
21349     selectPrevious : function(keepExisting){
21350         if(this.last){
21351             this.selectRow(this.last-1, keepExisting);
21352             this.grid.getView().focusRow(this.last);
21353         }
21354     },
21355
21356     /**
21357      * Returns the selected records
21358      * @return {Array} Array of selected records
21359      */
21360     getSelections : function(){
21361         return [].concat(this.selections.items);
21362     },
21363
21364     /**
21365      * Returns the first selected record.
21366      * @return {Record}
21367      */
21368     getSelected : function(){
21369         return this.selections.itemAt(0);
21370     },
21371
21372
21373     /**
21374      * Clears all selections.
21375      */
21376     clearSelections : function(fast){
21377         if(this.locked) return;
21378         if(fast !== true){
21379             var ds = this.grid.dataSource;
21380             var s = this.selections;
21381             s.each(function(r){
21382                 this.deselectRow(ds.indexOfId(r.id));
21383             }, this);
21384             s.clear();
21385         }else{
21386             this.selections.clear();
21387         }
21388         this.last = false;
21389     },
21390
21391
21392     /**
21393      * Selects all rows.
21394      */
21395     selectAll : function(){
21396         if(this.locked) return;
21397         this.selections.clear();
21398         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21399             this.selectRow(i, true);
21400         }
21401     },
21402
21403     /**
21404      * Returns True if there is a selection.
21405      * @return {Boolean}
21406      */
21407     hasSelection : function(){
21408         return this.selections.length > 0;
21409     },
21410
21411     /**
21412      * Returns True if the specified row is selected.
21413      * @param {Number/Record} record The record or index of the record to check
21414      * @return {Boolean}
21415      */
21416     isSelected : function(index){
21417         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21418         return (r && this.selections.key(r.id) ? true : false);
21419     },
21420
21421     /**
21422      * Returns True if the specified record id is selected.
21423      * @param {String} id The id of record to check
21424      * @return {Boolean}
21425      */
21426     isIdSelected : function(id){
21427         return (this.selections.key(id) ? true : false);
21428     },
21429
21430     // private
21431     handleMouseDown : function(e, t){
21432         var view = this.grid.getView(), rowIndex;
21433         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21434             return;
21435         };
21436         if(e.shiftKey && this.last !== false){
21437             var last = this.last;
21438             this.selectRange(last, rowIndex, e.ctrlKey);
21439             this.last = last; // reset the last
21440             view.focusRow(rowIndex);
21441         }else{
21442             var isSelected = this.isSelected(rowIndex);
21443             if(e.button !== 0 && isSelected){
21444                 view.focusRow(rowIndex);
21445             }else if(e.ctrlKey && isSelected){
21446                 this.deselectRow(rowIndex);
21447             }else if(!isSelected){
21448                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21449                 view.focusRow(rowIndex);
21450             }
21451         }
21452         this.fireEvent("afterselectionchange", this);
21453     },
21454     // private
21455     handleDragableRowClick :  function(grid, rowIndex, e) 
21456     {
21457         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21458             this.selectRow(rowIndex, false);
21459             grid.view.focusRow(rowIndex);
21460              this.fireEvent("afterselectionchange", this);
21461         }
21462     },
21463     
21464     /**
21465      * Selects multiple rows.
21466      * @param {Array} rows Array of the indexes of the row to select
21467      * @param {Boolean} keepExisting (optional) True to keep existing selections
21468      */
21469     selectRows : function(rows, keepExisting){
21470         if(!keepExisting){
21471             this.clearSelections();
21472         }
21473         for(var i = 0, len = rows.length; i < len; i++){
21474             this.selectRow(rows[i], true);
21475         }
21476     },
21477
21478     /**
21479      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21480      * @param {Number} startRow The index of the first row in the range
21481      * @param {Number} endRow The index of the last row in the range
21482      * @param {Boolean} keepExisting (optional) True to retain existing selections
21483      */
21484     selectRange : function(startRow, endRow, keepExisting){
21485         if(this.locked) return;
21486         if(!keepExisting){
21487             this.clearSelections();
21488         }
21489         if(startRow <= endRow){
21490             for(var i = startRow; i <= endRow; i++){
21491                 this.selectRow(i, true);
21492             }
21493         }else{
21494             for(var i = startRow; i >= endRow; i--){
21495                 this.selectRow(i, true);
21496             }
21497         }
21498     },
21499
21500     /**
21501      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21502      * @param {Number} startRow The index of the first row in the range
21503      * @param {Number} endRow The index of the last row in the range
21504      */
21505     deselectRange : function(startRow, endRow, preventViewNotify){
21506         if(this.locked) return;
21507         for(var i = startRow; i <= endRow; i++){
21508             this.deselectRow(i, preventViewNotify);
21509         }
21510     },
21511
21512     /**
21513      * Selects a row.
21514      * @param {Number} row The index of the row to select
21515      * @param {Boolean} keepExisting (optional) True to keep existing selections
21516      */
21517     selectRow : function(index, keepExisting, preventViewNotify){
21518         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21519         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21520             if(!keepExisting || this.singleSelect){
21521                 this.clearSelections();
21522             }
21523             var r = this.grid.dataSource.getAt(index);
21524             this.selections.add(r);
21525             this.last = this.lastActive = index;
21526             if(!preventViewNotify){
21527                 this.grid.getView().onRowSelect(index);
21528             }
21529             this.fireEvent("rowselect", this, index, r);
21530             this.fireEvent("selectionchange", this);
21531         }
21532     },
21533
21534     /**
21535      * Deselects a row.
21536      * @param {Number} row The index of the row to deselect
21537      */
21538     deselectRow : function(index, preventViewNotify){
21539         if(this.locked) return;
21540         if(this.last == index){
21541             this.last = false;
21542         }
21543         if(this.lastActive == index){
21544             this.lastActive = false;
21545         }
21546         var r = this.grid.dataSource.getAt(index);
21547         this.selections.remove(r);
21548         if(!preventViewNotify){
21549             this.grid.getView().onRowDeselect(index);
21550         }
21551         this.fireEvent("rowdeselect", this, index);
21552         this.fireEvent("selectionchange", this);
21553     },
21554
21555     // private
21556     restoreLast : function(){
21557         if(this._last){
21558             this.last = this._last;
21559         }
21560     },
21561
21562     // private
21563     acceptsNav : function(row, col, cm){
21564         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21565     },
21566
21567     // private
21568     onEditorKey : function(field, e){
21569         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21570         if(k == e.TAB){
21571             e.stopEvent();
21572             ed.completeEdit();
21573             if(e.shiftKey){
21574                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21575             }else{
21576                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21577             }
21578         }else if(k == e.ENTER && !e.ctrlKey){
21579             e.stopEvent();
21580             ed.completeEdit();
21581             if(e.shiftKey){
21582                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21583             }else{
21584                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21585             }
21586         }else if(k == e.ESC){
21587             ed.cancelEdit();
21588         }
21589         if(newCell){
21590             g.startEditing(newCell[0], newCell[1]);
21591         }
21592     }
21593 });/*
21594  * Based on:
21595  * Ext JS Library 1.1.1
21596  * Copyright(c) 2006-2007, Ext JS, LLC.
21597  *
21598  * Originally Released Under LGPL - original licence link has changed is not relivant.
21599  *
21600  * Fork - LGPL
21601  * <script type="text/javascript">
21602  */
21603  
21604 /**
21605  * @class Roo.bootstrap.PagingToolbar
21606  * @extends Roo.Row
21607  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21608  * @constructor
21609  * Create a new PagingToolbar
21610  * @param {Object} config The config object
21611  */
21612 Roo.bootstrap.PagingToolbar = function(config)
21613 {
21614     // old args format still supported... - xtype is prefered..
21615         // created from xtype...
21616     var ds = config.dataSource;
21617     this.toolbarItems = [];
21618     if (config.items) {
21619         this.toolbarItems = config.items;
21620 //        config.items = [];
21621     }
21622     
21623     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21624     this.ds = ds;
21625     this.cursor = 0;
21626     if (ds) { 
21627         this.bind(ds);
21628     }
21629     
21630     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21631     
21632 };
21633
21634 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21635     /**
21636      * @cfg {Roo.data.Store} dataSource
21637      * The underlying data store providing the paged data
21638      */
21639     /**
21640      * @cfg {String/HTMLElement/Element} container
21641      * container The id or element that will contain the toolbar
21642      */
21643     /**
21644      * @cfg {Boolean} displayInfo
21645      * True to display the displayMsg (defaults to false)
21646      */
21647     /**
21648      * @cfg {Number} pageSize
21649      * The number of records to display per page (defaults to 20)
21650      */
21651     pageSize: 20,
21652     /**
21653      * @cfg {String} displayMsg
21654      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21655      */
21656     displayMsg : 'Displaying {0} - {1} of {2}',
21657     /**
21658      * @cfg {String} emptyMsg
21659      * The message to display when no records are found (defaults to "No data to display")
21660      */
21661     emptyMsg : 'No data to display',
21662     /**
21663      * Customizable piece of the default paging text (defaults to "Page")
21664      * @type String
21665      */
21666     beforePageText : "Page",
21667     /**
21668      * Customizable piece of the default paging text (defaults to "of %0")
21669      * @type String
21670      */
21671     afterPageText : "of {0}",
21672     /**
21673      * Customizable piece of the default paging text (defaults to "First Page")
21674      * @type String
21675      */
21676     firstText : "First Page",
21677     /**
21678      * Customizable piece of the default paging text (defaults to "Previous Page")
21679      * @type String
21680      */
21681     prevText : "Previous Page",
21682     /**
21683      * Customizable piece of the default paging text (defaults to "Next Page")
21684      * @type String
21685      */
21686     nextText : "Next Page",
21687     /**
21688      * Customizable piece of the default paging text (defaults to "Last Page")
21689      * @type String
21690      */
21691     lastText : "Last Page",
21692     /**
21693      * Customizable piece of the default paging text (defaults to "Refresh")
21694      * @type String
21695      */
21696     refreshText : "Refresh",
21697
21698     buttons : false,
21699     // private
21700     onRender : function(ct, position) 
21701     {
21702         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21703         this.navgroup.parentId = this.id;
21704         this.navgroup.onRender(this.el, null);
21705         // add the buttons to the navgroup
21706         
21707         if(this.displayInfo){
21708             Roo.log(this.el.select('ul.navbar-nav',true).first());
21709             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21710             this.displayEl = this.el.select('.x-paging-info', true).first();
21711 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21712 //            this.displayEl = navel.el.select('span',true).first();
21713         }
21714         
21715         var _this = this;
21716         
21717         if(this.buttons){
21718             Roo.each(_this.buttons, function(e){
21719                Roo.factory(e).onRender(_this.el, null);
21720             });
21721         }
21722             
21723         Roo.each(_this.toolbarItems, function(e) {
21724             _this.navgroup.addItem(e);
21725         });
21726         
21727         
21728         this.first = this.navgroup.addItem({
21729             tooltip: this.firstText,
21730             cls: "prev",
21731             icon : 'fa fa-backward',
21732             disabled: true,
21733             preventDefault: true,
21734             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21735         });
21736         
21737         this.prev =  this.navgroup.addItem({
21738             tooltip: this.prevText,
21739             cls: "prev",
21740             icon : 'fa fa-step-backward',
21741             disabled: true,
21742             preventDefault: true,
21743             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21744         });
21745     //this.addSeparator();
21746         
21747         
21748         var field = this.navgroup.addItem( {
21749             tagtype : 'span',
21750             cls : 'x-paging-position',
21751             
21752             html : this.beforePageText  +
21753                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21754                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21755          } ); //?? escaped?
21756         
21757         this.field = field.el.select('input', true).first();
21758         this.field.on("keydown", this.onPagingKeydown, this);
21759         this.field.on("focus", function(){this.dom.select();});
21760     
21761     
21762         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21763         //this.field.setHeight(18);
21764         //this.addSeparator();
21765         this.next = this.navgroup.addItem({
21766             tooltip: this.nextText,
21767             cls: "next",
21768             html : ' <i class="fa fa-step-forward">',
21769             disabled: true,
21770             preventDefault: true,
21771             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21772         });
21773         this.last = this.navgroup.addItem({
21774             tooltip: this.lastText,
21775             icon : 'fa fa-forward',
21776             cls: "next",
21777             disabled: true,
21778             preventDefault: true,
21779             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21780         });
21781     //this.addSeparator();
21782         this.loading = this.navgroup.addItem({
21783             tooltip: this.refreshText,
21784             icon: 'fa fa-refresh',
21785             preventDefault: true,
21786             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21787         });
21788
21789     },
21790
21791     // private
21792     updateInfo : function(){
21793         if(this.displayEl){
21794             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21795             var msg = count == 0 ?
21796                 this.emptyMsg :
21797                 String.format(
21798                     this.displayMsg,
21799                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21800                 );
21801             this.displayEl.update(msg);
21802         }
21803     },
21804
21805     // private
21806     onLoad : function(ds, r, o){
21807        this.cursor = o.params ? o.params.start : 0;
21808        var d = this.getPageData(),
21809             ap = d.activePage,
21810             ps = d.pages;
21811         
21812        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21813        this.field.dom.value = ap;
21814        this.first.setDisabled(ap == 1);
21815        this.prev.setDisabled(ap == 1);
21816        this.next.setDisabled(ap == ps);
21817        this.last.setDisabled(ap == ps);
21818        this.loading.enable();
21819        this.updateInfo();
21820     },
21821
21822     // private
21823     getPageData : function(){
21824         var total = this.ds.getTotalCount();
21825         return {
21826             total : total,
21827             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21828             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21829         };
21830     },
21831
21832     // private
21833     onLoadError : function(){
21834         this.loading.enable();
21835     },
21836
21837     // private
21838     onPagingKeydown : function(e){
21839         var k = e.getKey();
21840         var d = this.getPageData();
21841         if(k == e.RETURN){
21842             var v = this.field.dom.value, pageNum;
21843             if(!v || isNaN(pageNum = parseInt(v, 10))){
21844                 this.field.dom.value = d.activePage;
21845                 return;
21846             }
21847             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21848             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21849             e.stopEvent();
21850         }
21851         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))
21852         {
21853           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21854           this.field.dom.value = pageNum;
21855           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21856           e.stopEvent();
21857         }
21858         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21859         {
21860           var v = this.field.dom.value, pageNum; 
21861           var increment = (e.shiftKey) ? 10 : 1;
21862           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21863             increment *= -1;
21864           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21865             this.field.dom.value = d.activePage;
21866             return;
21867           }
21868           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21869           {
21870             this.field.dom.value = parseInt(v, 10) + increment;
21871             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21872             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21873           }
21874           e.stopEvent();
21875         }
21876     },
21877
21878     // private
21879     beforeLoad : function(){
21880         if(this.loading){
21881             this.loading.disable();
21882         }
21883     },
21884
21885     // private
21886     onClick : function(which){
21887         
21888         var ds = this.ds;
21889         if (!ds) {
21890             return;
21891         }
21892         
21893         switch(which){
21894             case "first":
21895                 ds.load({params:{start: 0, limit: this.pageSize}});
21896             break;
21897             case "prev":
21898                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21899             break;
21900             case "next":
21901                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21902             break;
21903             case "last":
21904                 var total = ds.getTotalCount();
21905                 var extra = total % this.pageSize;
21906                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21907                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21908             break;
21909             case "refresh":
21910                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21911             break;
21912         }
21913     },
21914
21915     /**
21916      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21917      * @param {Roo.data.Store} store The data store to unbind
21918      */
21919     unbind : function(ds){
21920         ds.un("beforeload", this.beforeLoad, this);
21921         ds.un("load", this.onLoad, this);
21922         ds.un("loadexception", this.onLoadError, this);
21923         ds.un("remove", this.updateInfo, this);
21924         ds.un("add", this.updateInfo, this);
21925         this.ds = undefined;
21926     },
21927
21928     /**
21929      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21930      * @param {Roo.data.Store} store The data store to bind
21931      */
21932     bind : function(ds){
21933         ds.on("beforeload", this.beforeLoad, this);
21934         ds.on("load", this.onLoad, this);
21935         ds.on("loadexception", this.onLoadError, this);
21936         ds.on("remove", this.updateInfo, this);
21937         ds.on("add", this.updateInfo, this);
21938         this.ds = ds;
21939     }
21940 });/*
21941  * - LGPL
21942  *
21943  * element
21944  * 
21945  */
21946
21947 /**
21948  * @class Roo.bootstrap.MessageBar
21949  * @extends Roo.bootstrap.Component
21950  * Bootstrap MessageBar class
21951  * @cfg {String} html contents of the MessageBar
21952  * @cfg {String} weight (info | success | warning | danger) default info
21953  * @cfg {String} beforeClass insert the bar before the given class
21954  * @cfg {Boolean} closable (true | false) default false
21955  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21956  * 
21957  * @constructor
21958  * Create a new Element
21959  * @param {Object} config The config object
21960  */
21961
21962 Roo.bootstrap.MessageBar = function(config){
21963     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21964 };
21965
21966 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21967     
21968     html: '',
21969     weight: 'info',
21970     closable: false,
21971     fixed: false,
21972     beforeClass: 'bootstrap-sticky-wrap',
21973     
21974     getAutoCreate : function(){
21975         
21976         var cfg = {
21977             tag: 'div',
21978             cls: 'alert alert-dismissable alert-' + this.weight,
21979             cn: [
21980                 {
21981                     tag: 'span',
21982                     cls: 'message',
21983                     html: this.html || ''
21984                 }
21985             ]
21986         }
21987         
21988         if(this.fixed){
21989             cfg.cls += ' alert-messages-fixed';
21990         }
21991         
21992         if(this.closable){
21993             cfg.cn.push({
21994                 tag: 'button',
21995                 cls: 'close',
21996                 html: 'x'
21997             });
21998         }
21999         
22000         return cfg;
22001     },
22002     
22003     onRender : function(ct, position)
22004     {
22005         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22006         
22007         if(!this.el){
22008             var cfg = Roo.apply({},  this.getAutoCreate());
22009             cfg.id = Roo.id();
22010             
22011             if (this.cls) {
22012                 cfg.cls += ' ' + this.cls;
22013             }
22014             if (this.style) {
22015                 cfg.style = this.style;
22016             }
22017             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22018             
22019             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22020         }
22021         
22022         this.el.select('>button.close').on('click', this.hide, this);
22023         
22024     },
22025     
22026     show : function()
22027     {
22028         if (!this.rendered) {
22029             this.render();
22030         }
22031         
22032         this.el.show();
22033         
22034         this.fireEvent('show', this);
22035         
22036     },
22037     
22038     hide : function()
22039     {
22040         if (!this.rendered) {
22041             this.render();
22042         }
22043         
22044         this.el.hide();
22045         
22046         this.fireEvent('hide', this);
22047     },
22048     
22049     update : function()
22050     {
22051 //        var e = this.el.dom.firstChild;
22052 //        
22053 //        if(this.closable){
22054 //            e = e.nextSibling;
22055 //        }
22056 //        
22057 //        e.data = this.html || '';
22058
22059         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22060     }
22061    
22062 });
22063
22064  
22065
22066      /*
22067  * - LGPL
22068  *
22069  * Graph
22070  * 
22071  */
22072
22073
22074 /**
22075  * @class Roo.bootstrap.Graph
22076  * @extends Roo.bootstrap.Component
22077  * Bootstrap Graph class
22078 > Prameters
22079  -sm {number} sm 4
22080  -md {number} md 5
22081  @cfg {String} graphtype  bar | vbar | pie
22082  @cfg {number} g_x coodinator | centre x (pie)
22083  @cfg {number} g_y coodinator | centre y (pie)
22084  @cfg {number} g_r radius (pie)
22085  @cfg {number} g_height height of the chart (respected by all elements in the set)
22086  @cfg {number} g_width width of the chart (respected by all elements in the set)
22087  @cfg {Object} title The title of the chart
22088     
22089  -{Array}  values
22090  -opts (object) options for the chart 
22091      o {
22092      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22093      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22094      o vgutter (number)
22095      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.
22096      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22097      o to
22098      o stretch (boolean)
22099      o }
22100  -opts (object) options for the pie
22101      o{
22102      o cut
22103      o startAngle (number)
22104      o endAngle (number)
22105      } 
22106  *
22107  * @constructor
22108  * Create a new Input
22109  * @param {Object} config The config object
22110  */
22111
22112 Roo.bootstrap.Graph = function(config){
22113     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22114     
22115     this.addEvents({
22116         // img events
22117         /**
22118          * @event click
22119          * The img click event for the img.
22120          * @param {Roo.EventObject} e
22121          */
22122         "click" : true
22123     });
22124 };
22125
22126 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22127     
22128     sm: 4,
22129     md: 5,
22130     graphtype: 'bar',
22131     g_height: 250,
22132     g_width: 400,
22133     g_x: 50,
22134     g_y: 50,
22135     g_r: 30,
22136     opts:{
22137         //g_colors: this.colors,
22138         g_type: 'soft',
22139         g_gutter: '20%'
22140
22141     },
22142     title : false,
22143
22144     getAutoCreate : function(){
22145         
22146         var cfg = {
22147             tag: 'div',
22148             html : null
22149         }
22150         
22151         
22152         return  cfg;
22153     },
22154
22155     onRender : function(ct,position){
22156         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22157         this.raphael = Raphael(this.el.dom);
22158         
22159                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22160                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22161                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22162                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22163                 /*
22164                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22165                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22166                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22167                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22168                 
22169                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22170                 r.barchart(330, 10, 300, 220, data1);
22171                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22172                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22173                 */
22174                 
22175                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22176                 // r.barchart(30, 30, 560, 250,  xdata, {
22177                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22178                 //     axis : "0 0 1 1",
22179                 //     axisxlabels :  xdata
22180                 //     //yvalues : cols,
22181                    
22182                 // });
22183 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22184 //        
22185 //        this.load(null,xdata,{
22186 //                axis : "0 0 1 1",
22187 //                axisxlabels :  xdata
22188 //                });
22189
22190     },
22191
22192     load : function(graphtype,xdata,opts){
22193         this.raphael.clear();
22194         if(!graphtype) {
22195             graphtype = this.graphtype;
22196         }
22197         if(!opts){
22198             opts = this.opts;
22199         }
22200         var r = this.raphael,
22201             fin = function () {
22202                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22203             },
22204             fout = function () {
22205                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22206             },
22207             pfin = function() {
22208                 this.sector.stop();
22209                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22210
22211                 if (this.label) {
22212                     this.label[0].stop();
22213                     this.label[0].attr({ r: 7.5 });
22214                     this.label[1].attr({ "font-weight": 800 });
22215                 }
22216             },
22217             pfout = function() {
22218                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22219
22220                 if (this.label) {
22221                     this.label[0].animate({ r: 5 }, 500, "bounce");
22222                     this.label[1].attr({ "font-weight": 400 });
22223                 }
22224             };
22225
22226         switch(graphtype){
22227             case 'bar':
22228                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22229                 break;
22230             case 'hbar':
22231                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22232                 break;
22233             case 'pie':
22234 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22235 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22236 //            
22237                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22238                 
22239                 break;
22240
22241         }
22242         
22243         if(this.title){
22244             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22245         }
22246         
22247     },
22248     
22249     setTitle: function(o)
22250     {
22251         this.title = o;
22252     },
22253     
22254     initEvents: function() {
22255         
22256         if(!this.href){
22257             this.el.on('click', this.onClick, this);
22258         }
22259     },
22260     
22261     onClick : function(e)
22262     {
22263         Roo.log('img onclick');
22264         this.fireEvent('click', this, e);
22265     }
22266    
22267 });
22268
22269  
22270 /*
22271  * - LGPL
22272  *
22273  * numberBox
22274  * 
22275  */
22276 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22277
22278 /**
22279  * @class Roo.bootstrap.dash.NumberBox
22280  * @extends Roo.bootstrap.Component
22281  * Bootstrap NumberBox class
22282  * @cfg {String} headline Box headline
22283  * @cfg {String} content Box content
22284  * @cfg {String} icon Box icon
22285  * @cfg {String} footer Footer text
22286  * @cfg {String} fhref Footer href
22287  * 
22288  * @constructor
22289  * Create a new NumberBox
22290  * @param {Object} config The config object
22291  */
22292
22293
22294 Roo.bootstrap.dash.NumberBox = function(config){
22295     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22296     
22297 };
22298
22299 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22300     
22301     headline : '',
22302     content : '',
22303     icon : '',
22304     footer : '',
22305     fhref : '',
22306     ficon : '',
22307     
22308     getAutoCreate : function(){
22309         
22310         var cfg = {
22311             tag : 'div',
22312             cls : 'small-box ',
22313             cn : [
22314                 {
22315                     tag : 'div',
22316                     cls : 'inner',
22317                     cn :[
22318                         {
22319                             tag : 'h3',
22320                             cls : 'roo-headline',
22321                             html : this.headline
22322                         },
22323                         {
22324                             tag : 'p',
22325                             cls : 'roo-content',
22326                             html : this.content
22327                         }
22328                     ]
22329                 }
22330             ]
22331         }
22332         
22333         if(this.icon){
22334             cfg.cn.push({
22335                 tag : 'div',
22336                 cls : 'icon',
22337                 cn :[
22338                     {
22339                         tag : 'i',
22340                         cls : 'ion ' + this.icon
22341                     }
22342                 ]
22343             });
22344         }
22345         
22346         if(this.footer){
22347             var footer = {
22348                 tag : 'a',
22349                 cls : 'small-box-footer',
22350                 href : this.fhref || '#',
22351                 html : this.footer
22352             };
22353             
22354             cfg.cn.push(footer);
22355             
22356         }
22357         
22358         return  cfg;
22359     },
22360
22361     onRender : function(ct,position){
22362         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22363
22364
22365        
22366                 
22367     },
22368
22369     setHeadline: function (value)
22370     {
22371         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22372     },
22373     
22374     setFooter: function (value, href)
22375     {
22376         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22377         
22378         if(href){
22379             this.el.select('a.small-box-footer',true).first().attr('href', href);
22380         }
22381         
22382     },
22383
22384     setContent: function (value)
22385     {
22386         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22387     },
22388
22389     initEvents: function() 
22390     {   
22391         
22392     }
22393     
22394 });
22395
22396  
22397 /*
22398  * - LGPL
22399  *
22400  * TabBox
22401  * 
22402  */
22403 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22404
22405 /**
22406  * @class Roo.bootstrap.dash.TabBox
22407  * @extends Roo.bootstrap.Component
22408  * Bootstrap TabBox class
22409  * @cfg {String} title Title of the TabBox
22410  * @cfg {String} icon Icon of the TabBox
22411  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22412  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22413  * 
22414  * @constructor
22415  * Create a new TabBox
22416  * @param {Object} config The config object
22417  */
22418
22419
22420 Roo.bootstrap.dash.TabBox = function(config){
22421     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22422     this.addEvents({
22423         // raw events
22424         /**
22425          * @event addpane
22426          * When a pane is added
22427          * @param {Roo.bootstrap.dash.TabPane} pane
22428          */
22429         "addpane" : true,
22430         /**
22431          * @event activatepane
22432          * When a pane is activated
22433          * @param {Roo.bootstrap.dash.TabPane} pane
22434          */
22435         "activatepane" : true
22436         
22437          
22438     });
22439     
22440     this.panes = [];
22441 };
22442
22443 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22444
22445     title : '',
22446     icon : false,
22447     showtabs : true,
22448     tabScrollable : false,
22449     
22450     getChildContainer : function()
22451     {
22452         return this.el.select('.tab-content', true).first();
22453     },
22454     
22455     getAutoCreate : function(){
22456         
22457         var header = {
22458             tag: 'li',
22459             cls: 'pull-left header',
22460             html: this.title,
22461             cn : []
22462         };
22463         
22464         if(this.icon){
22465             header.cn.push({
22466                 tag: 'i',
22467                 cls: 'fa ' + this.icon
22468             });
22469         }
22470         
22471         var h = {
22472             tag: 'ul',
22473             cls: 'nav nav-tabs pull-right',
22474             cn: [
22475                 header
22476             ]
22477         };
22478         
22479         if(this.tabScrollable){
22480             h = {
22481                 tag: 'div',
22482                 cls: 'tab-header',
22483                 cn: [
22484                     {
22485                         tag: 'ul',
22486                         cls: 'nav nav-tabs pull-right',
22487                         cn: [
22488                             header
22489                         ]
22490                     }
22491                 ]
22492             }
22493         }
22494         
22495         var cfg = {
22496             tag: 'div',
22497             cls: 'nav-tabs-custom',
22498             cn: [
22499                 h,
22500                 {
22501                     tag: 'div',
22502                     cls: 'tab-content no-padding',
22503                     cn: []
22504                 }
22505             ]
22506         }
22507
22508         return  cfg;
22509     },
22510     initEvents : function()
22511     {
22512         //Roo.log('add add pane handler');
22513         this.on('addpane', this.onAddPane, this);
22514     },
22515      /**
22516      * Updates the box title
22517      * @param {String} html to set the title to.
22518      */
22519     setTitle : function(value)
22520     {
22521         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22522     },
22523     onAddPane : function(pane)
22524     {
22525         this.panes.push(pane);
22526         //Roo.log('addpane');
22527         //Roo.log(pane);
22528         // tabs are rendere left to right..
22529         if(!this.showtabs){
22530             return;
22531         }
22532         
22533         var ctr = this.el.select('.nav-tabs', true).first();
22534          
22535          
22536         var existing = ctr.select('.nav-tab',true);
22537         var qty = existing.getCount();;
22538         
22539         
22540         var tab = ctr.createChild({
22541             tag : 'li',
22542             cls : 'nav-tab' + (qty ? '' : ' active'),
22543             cn : [
22544                 {
22545                     tag : 'a',
22546                     href:'#',
22547                     html : pane.title
22548                 }
22549             ]
22550         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22551         pane.tab = tab;
22552         
22553         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22554         if (!qty) {
22555             pane.el.addClass('active');
22556         }
22557         
22558                 
22559     },
22560     onTabClick : function(ev,un,ob,pane)
22561     {
22562         //Roo.log('tab - prev default');
22563         ev.preventDefault();
22564         
22565         
22566         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22567         pane.tab.addClass('active');
22568         //Roo.log(pane.title);
22569         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22570         // technically we should have a deactivate event.. but maybe add later.
22571         // and it should not de-activate the selected tab...
22572         this.fireEvent('activatepane', pane);
22573         pane.el.addClass('active');
22574         pane.fireEvent('activate');
22575         
22576         
22577     },
22578     
22579     getActivePane : function()
22580     {
22581         var r = false;
22582         Roo.each(this.panes, function(p) {
22583             if(p.el.hasClass('active')){
22584                 r = p;
22585                 return false;
22586             }
22587             
22588             return;
22589         });
22590         
22591         return r;
22592     }
22593     
22594     
22595 });
22596
22597  
22598 /*
22599  * - LGPL
22600  *
22601  * Tab pane
22602  * 
22603  */
22604 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22605 /**
22606  * @class Roo.bootstrap.TabPane
22607  * @extends Roo.bootstrap.Component
22608  * Bootstrap TabPane class
22609  * @cfg {Boolean} active (false | true) Default false
22610  * @cfg {String} title title of panel
22611
22612  * 
22613  * @constructor
22614  * Create a new TabPane
22615  * @param {Object} config The config object
22616  */
22617
22618 Roo.bootstrap.dash.TabPane = function(config){
22619     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22620     
22621     this.addEvents({
22622         // raw events
22623         /**
22624          * @event activate
22625          * When a pane is activated
22626          * @param {Roo.bootstrap.dash.TabPane} pane
22627          */
22628         "activate" : true
22629          
22630     });
22631 };
22632
22633 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22634     
22635     active : false,
22636     title : '',
22637     
22638     // the tabBox that this is attached to.
22639     tab : false,
22640      
22641     getAutoCreate : function() 
22642     {
22643         var cfg = {
22644             tag: 'div',
22645             cls: 'tab-pane'
22646         }
22647         
22648         if(this.active){
22649             cfg.cls += ' active';
22650         }
22651         
22652         return cfg;
22653     },
22654     initEvents  : function()
22655     {
22656         //Roo.log('trigger add pane handler');
22657         this.parent().fireEvent('addpane', this)
22658     },
22659     
22660      /**
22661      * Updates the tab title 
22662      * @param {String} html to set the title to.
22663      */
22664     setTitle: function(str)
22665     {
22666         if (!this.tab) {
22667             return;
22668         }
22669         this.title = str;
22670         this.tab.select('a', true).first().dom.innerHTML = str;
22671         
22672     }
22673     
22674     
22675     
22676 });
22677
22678  
22679
22680
22681  /*
22682  * - LGPL
22683  *
22684  * menu
22685  * 
22686  */
22687 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22688
22689 /**
22690  * @class Roo.bootstrap.menu.Menu
22691  * @extends Roo.bootstrap.Component
22692  * Bootstrap Menu class - container for Menu
22693  * @cfg {String} html Text of the menu
22694  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22695  * @cfg {String} icon Font awesome icon
22696  * @cfg {String} pos Menu align to (top | bottom) default bottom
22697  * 
22698  * 
22699  * @constructor
22700  * Create a new Menu
22701  * @param {Object} config The config object
22702  */
22703
22704
22705 Roo.bootstrap.menu.Menu = function(config){
22706     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22707     
22708     this.addEvents({
22709         /**
22710          * @event beforeshow
22711          * Fires before this menu is displayed
22712          * @param {Roo.bootstrap.menu.Menu} this
22713          */
22714         beforeshow : true,
22715         /**
22716          * @event beforehide
22717          * Fires before this menu is hidden
22718          * @param {Roo.bootstrap.menu.Menu} this
22719          */
22720         beforehide : true,
22721         /**
22722          * @event show
22723          * Fires after this menu is displayed
22724          * @param {Roo.bootstrap.menu.Menu} this
22725          */
22726         show : true,
22727         /**
22728          * @event hide
22729          * Fires after this menu is hidden
22730          * @param {Roo.bootstrap.menu.Menu} this
22731          */
22732         hide : true,
22733         /**
22734          * @event click
22735          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22736          * @param {Roo.bootstrap.menu.Menu} this
22737          * @param {Roo.EventObject} e
22738          */
22739         click : true
22740     });
22741     
22742 };
22743
22744 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22745     
22746     submenu : false,
22747     html : '',
22748     weight : 'default',
22749     icon : false,
22750     pos : 'bottom',
22751     
22752     
22753     getChildContainer : function() {
22754         if(this.isSubMenu){
22755             return this.el;
22756         }
22757         
22758         return this.el.select('ul.dropdown-menu', true).first();  
22759     },
22760     
22761     getAutoCreate : function()
22762     {
22763         var text = [
22764             {
22765                 tag : 'span',
22766                 cls : 'roo-menu-text',
22767                 html : this.html
22768             }
22769         ];
22770         
22771         if(this.icon){
22772             text.unshift({
22773                 tag : 'i',
22774                 cls : 'fa ' + this.icon
22775             })
22776         }
22777         
22778         
22779         var cfg = {
22780             tag : 'div',
22781             cls : 'btn-group',
22782             cn : [
22783                 {
22784                     tag : 'button',
22785                     cls : 'dropdown-button btn btn-' + this.weight,
22786                     cn : text
22787                 },
22788                 {
22789                     tag : 'button',
22790                     cls : 'dropdown-toggle btn btn-' + this.weight,
22791                     cn : [
22792                         {
22793                             tag : 'span',
22794                             cls : 'caret'
22795                         }
22796                     ]
22797                 },
22798                 {
22799                     tag : 'ul',
22800                     cls : 'dropdown-menu'
22801                 }
22802             ]
22803             
22804         };
22805         
22806         if(this.pos == 'top'){
22807             cfg.cls += ' dropup';
22808         }
22809         
22810         if(this.isSubMenu){
22811             cfg = {
22812                 tag : 'ul',
22813                 cls : 'dropdown-menu'
22814             }
22815         }
22816         
22817         return cfg;
22818     },
22819     
22820     onRender : function(ct, position)
22821     {
22822         this.isSubMenu = ct.hasClass('dropdown-submenu');
22823         
22824         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22825     },
22826     
22827     initEvents : function() 
22828     {
22829         if(this.isSubMenu){
22830             return;
22831         }
22832         
22833         this.hidden = true;
22834         
22835         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22836         this.triggerEl.on('click', this.onTriggerPress, this);
22837         
22838         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22839         this.buttonEl.on('click', this.onClick, this);
22840         
22841     },
22842     
22843     list : function()
22844     {
22845         if(this.isSubMenu){
22846             return this.el;
22847         }
22848         
22849         return this.el.select('ul.dropdown-menu', true).first();
22850     },
22851     
22852     onClick : function(e)
22853     {
22854         this.fireEvent("click", this, e);
22855     },
22856     
22857     onTriggerPress  : function(e)
22858     {   
22859         if (this.isVisible()) {
22860             this.hide();
22861         } else {
22862             this.show();
22863         }
22864     },
22865     
22866     isVisible : function(){
22867         return !this.hidden;
22868     },
22869     
22870     show : function()
22871     {
22872         this.fireEvent("beforeshow", this);
22873         
22874         this.hidden = false;
22875         this.el.addClass('open');
22876         
22877         Roo.get(document).on("mouseup", this.onMouseUp, this);
22878         
22879         this.fireEvent("show", this);
22880         
22881         
22882     },
22883     
22884     hide : function()
22885     {
22886         this.fireEvent("beforehide", this);
22887         
22888         this.hidden = true;
22889         this.el.removeClass('open');
22890         
22891         Roo.get(document).un("mouseup", this.onMouseUp);
22892         
22893         this.fireEvent("hide", this);
22894     },
22895     
22896     onMouseUp : function()
22897     {
22898         this.hide();
22899     }
22900     
22901 });
22902
22903  
22904  /*
22905  * - LGPL
22906  *
22907  * menu item
22908  * 
22909  */
22910 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22911
22912 /**
22913  * @class Roo.bootstrap.menu.Item
22914  * @extends Roo.bootstrap.Component
22915  * Bootstrap MenuItem class
22916  * @cfg {Boolean} submenu (true | false) default false
22917  * @cfg {String} html text of the item
22918  * @cfg {String} href the link
22919  * @cfg {Boolean} disable (true | false) default false
22920  * @cfg {Boolean} preventDefault (true | false) default true
22921  * @cfg {String} icon Font awesome icon
22922  * @cfg {String} pos Submenu align to (left | right) default right 
22923  * 
22924  * 
22925  * @constructor
22926  * Create a new Item
22927  * @param {Object} config The config object
22928  */
22929
22930
22931 Roo.bootstrap.menu.Item = function(config){
22932     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22933     this.addEvents({
22934         /**
22935          * @event mouseover
22936          * Fires when the mouse is hovering over this menu
22937          * @param {Roo.bootstrap.menu.Item} this
22938          * @param {Roo.EventObject} e
22939          */
22940         mouseover : true,
22941         /**
22942          * @event mouseout
22943          * Fires when the mouse exits this menu
22944          * @param {Roo.bootstrap.menu.Item} this
22945          * @param {Roo.EventObject} e
22946          */
22947         mouseout : true,
22948         // raw events
22949         /**
22950          * @event click
22951          * The raw click event for the entire grid.
22952          * @param {Roo.EventObject} e
22953          */
22954         click : true
22955     });
22956 };
22957
22958 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22959     
22960     submenu : false,
22961     href : '',
22962     html : '',
22963     preventDefault: true,
22964     disable : false,
22965     icon : false,
22966     pos : 'right',
22967     
22968     getAutoCreate : function()
22969     {
22970         var text = [
22971             {
22972                 tag : 'span',
22973                 cls : 'roo-menu-item-text',
22974                 html : this.html
22975             }
22976         ];
22977         
22978         if(this.icon){
22979             text.unshift({
22980                 tag : 'i',
22981                 cls : 'fa ' + this.icon
22982             })
22983         }
22984         
22985         var cfg = {
22986             tag : 'li',
22987             cn : [
22988                 {
22989                     tag : 'a',
22990                     href : this.href || '#',
22991                     cn : text
22992                 }
22993             ]
22994         };
22995         
22996         if(this.disable){
22997             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22998         }
22999         
23000         if(this.submenu){
23001             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23002             
23003             if(this.pos == 'left'){
23004                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23005             }
23006         }
23007         
23008         return cfg;
23009     },
23010     
23011     initEvents : function() 
23012     {
23013         this.el.on('mouseover', this.onMouseOver, this);
23014         this.el.on('mouseout', this.onMouseOut, this);
23015         
23016         this.el.select('a', true).first().on('click', this.onClick, this);
23017         
23018     },
23019     
23020     onClick : function(e)
23021     {
23022         if(this.preventDefault){
23023             e.preventDefault();
23024         }
23025         
23026         this.fireEvent("click", this, e);
23027     },
23028     
23029     onMouseOver : function(e)
23030     {
23031         if(this.submenu && this.pos == 'left'){
23032             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23033         }
23034         
23035         this.fireEvent("mouseover", this, e);
23036     },
23037     
23038     onMouseOut : function(e)
23039     {
23040         this.fireEvent("mouseout", this, e);
23041     }
23042 });
23043
23044  
23045
23046  /*
23047  * - LGPL
23048  *
23049  * menu separator
23050  * 
23051  */
23052 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23053
23054 /**
23055  * @class Roo.bootstrap.menu.Separator
23056  * @extends Roo.bootstrap.Component
23057  * Bootstrap Separator class
23058  * 
23059  * @constructor
23060  * Create a new Separator
23061  * @param {Object} config The config object
23062  */
23063
23064
23065 Roo.bootstrap.menu.Separator = function(config){
23066     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23067 };
23068
23069 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23070     
23071     getAutoCreate : function(){
23072         var cfg = {
23073             tag : 'li',
23074             cls: 'divider'
23075         };
23076         
23077         return cfg;
23078     }
23079    
23080 });
23081
23082  
23083
23084  /*
23085  * - LGPL
23086  *
23087  * Tooltip
23088  * 
23089  */
23090
23091 /**
23092  * @class Roo.bootstrap.Tooltip
23093  * Bootstrap Tooltip class
23094  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23095  * to determine which dom element triggers the tooltip.
23096  * 
23097  * It needs to add support for additional attributes like tooltip-position
23098  * 
23099  * @constructor
23100  * Create a new Toolti
23101  * @param {Object} config The config object
23102  */
23103
23104 Roo.bootstrap.Tooltip = function(config){
23105     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23106 };
23107
23108 Roo.apply(Roo.bootstrap.Tooltip, {
23109     /**
23110      * @function init initialize tooltip monitoring.
23111      * @static
23112      */
23113     currentEl : false,
23114     currentTip : false,
23115     currentRegion : false,
23116     
23117     //  init : delay?
23118     
23119     init : function()
23120     {
23121         Roo.get(document).on('mouseover', this.enter ,this);
23122         Roo.get(document).on('mouseout', this.leave, this);
23123          
23124         
23125         this.currentTip = new Roo.bootstrap.Tooltip();
23126     },
23127     
23128     enter : function(ev)
23129     {
23130         var dom = ev.getTarget();
23131         
23132         //Roo.log(['enter',dom]);
23133         var el = Roo.fly(dom);
23134         if (this.currentEl) {
23135             //Roo.log(dom);
23136             //Roo.log(this.currentEl);
23137             //Roo.log(this.currentEl.contains(dom));
23138             if (this.currentEl == el) {
23139                 return;
23140             }
23141             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23142                 return;
23143             }
23144
23145         }
23146         
23147         
23148         
23149         if (this.currentTip.el) {
23150             this.currentTip.el.hide(); // force hiding...
23151         }    
23152         //Roo.log(ev);
23153         var bindEl = el;
23154         
23155         // you can not look for children, as if el is the body.. then everythign is the child..
23156         if (!el.attr('tooltip')) { //
23157             if (!el.select("[tooltip]").elements.length) {
23158                 return;
23159             }
23160             // is the mouse over this child...?
23161             bindEl = el.select("[tooltip]").first();
23162             var xy = ev.getXY();
23163             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23164                 //Roo.log("not in region.");
23165                 return;
23166             }
23167             //Roo.log("child element over..");
23168             
23169         }
23170         this.currentEl = bindEl;
23171         this.currentTip.bind(bindEl);
23172         this.currentRegion = Roo.lib.Region.getRegion(dom);
23173         this.currentTip.enter();
23174         
23175     },
23176     leave : function(ev)
23177     {
23178         var dom = ev.getTarget();
23179         //Roo.log(['leave',dom]);
23180         if (!this.currentEl) {
23181             return;
23182         }
23183         
23184         
23185         if (dom != this.currentEl.dom) {
23186             return;
23187         }
23188         var xy = ev.getXY();
23189         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23190             return;
23191         }
23192         // only activate leave if mouse cursor is outside... bounding box..
23193         
23194         
23195         
23196         
23197         if (this.currentTip) {
23198             this.currentTip.leave();
23199         }
23200         //Roo.log('clear currentEl');
23201         this.currentEl = false;
23202         
23203         
23204     },
23205     alignment : {
23206         'left' : ['r-l', [-2,0], 'right'],
23207         'right' : ['l-r', [2,0], 'left'],
23208         'bottom' : ['t-b', [0,2], 'top'],
23209         'top' : [ 'b-t', [0,-2], 'bottom']
23210     }
23211     
23212 });
23213
23214
23215 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23216     
23217     
23218     bindEl : false,
23219     
23220     delay : null, // can be { show : 300 , hide: 500}
23221     
23222     timeout : null,
23223     
23224     hoverState : null, //???
23225     
23226     placement : 'bottom', 
23227     
23228     getAutoCreate : function(){
23229     
23230         var cfg = {
23231            cls : 'tooltip',
23232            role : 'tooltip',
23233            cn : [
23234                 {
23235                     cls : 'tooltip-arrow'
23236                 },
23237                 {
23238                     cls : 'tooltip-inner'
23239                 }
23240            ]
23241         };
23242         
23243         return cfg;
23244     },
23245     bind : function(el)
23246     {
23247         this.bindEl = el;
23248     },
23249       
23250     
23251     enter : function () {
23252        
23253         if (this.timeout != null) {
23254             clearTimeout(this.timeout);
23255         }
23256         
23257         this.hoverState = 'in';
23258          //Roo.log("enter - show");
23259         if (!this.delay || !this.delay.show) {
23260             this.show();
23261             return;
23262         }
23263         var _t = this;
23264         this.timeout = setTimeout(function () {
23265             if (_t.hoverState == 'in') {
23266                 _t.show();
23267             }
23268         }, this.delay.show);
23269     },
23270     leave : function()
23271     {
23272         clearTimeout(this.timeout);
23273     
23274         this.hoverState = 'out';
23275          if (!this.delay || !this.delay.hide) {
23276             this.hide();
23277             return;
23278         }
23279        
23280         var _t = this;
23281         this.timeout = setTimeout(function () {
23282             //Roo.log("leave - timeout");
23283             
23284             if (_t.hoverState == 'out') {
23285                 _t.hide();
23286                 Roo.bootstrap.Tooltip.currentEl = false;
23287             }
23288         }, delay);
23289     },
23290     
23291     show : function ()
23292     {
23293         if (!this.el) {
23294             this.render(document.body);
23295         }
23296         // set content.
23297         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23298         
23299         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23300         
23301         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23302         
23303         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23304         
23305         var placement = typeof this.placement == 'function' ?
23306             this.placement.call(this, this.el, on_el) :
23307             this.placement;
23308             
23309         var autoToken = /\s?auto?\s?/i;
23310         var autoPlace = autoToken.test(placement);
23311         if (autoPlace) {
23312             placement = placement.replace(autoToken, '') || 'top';
23313         }
23314         
23315         //this.el.detach()
23316         //this.el.setXY([0,0]);
23317         this.el.show();
23318         //this.el.dom.style.display='block';
23319         this.el.addClass(placement);
23320         
23321         //this.el.appendTo(on_el);
23322         
23323         var p = this.getPosition();
23324         var box = this.el.getBox();
23325         
23326         if (autoPlace) {
23327             // fixme..
23328         }
23329         var align = Roo.bootstrap.Tooltip.alignment[placement];
23330         this.el.alignTo(this.bindEl, align[0],align[1]);
23331         //var arrow = this.el.select('.arrow',true).first();
23332         //arrow.set(align[2], 
23333         
23334         this.el.addClass('in fade');
23335         this.hoverState = null;
23336         
23337         if (this.el.hasClass('fade')) {
23338             // fade it?
23339         }
23340         
23341     },
23342     hide : function()
23343     {
23344          
23345         if (!this.el) {
23346             return;
23347         }
23348         //this.el.setXY([0,0]);
23349         this.el.removeClass('in');
23350         //this.el.hide();
23351         
23352     }
23353     
23354 });
23355  
23356
23357  /*
23358  * - LGPL
23359  *
23360  * Location Picker
23361  * 
23362  */
23363
23364 /**
23365  * @class Roo.bootstrap.LocationPicker
23366  * @extends Roo.bootstrap.Component
23367  * Bootstrap LocationPicker class
23368  * @cfg {Number} latitude Position when init default 0
23369  * @cfg {Number} longitude Position when init default 0
23370  * @cfg {Number} zoom default 15
23371  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23372  * @cfg {Boolean} mapTypeControl default false
23373  * @cfg {Boolean} disableDoubleClickZoom default false
23374  * @cfg {Boolean} scrollwheel default true
23375  * @cfg {Boolean} streetViewControl default false
23376  * @cfg {Number} radius default 0
23377  * @cfg {String} locationName
23378  * @cfg {Boolean} draggable default true
23379  * @cfg {Boolean} enableAutocomplete default false
23380  * @cfg {Boolean} enableReverseGeocode default true
23381  * @cfg {String} markerTitle
23382  * 
23383  * @constructor
23384  * Create a new LocationPicker
23385  * @param {Object} config The config object
23386  */
23387
23388
23389 Roo.bootstrap.LocationPicker = function(config){
23390     
23391     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23392     
23393     this.addEvents({
23394         /**
23395          * @event initial
23396          * Fires when the picker initialized.
23397          * @param {Roo.bootstrap.LocationPicker} this
23398          * @param {Google Location} location
23399          */
23400         initial : true,
23401         /**
23402          * @event positionchanged
23403          * Fires when the picker position changed.
23404          * @param {Roo.bootstrap.LocationPicker} this
23405          * @param {Google Location} location
23406          */
23407         positionchanged : true,
23408         /**
23409          * @event resize
23410          * Fires when the map resize.
23411          * @param {Roo.bootstrap.LocationPicker} this
23412          */
23413         resize : true,
23414         /**
23415          * @event show
23416          * Fires when the map show.
23417          * @param {Roo.bootstrap.LocationPicker} this
23418          */
23419         show : true,
23420         /**
23421          * @event hide
23422          * Fires when the map hide.
23423          * @param {Roo.bootstrap.LocationPicker} this
23424          */
23425         hide : true,
23426         /**
23427          * @event mapClick
23428          * Fires when click the map.
23429          * @param {Roo.bootstrap.LocationPicker} this
23430          * @param {Map event} e
23431          */
23432         mapClick : true,
23433         /**
23434          * @event mapRightClick
23435          * Fires when right click the map.
23436          * @param {Roo.bootstrap.LocationPicker} this
23437          * @param {Map event} e
23438          */
23439         mapRightClick : true,
23440         /**
23441          * @event markerClick
23442          * Fires when click the marker.
23443          * @param {Roo.bootstrap.LocationPicker} this
23444          * @param {Map event} e
23445          */
23446         markerClick : true,
23447         /**
23448          * @event markerRightClick
23449          * Fires when right click the marker.
23450          * @param {Roo.bootstrap.LocationPicker} this
23451          * @param {Map event} e
23452          */
23453         markerRightClick : true,
23454         /**
23455          * @event OverlayViewDraw
23456          * Fires when OverlayView Draw
23457          * @param {Roo.bootstrap.LocationPicker} this
23458          */
23459         OverlayViewDraw : true,
23460         /**
23461          * @event OverlayViewOnAdd
23462          * Fires when OverlayView Draw
23463          * @param {Roo.bootstrap.LocationPicker} this
23464          */
23465         OverlayViewOnAdd : true,
23466         /**
23467          * @event OverlayViewOnRemove
23468          * Fires when OverlayView Draw
23469          * @param {Roo.bootstrap.LocationPicker} this
23470          */
23471         OverlayViewOnRemove : true,
23472         /**
23473          * @event OverlayViewShow
23474          * Fires when OverlayView Draw
23475          * @param {Roo.bootstrap.LocationPicker} this
23476          * @param {Pixel} cpx
23477          */
23478         OverlayViewShow : true,
23479         /**
23480          * @event OverlayViewHide
23481          * Fires when OverlayView Draw
23482          * @param {Roo.bootstrap.LocationPicker} this
23483          */
23484         OverlayViewHide : true
23485     });
23486         
23487 };
23488
23489 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23490     
23491     gMapContext: false,
23492     
23493     latitude: 0,
23494     longitude: 0,
23495     zoom: 15,
23496     mapTypeId: false,
23497     mapTypeControl: false,
23498     disableDoubleClickZoom: false,
23499     scrollwheel: true,
23500     streetViewControl: false,
23501     radius: 0,
23502     locationName: '',
23503     draggable: true,
23504     enableAutocomplete: false,
23505     enableReverseGeocode: true,
23506     markerTitle: '',
23507     
23508     getAutoCreate: function()
23509     {
23510
23511         var cfg = {
23512             tag: 'div',
23513             cls: 'roo-location-picker'
23514         };
23515         
23516         return cfg
23517     },
23518     
23519     initEvents: function(ct, position)
23520     {       
23521         if(!this.el.getWidth() || this.isApplied()){
23522             return;
23523         }
23524         
23525         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23526         
23527         this.initial();
23528     },
23529     
23530     initial: function()
23531     {
23532         if(!this.mapTypeId){
23533             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23534         }
23535         
23536         this.gMapContext = this.GMapContext();
23537         
23538         this.initOverlayView();
23539         
23540         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23541         
23542         var _this = this;
23543                 
23544         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23545             _this.setPosition(_this.gMapContext.marker.position);
23546         });
23547         
23548         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23549             _this.fireEvent('mapClick', this, event);
23550             
23551         });
23552
23553         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23554             _this.fireEvent('mapRightClick', this, event);
23555             
23556         });
23557         
23558         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23559             _this.fireEvent('markerClick', this, event);
23560             
23561         });
23562
23563         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23564             _this.fireEvent('markerRightClick', this, event);
23565             
23566         });
23567         
23568         this.setPosition(this.gMapContext.location);
23569         
23570         this.fireEvent('initial', this, this.gMapContext.location);
23571     },
23572     
23573     initOverlayView: function()
23574     {
23575         var _this = this;
23576         
23577         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23578             
23579             draw: function()
23580             {
23581                 _this.fireEvent('OverlayViewDraw', _this);
23582             },
23583             
23584             onAdd: function()
23585             {
23586                 _this.fireEvent('OverlayViewOnAdd', _this);
23587             },
23588             
23589             onRemove: function()
23590             {
23591                 _this.fireEvent('OverlayViewOnRemove', _this);
23592             },
23593             
23594             show: function(cpx)
23595             {
23596                 _this.fireEvent('OverlayViewShow', _this, cpx);
23597             },
23598             
23599             hide: function()
23600             {
23601                 _this.fireEvent('OverlayViewHide', _this);
23602             }
23603             
23604         });
23605     },
23606     
23607     fromLatLngToContainerPixel: function(event)
23608     {
23609         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23610     },
23611     
23612     isApplied: function() 
23613     {
23614         return this.getGmapContext() == false ? false : true;
23615     },
23616     
23617     getGmapContext: function() 
23618     {
23619         return this.gMapContext
23620     },
23621     
23622     GMapContext: function() 
23623     {
23624         var position = new google.maps.LatLng(this.latitude, this.longitude);
23625         
23626         var _map = new google.maps.Map(this.el.dom, {
23627             center: position,
23628             zoom: this.zoom,
23629             mapTypeId: this.mapTypeId,
23630             mapTypeControl: this.mapTypeControl,
23631             disableDoubleClickZoom: this.disableDoubleClickZoom,
23632             scrollwheel: this.scrollwheel,
23633             streetViewControl: this.streetViewControl,
23634             locationName: this.locationName,
23635             draggable: this.draggable,
23636             enableAutocomplete: this.enableAutocomplete,
23637             enableReverseGeocode: this.enableReverseGeocode
23638         });
23639         
23640         var _marker = new google.maps.Marker({
23641             position: position,
23642             map: _map,
23643             title: this.markerTitle,
23644             draggable: this.draggable
23645         });
23646         
23647         return {
23648             map: _map,
23649             marker: _marker,
23650             circle: null,
23651             location: position,
23652             radius: this.radius,
23653             locationName: this.locationName,
23654             addressComponents: {
23655                 formatted_address: null,
23656                 addressLine1: null,
23657                 addressLine2: null,
23658                 streetName: null,
23659                 streetNumber: null,
23660                 city: null,
23661                 district: null,
23662                 state: null,
23663                 stateOrProvince: null
23664             },
23665             settings: this,
23666             domContainer: this.el.dom,
23667             geodecoder: new google.maps.Geocoder()
23668         };
23669     },
23670     
23671     drawCircle: function(center, radius, options) 
23672     {
23673         if (this.gMapContext.circle != null) {
23674             this.gMapContext.circle.setMap(null);
23675         }
23676         if (radius > 0) {
23677             radius *= 1;
23678             options = Roo.apply({}, options, {
23679                 strokeColor: "#0000FF",
23680                 strokeOpacity: .35,
23681                 strokeWeight: 2,
23682                 fillColor: "#0000FF",
23683                 fillOpacity: .2
23684             });
23685             
23686             options.map = this.gMapContext.map;
23687             options.radius = radius;
23688             options.center = center;
23689             this.gMapContext.circle = new google.maps.Circle(options);
23690             return this.gMapContext.circle;
23691         }
23692         
23693         return null;
23694     },
23695     
23696     setPosition: function(location) 
23697     {
23698         this.gMapContext.location = location;
23699         this.gMapContext.marker.setPosition(location);
23700         this.gMapContext.map.panTo(location);
23701         this.drawCircle(location, this.gMapContext.radius, {});
23702         
23703         var _this = this;
23704         
23705         if (this.gMapContext.settings.enableReverseGeocode) {
23706             this.gMapContext.geodecoder.geocode({
23707                 latLng: this.gMapContext.location
23708             }, function(results, status) {
23709                 
23710                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23711                     _this.gMapContext.locationName = results[0].formatted_address;
23712                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23713                     
23714                     _this.fireEvent('positionchanged', this, location);
23715                 }
23716             });
23717             
23718             return;
23719         }
23720         
23721         this.fireEvent('positionchanged', this, location);
23722     },
23723     
23724     resize: function()
23725     {
23726         google.maps.event.trigger(this.gMapContext.map, "resize");
23727         
23728         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23729         
23730         this.fireEvent('resize', this);
23731     },
23732     
23733     setPositionByLatLng: function(latitude, longitude)
23734     {
23735         this.setPosition(new google.maps.LatLng(latitude, longitude));
23736     },
23737     
23738     getCurrentPosition: function() 
23739     {
23740         return {
23741             latitude: this.gMapContext.location.lat(),
23742             longitude: this.gMapContext.location.lng()
23743         };
23744     },
23745     
23746     getAddressName: function() 
23747     {
23748         return this.gMapContext.locationName;
23749     },
23750     
23751     getAddressComponents: function() 
23752     {
23753         return this.gMapContext.addressComponents;
23754     },
23755     
23756     address_component_from_google_geocode: function(address_components) 
23757     {
23758         var result = {};
23759         
23760         for (var i = 0; i < address_components.length; i++) {
23761             var component = address_components[i];
23762             if (component.types.indexOf("postal_code") >= 0) {
23763                 result.postalCode = component.short_name;
23764             } else if (component.types.indexOf("street_number") >= 0) {
23765                 result.streetNumber = component.short_name;
23766             } else if (component.types.indexOf("route") >= 0) {
23767                 result.streetName = component.short_name;
23768             } else if (component.types.indexOf("neighborhood") >= 0) {
23769                 result.city = component.short_name;
23770             } else if (component.types.indexOf("locality") >= 0) {
23771                 result.city = component.short_name;
23772             } else if (component.types.indexOf("sublocality") >= 0) {
23773                 result.district = component.short_name;
23774             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23775                 result.stateOrProvince = component.short_name;
23776             } else if (component.types.indexOf("country") >= 0) {
23777                 result.country = component.short_name;
23778             }
23779         }
23780         
23781         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23782         result.addressLine2 = "";
23783         return result;
23784     },
23785     
23786     setZoomLevel: function(zoom)
23787     {
23788         this.gMapContext.map.setZoom(zoom);
23789     },
23790     
23791     show: function()
23792     {
23793         if(!this.el){
23794             return;
23795         }
23796         
23797         this.el.show();
23798         
23799         this.resize();
23800         
23801         this.fireEvent('show', this);
23802     },
23803     
23804     hide: function()
23805     {
23806         if(!this.el){
23807             return;
23808         }
23809         
23810         this.el.hide();
23811         
23812         this.fireEvent('hide', this);
23813     }
23814     
23815 });
23816
23817 Roo.apply(Roo.bootstrap.LocationPicker, {
23818     
23819     OverlayView : function(map, options)
23820     {
23821         options = options || {};
23822         
23823         this.setMap(map);
23824     }
23825     
23826     
23827 });/*
23828  * - LGPL
23829  *
23830  * Alert
23831  * 
23832  */
23833
23834 /**
23835  * @class Roo.bootstrap.Alert
23836  * @extends Roo.bootstrap.Component
23837  * Bootstrap Alert class
23838  * @cfg {String} title The title of alert
23839  * @cfg {String} html The content of alert
23840  * @cfg {String} weight (  success | info | warning | danger )
23841  * @cfg {String} faicon font-awesomeicon
23842  * 
23843  * @constructor
23844  * Create a new alert
23845  * @param {Object} config The config object
23846  */
23847
23848
23849 Roo.bootstrap.Alert = function(config){
23850     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23851     
23852 };
23853
23854 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23855     
23856     title: '',
23857     html: '',
23858     weight: false,
23859     faicon: false,
23860     
23861     getAutoCreate : function()
23862     {
23863         
23864         var cfg = {
23865             tag : 'div',
23866             cls : 'alert',
23867             cn : [
23868                 {
23869                     tag : 'i',
23870                     cls : 'roo-alert-icon'
23871                     
23872                 },
23873                 {
23874                     tag : 'b',
23875                     cls : 'roo-alert-title',
23876                     html : this.title
23877                 },
23878                 {
23879                     tag : 'span',
23880                     cls : 'roo-alert-text',
23881                     html : this.html
23882                 }
23883             ]
23884         };
23885         
23886         if(this.faicon){
23887             cfg.cn[0].cls += ' fa ' + this.faicon;
23888         }
23889         
23890         if(this.weight){
23891             cfg.cls += ' alert-' + this.weight;
23892         }
23893         
23894         return cfg;
23895     },
23896     
23897     initEvents: function() 
23898     {
23899         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23900     },
23901     
23902     setTitle : function(str)
23903     {
23904         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23905     },
23906     
23907     setText : function(str)
23908     {
23909         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23910     },
23911     
23912     setWeight : function(weight)
23913     {
23914         if(this.weight){
23915             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23916         }
23917         
23918         this.weight = weight;
23919         
23920         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23921     },
23922     
23923     setIcon : function(icon)
23924     {
23925         if(this.faicon){
23926             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23927         }
23928         
23929         this.faicon = icon
23930         
23931         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23932     },
23933     
23934     hide: function() 
23935     {
23936         this.el.hide();   
23937     },
23938     
23939     show: function() 
23940     {  
23941         this.el.show();   
23942     }
23943     
23944 });
23945
23946  
23947 /*
23948 * Licence: LGPL
23949 */
23950
23951 /**
23952  * @class Roo.bootstrap.UploadCropbox
23953  * @extends Roo.bootstrap.Component
23954  * Bootstrap UploadCropbox class
23955  * @cfg {String} emptyText show when image has been loaded
23956  * @cfg {Number} minWidth default 300
23957  * @cfg {Number} minHeight default 300
23958  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23959  * 
23960  * @constructor
23961  * Create a new UploadCropbox
23962  * @param {Object} config The config object
23963  */
23964
23965 Roo.bootstrap.UploadCropbox = function(config){
23966     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23967     
23968     this.addEvents({
23969         /**
23970          * @event beforeselectfile
23971          * Fire before select file
23972          * @param {Roo.bootstrap.UploadCropbox} this
23973          */
23974         "beforeselectfile" : true,
23975         /**
23976          * @event initial
23977          * Fire after initEvent
23978          * @param {Roo.bootstrap.UploadCropbox} this
23979          */
23980         "initial" : true,
23981         /**
23982          * @event crop
23983          * Fire after initEvent
23984          * @param {Roo.bootstrap.UploadCropbox} this
23985          * @param {String} data
23986          */
23987         "crop" : true,
23988         /**
23989          * @event prepare
23990          * Fire when preparing the file data
23991          * @param {Roo.bootstrap.UploadCropbox} this
23992          * @param {Object} file
23993          */
23994         "prepare" : true,
23995         /**
23996          * @event exception
23997          * Fire when get exception
23998          * @param {Roo.bootstrap.UploadCropbox} this
23999          * @param {Object} options
24000          */
24001         "exception" : true,
24002         /**
24003          * @event beforeloadcanvas
24004          * Fire before load the canvas
24005          * @param {Roo.bootstrap.UploadCropbox} this
24006          * @param {String} src
24007          */
24008         "beforeloadcanvas" : true,
24009         /**
24010          * @event trash
24011          * Fire when trash image
24012          * @param {Roo.bootstrap.UploadCropbox} this
24013          */
24014         "trash" : true,
24015         /**
24016          * @event download
24017          * Fire when download the image
24018          * @param {Roo.bootstrap.UploadCropbox} this
24019          */
24020         "download" : true,
24021         /**
24022          * @event footerbuttonclick
24023          * Fire when footerbuttonclick
24024          * @param {Roo.bootstrap.UploadCropbox} this
24025          * @param {String} type
24026          */
24027         "footerbuttonclick" : true,
24028         /**
24029          * @event resize
24030          * Fire when resize
24031          * @param {Roo.bootstrap.UploadCropbox} this
24032          */
24033         "resize" : true
24034         
24035     });
24036     
24037     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24038 };
24039
24040 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24041     
24042     emptyText : 'Click to upload image',
24043     scale : 0,
24044     baseScale : 1,
24045     rotate : 0,
24046     dragable : false,
24047     pinching : false,
24048     mouseX : 0,
24049     mouseY : 0,
24050     cropData : false,
24051     minWidth : 300,
24052     minHeight : 300,
24053     file : false,
24054     exif : {},
24055     baseRotate : 1,
24056     cropType : 'image/jpeg',
24057     buttons : false,
24058     
24059     getAutoCreate : function()
24060     {
24061         var cfg = {
24062             tag : 'div',
24063             cls : 'roo-upload-cropbox',
24064             cn : [
24065                 {
24066                     tag : 'div',
24067                     cls : 'roo-upload-cropbox-body',
24068                     cn : [
24069                         {
24070                             tag : 'div',
24071                             cls : 'roo-upload-cropbox-preview'
24072                         },
24073                         {
24074                             tag : 'div',
24075                             cls : 'roo-upload-cropbox-thumb'
24076                         },
24077                         {
24078                             tag : 'div',
24079                             cls : 'roo-upload-cropbox-empty-notify',
24080                             html : this.emptyText
24081                         }
24082                     ]
24083                 },
24084                 {
24085                     tag : 'div',
24086                     cls : 'roo-upload-cropbox-footer',
24087                     cn : {
24088                         tag : 'div',
24089                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24090                         cn : []
24091                     }
24092                 }
24093             ]
24094         };
24095         
24096         return cfg;
24097     },
24098     
24099     onRender : function(ct, position)
24100     {
24101         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24102         
24103         if (this.buttons.length) {
24104             
24105             Roo.each(this.buttons, function(bb) {
24106                 
24107                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24108                 
24109                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24110                 
24111             }, this);
24112         }
24113     },
24114     
24115     initEvents : function()
24116     {
24117         this.urlAPI = (window.createObjectURL && window) || 
24118                                 (window.URL && URL.revokeObjectURL && URL) || 
24119                                 (window.webkitURL && webkitURL);
24120                         
24121         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24122         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24123         this.bodyHasOnClickEvent = false;
24124         
24125         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24126         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24127         
24128         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24129         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24130         this.thumbEl.hide();
24131         
24132         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24133         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24134         
24135         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24136         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24137         this.footerEl.hide();
24138         
24139         this.setThumbBoxSize();
24140         
24141         this.bind();
24142         
24143         this.resize();
24144         
24145         this.fireEvent('initial', this);
24146     },
24147
24148     bind : function()
24149     {
24150         var _this = this;
24151         
24152         window.addEventListener("resize", function() { _this.resize(); } );
24153         
24154         if(!this.bodyHasOnClickEvent){
24155             this.bodyEl.on('click', this.beforeSelectFile, this);
24156             this.bodyHasOnClickEvent = true;
24157         }
24158         
24159         if(Roo.isTouch){
24160             this.bodyEl.on('touchstart', this.onTouchStart, this);
24161             this.bodyEl.on('touchmove', this.onTouchMove, this);
24162             this.bodyEl.on('touchend', this.onTouchEnd, this);
24163         }
24164         
24165         if(!Roo.isTouch){
24166             this.bodyEl.on('mousedown', this.onMouseDown, this);
24167             this.bodyEl.on('mousemove', this.onMouseMove, this);
24168             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24169             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24170             Roo.get(document).on('mouseup', this.onMouseUp, this);
24171         }
24172     },
24173     
24174     reset : function()
24175     {    
24176         this.scale = 0;
24177         this.baseScale = 1;
24178         this.rotate = 0;
24179         this.baseRotate = 1;
24180         this.dragable = false;
24181         this.pinching = false;
24182         this.mouseX = 0;
24183         this.mouseY = 0;
24184         this.cropData = false;
24185         this.notifyEl.dom.innerHTML = this.emptyText;
24186         
24187     },
24188     
24189     resize : function()
24190     {
24191         if(this.fireEvent('resize', this) != false){
24192             this.setThumbBoxPosition();
24193             this.setCanvasPosition();
24194         }
24195     },
24196     
24197     onFooterButtonClick : function(e, el, o, type)
24198     {
24199         switch (type) {
24200             case 'rotate-left' :
24201                 this.onRotateLeft(e);
24202                 break;
24203             case 'rotate-right' :
24204                 this.onRotateRight(e);
24205                 break;
24206             case 'picture' :
24207                 this.beforeSelectFile(e);
24208                 break;
24209             case 'trash' :
24210                 this.trash(e);
24211                 break;
24212             case 'crop' :
24213                 this.crop(e);
24214                 break;
24215             case 'download' :
24216                 this.download(e);
24217                 break;
24218             default :
24219                 break;
24220         }
24221         
24222         this.fireEvent('footerbuttonclick', this, type);
24223     },
24224     
24225     beforeSelectFile : function(e)
24226     {
24227         if(e){
24228             e.preventDefault();
24229         }
24230         
24231         this.fireEvent('beforeselectfile', this);
24232     },
24233     
24234     trash : function(e)
24235     {
24236         if(e){
24237             e.preventDefault();
24238         }
24239         
24240         this.fireEvent('trash', this);
24241     },
24242     
24243     download : function(e)
24244     {
24245         if(e){
24246             e.preventDefault();
24247         }
24248         
24249         this.fireEvent('download', this);
24250     },
24251     
24252     loadCanvas : function(src)
24253     {   
24254         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24255             
24256             this.reset();
24257             
24258             this.imageEl = document.createElement('img');
24259             
24260             var _this = this;
24261             
24262             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24263             
24264             this.imageEl.src = src;
24265         }
24266     },
24267     
24268     onLoadCanvas : function()
24269     {   
24270         if(this.bodyHasOnClickEvent){
24271             this.bodyEl.un('click', this.beforeSelectFile, this);
24272             this.bodyHasOnClickEvent = false;
24273         }
24274         
24275         this.notifyEl.hide();
24276         this.thumbEl.show();
24277         this.footerEl.show();
24278         
24279         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24280         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24281         
24282         this.setThumbBoxPosition();
24283         this.baseRotateLevel();
24284         this.baseScaleLevel();
24285         
24286         this.draw();
24287         
24288         this.resize();
24289         
24290     },
24291     
24292     setCanvasPosition : function()
24293     {   
24294         if(!this.canvasEl){
24295             return;
24296         }
24297         
24298         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24299         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24300         
24301         this.previewEl.setLeft(pw);
24302         this.previewEl.setTop(ph);
24303     },
24304     
24305     onMouseDown : function(e)
24306     {   
24307         e.stopEvent();
24308         
24309         this.dragable = true;
24310         this.pinching = false;
24311         
24312         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24313         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24314         
24315     },
24316     
24317     onMouseMove : function(e)
24318     {   
24319         e.stopEvent();
24320         
24321         if(typeof(this.canvasEl) == 'undefined'){
24322             return;
24323         }
24324         
24325         if (!this.dragable){
24326             return;
24327         }
24328         
24329         var minX = Math.ceil(this.thumbEl.getLeft(true));
24330         var minY = Math.ceil(this.thumbEl.getTop(true));
24331         
24332         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24333         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24334         
24335         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24336         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24337         
24338         x = x - this.mouseX;
24339         y = y - this.mouseY;
24340         
24341         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24342         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24343         
24344         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24345         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24346         
24347         this.previewEl.setLeft(bgX);
24348         this.previewEl.setTop(bgY);
24349         
24350         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24351         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24352     },
24353     
24354     onMouseUp : function(e)
24355     {   
24356         e.stopEvent();
24357         
24358         this.dragable = false;
24359     },
24360     
24361     onMouseWheel : function(e)
24362     {   
24363         e.stopEvent();
24364         
24365         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24366         
24367         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24368         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24369         
24370         if(
24371                 e.getWheelDelta() == -1 &&
24372                 (
24373                     (
24374                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24375                     )
24376                     ||
24377                     (
24378                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24379                     )
24380                 )
24381         ){
24382             this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
24383             return;
24384         }
24385         
24386         this.draw();
24387     },
24388     
24389     onRotateLeft : function(e)
24390     {
24391         if(e){
24392             e.preventDefault();
24393         }
24394         
24395         if(
24396                 (
24397                     (this.rotate == 0 || this.rotate == 180) 
24398                     &&
24399                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24400                 )
24401                 ||
24402                 (
24403                     (this.rotate == 90 || this.rotate == 270) 
24404                     &&
24405                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24406                 )
24407                 
24408         ){
24409             return;
24410         }
24411         
24412         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24413
24414         this.draw();
24415         
24416     },
24417     
24418     onRotateRight : function(e)
24419     {
24420         if(e){
24421             e.preventDefault();
24422         }
24423         
24424         if(
24425                 (
24426                     (this.rotate == 0 || this.rotate == 180) 
24427                     &&
24428                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24429                 )
24430                 ||
24431                 (
24432                     (this.rotate == 90 || this.rotate == 270) 
24433                     &&
24434                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24435                 )
24436                 
24437         ){
24438             return false;
24439         }
24440         
24441         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24442
24443         this.draw();
24444         
24445     },
24446     
24447     draw : function()
24448     {
24449         this.previewEl.dom.innerHTML = '';
24450         
24451         var canvasEl = document.createElement("canvas");
24452         
24453         var contextEl = canvasEl.getContext("2d");
24454         
24455         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24456         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24457         var center = this.imageEl.OriginWidth / 2;
24458         
24459         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24460             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24461             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24462             center = this.imageEl.OriginHeight / 2;
24463         }
24464         
24465         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24466         
24467         contextEl.translate(center, center);
24468         contextEl.rotate(this.rotate * Math.PI / 180);
24469
24470         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24471         
24472         this.canvasEl = document.createElement("canvas");
24473         
24474         this.contextEl = this.canvasEl.getContext("2d");
24475         
24476         switch (this.rotate) {
24477             case 0 :
24478                 
24479                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24480                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24481                 
24482                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24483                 
24484                 break;
24485             case 90 : 
24486                 
24487                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24488                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24489                 
24490                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24491                     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);
24492                     break;
24493                 }
24494                 
24495                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24496                 
24497                 break;
24498             case 180 :
24499                 
24500                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24501                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24502                 
24503                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24504                     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);
24505                     break;
24506                 }
24507                 
24508                 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);
24509                 
24510                 break;
24511             case 270 :
24512                 
24513                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24514                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24515         
24516                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24517                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24518                     break;
24519                 }
24520                 
24521                 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);
24522                 
24523                 break;
24524             default : 
24525                 break;
24526         }
24527         
24528         this.previewEl.appendChild(this.canvasEl);
24529         
24530         this.setCanvasPosition();
24531     },
24532     
24533     crop : function()
24534     {
24535         if(typeof(this.canvasEl) == 'undefined'){
24536             return;
24537         }
24538         var canvas = document.createElement("canvas");
24539         
24540         var context = canvas.getContext("2d");
24541         
24542         canvas.width = this.minWidth;
24543         canvas.height = this.minHeight;
24544         
24545         var cropWidth = this.thumbEl.getWidth();
24546         var cropHeight = this.thumbEl.getHeight();
24547         
24548         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24549         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24550         
24551         if(this.canvasEl.width - cropWidth < x){
24552             x = this.canvasEl.width - cropWidth;
24553         }
24554         
24555         if(this.canvasEl.height - cropHeight < y){
24556             y = this.canvasEl.height - cropHeight;
24557         }
24558         
24559         x = x < 0 ? 0 : x;
24560         y = y < 0 ? 0 : y;
24561         
24562         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24563         
24564         this.cropData = canvas.toDataURL(this.cropType);
24565         
24566         this.fireEvent('crop', this, this.cropData);
24567         
24568     },
24569     
24570     setThumbBoxSize : function()
24571     {
24572         var height = 300;
24573         var width = Math.ceil(this.minWidth * height / this.minHeight);
24574         
24575         if(this.minWidth > this.minHeight){
24576             width = 300;
24577             height = Math.ceil(this.minHeight * width / this.minWidth);
24578         }
24579         
24580         this.thumbEl.setStyle({
24581             width : width + 'px',
24582             height : height + 'px'
24583         });
24584
24585         return;
24586             
24587     },
24588     
24589     setThumbBoxPosition : function()
24590     {
24591         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24592         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24593         
24594         this.thumbEl.setLeft(x);
24595         this.thumbEl.setTop(y);
24596         
24597     },
24598     
24599     baseRotateLevel : function()
24600     {
24601         this.baseRotate = 1;
24602         
24603         if(
24604                 typeof(this.exif) != 'undefined' &&
24605                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24606                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24607         ){
24608             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24609         }
24610         
24611         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24612         
24613     },
24614     
24615     baseScaleLevel : function()
24616     {
24617         var width, height;
24618         
24619         if(this.baseRotate == 6 || this.baseRotate == 8){
24620             
24621             width = this.thumbEl.getHeight();
24622             this.baseScale = height / this.imageEl.OriginHeight;
24623             
24624             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24625                 height = this.thumbEl.getWidth();
24626                 this.baseScale = height / this.imageEl.OriginHeight;
24627             }
24628             
24629             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24630                 height = this.thumbEl.getWidth();
24631                 this.baseScale = height / this.imageEl.OriginHeight;
24632                 
24633                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24634                     width = this.thumbEl.getHeight();
24635                     this.baseScale = width / this.imageEl.OriginWidth;
24636                 }
24637             }
24638             
24639             return;
24640         }
24641         
24642         width = this.thumbEl.getWidth();
24643         this.baseScale = width / this.imageEl.OriginWidth;
24644         
24645         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24646             height = this.thumbEl.getHeight();
24647             this.baseScale = height / this.imageEl.OriginHeight;
24648         }
24649         
24650         
24651         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24652             
24653             height = this.thumbEl.getHeight();
24654             this.baseScale = height / this.imageEl.OriginHeight;
24655             
24656             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24657                 width = this.thumbEl.getWidth();
24658                 this.baseScale = width / this.imageEl.OriginWidth;
24659             }
24660             
24661         }
24662         
24663         return;
24664     },
24665     
24666     getScaleLevel : function()
24667     {
24668         return this.baseScale * Math.pow(1.1, this.scale);
24669     },
24670     
24671     onTouchStart : function(e)
24672     {
24673         e.stopEvent();
24674         
24675         var touches = e.browserEvent.touches;
24676         
24677         if(!touches){
24678             return;
24679         }
24680         
24681         if(touches.length == 1){
24682             this.onMouseDown(e);
24683             return;
24684         }
24685         
24686         if(touches.length != 2){
24687             return;
24688         }
24689         
24690         var coords = [];
24691         
24692         for(var i = 0, finger; finger = touches[i]; i++){
24693             coords.push(finger.pageX, finger.pageY);
24694         }
24695         
24696         var x = Math.pow(coords[0] - coords[2], 2);
24697         var y = Math.pow(coords[1] - coords[3], 2);
24698         
24699         this.startDistance = Math.sqrt(x + y);
24700         
24701         this.startScale = this.scale;
24702         
24703         this.pinching = true;
24704         this.dragable = false;
24705         
24706     },
24707     
24708     onTouchMove : function(e)
24709     {
24710         e.stopEvent();
24711         
24712         if(!this.pinching && !this.dragable){
24713             return;
24714         }
24715         
24716         var touches = e.browserEvent.touches;
24717         
24718         if(!touches){
24719             return;
24720         }
24721         
24722         if(this.dragable){
24723             this.onMouseMove(e);
24724             return;
24725         }
24726         
24727         var coords = [];
24728         
24729         for(var i = 0, finger; finger = touches[i]; i++){
24730             coords.push(finger.pageX, finger.pageY);
24731         }
24732         
24733         var x = Math.pow(coords[0] - coords[2], 2);
24734         var y = Math.pow(coords[1] - coords[3], 2);
24735         
24736         this.endDistance = Math.sqrt(x + y);
24737         
24738         var scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24739         
24740         var width = Math.ceil(this.imageEl.OriginWidth * this.baseScale * Math.pow(1.1, scale));
24741         var height = Math.ceil(this.imageEl.OriginHeight * this.baseScale * Math.pow(1.1, scale));
24742         
24743         if(
24744                 this.endDistance / this.startDistance < 1 &&
24745                 (
24746                     (
24747                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24748                     )
24749                     ||
24750                     (
24751                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24752                     )
24753                 )
24754         ){
24755             return;
24756         }
24757         
24758         this.scale = scale;
24759         
24760         this.draw();
24761         
24762     },
24763     
24764     onTouchEnd : function(e)
24765     {
24766         e.stopEvent();
24767         
24768         this.pinching = false;
24769         this.dragable = false;
24770         
24771     },
24772     
24773     prepare : function(input)
24774     {        
24775         this.file = false;
24776         this.exif = {};
24777         
24778         if(typeof(input) === 'string'){
24779             this.loadCanvas(input);
24780             return;
24781         }
24782         
24783         if(!input.files || !input.files[0] || !this.urlAPI){
24784             return;
24785         }
24786         
24787         this.file = input.files[0];
24788         this.cropType = this.file.type;
24789         
24790         var _this = this;
24791         
24792         if(this.fireEvent('prepare', this, this.file) != false){
24793             
24794             var reader = new FileReader();
24795             
24796             reader.onload = function (e) {
24797                 if (e.target.error) {
24798                     Roo.log(e.target.error);
24799                     return;
24800                 }
24801                 
24802                 var buffer = e.target.result,
24803                     dataView = new DataView(buffer),
24804                     offset = 2,
24805                     maxOffset = dataView.byteLength - 4,
24806                     markerBytes,
24807                     markerLength;
24808                 
24809                 if (dataView.getUint16(0) === 0xffd8) {
24810                     while (offset < maxOffset) {
24811                         markerBytes = dataView.getUint16(offset);
24812                         
24813                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24814                             markerLength = dataView.getUint16(offset + 2) + 2;
24815                             if (offset + markerLength > dataView.byteLength) {
24816                                 Roo.log('Invalid meta data: Invalid segment size.');
24817                                 break;
24818                             }
24819                             
24820                             if(markerBytes == 0xffe1){
24821                                 _this.parseExifData(
24822                                     dataView,
24823                                     offset,
24824                                     markerLength
24825                                 );
24826                             }
24827                             
24828                             offset += markerLength;
24829                             
24830                             continue;
24831                         }
24832                         
24833                         break;
24834                     }
24835                     
24836                 }
24837                 
24838                 var url = _this.urlAPI.createObjectURL(_this.file);
24839                 
24840                 _this.loadCanvas(url);
24841                 
24842                 return;
24843             }
24844             
24845             reader.readAsArrayBuffer(this.file);
24846             
24847         }
24848         
24849     },
24850     
24851     parseExifData : function(dataView, offset, length)
24852     {
24853         var tiffOffset = offset + 10,
24854             littleEndian,
24855             dirOffset;
24856     
24857         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24858             // No Exif data, might be XMP data instead
24859             return;
24860         }
24861         
24862         // Check for the ASCII code for "Exif" (0x45786966):
24863         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24864             // No Exif data, might be XMP data instead
24865             return;
24866         }
24867         if (tiffOffset + 8 > dataView.byteLength) {
24868             Roo.log('Invalid Exif data: Invalid segment size.');
24869             return;
24870         }
24871         // Check for the two null bytes:
24872         if (dataView.getUint16(offset + 8) !== 0x0000) {
24873             Roo.log('Invalid Exif data: Missing byte alignment offset.');
24874             return;
24875         }
24876         // Check the byte alignment:
24877         switch (dataView.getUint16(tiffOffset)) {
24878         case 0x4949:
24879             littleEndian = true;
24880             break;
24881         case 0x4D4D:
24882             littleEndian = false;
24883             break;
24884         default:
24885             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24886             return;
24887         }
24888         // Check for the TIFF tag marker (0x002A):
24889         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24890             Roo.log('Invalid Exif data: Missing TIFF marker.');
24891             return;
24892         }
24893         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24894         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24895         
24896         this.parseExifTags(
24897             dataView,
24898             tiffOffset,
24899             tiffOffset + dirOffset,
24900             littleEndian
24901         );
24902     },
24903     
24904     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24905     {
24906         var tagsNumber,
24907             dirEndOffset,
24908             i;
24909         if (dirOffset + 6 > dataView.byteLength) {
24910             Roo.log('Invalid Exif data: Invalid directory offset.');
24911             return;
24912         }
24913         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24914         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24915         if (dirEndOffset + 4 > dataView.byteLength) {
24916             Roo.log('Invalid Exif data: Invalid directory size.');
24917             return;
24918         }
24919         for (i = 0; i < tagsNumber; i += 1) {
24920             this.parseExifTag(
24921                 dataView,
24922                 tiffOffset,
24923                 dirOffset + 2 + 12 * i, // tag offset
24924                 littleEndian
24925             );
24926         }
24927         // Return the offset to the next directory:
24928         return dataView.getUint32(dirEndOffset, littleEndian);
24929     },
24930     
24931     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
24932     {
24933         var tag = dataView.getUint16(offset, littleEndian);
24934         
24935         this.exif[tag] = this.getExifValue(
24936             dataView,
24937             tiffOffset,
24938             offset,
24939             dataView.getUint16(offset + 2, littleEndian), // tag type
24940             dataView.getUint32(offset + 4, littleEndian), // tag length
24941             littleEndian
24942         );
24943     },
24944     
24945     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24946     {
24947         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
24948             tagSize,
24949             dataOffset,
24950             values,
24951             i,
24952             str,
24953             c;
24954     
24955         if (!tagType) {
24956             Roo.log('Invalid Exif data: Invalid tag type.');
24957             return;
24958         }
24959         
24960         tagSize = tagType.size * length;
24961         // Determine if the value is contained in the dataOffset bytes,
24962         // or if the value at the dataOffset is a pointer to the actual data:
24963         dataOffset = tagSize > 4 ?
24964                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
24965         if (dataOffset + tagSize > dataView.byteLength) {
24966             Roo.log('Invalid Exif data: Invalid data offset.');
24967             return;
24968         }
24969         if (length === 1) {
24970             return tagType.getValue(dataView, dataOffset, littleEndian);
24971         }
24972         values = [];
24973         for (i = 0; i < length; i += 1) {
24974             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
24975         }
24976         
24977         if (tagType.ascii) {
24978             str = '';
24979             // Concatenate the chars:
24980             for (i = 0; i < values.length; i += 1) {
24981                 c = values[i];
24982                 // Ignore the terminating NULL byte(s):
24983                 if (c === '\u0000') {
24984                     break;
24985                 }
24986                 str += c;
24987             }
24988             return str;
24989         }
24990         return values;
24991     }
24992     
24993 });
24994
24995 Roo.apply(Roo.bootstrap.UploadCropbox, {
24996     tags : {
24997         'Orientation': 0x0112
24998     },
24999     
25000     Orientation: {
25001             1: 0, //'top-left',
25002 //            2: 'top-right',
25003             3: 180, //'bottom-right',
25004 //            4: 'bottom-left',
25005 //            5: 'left-top',
25006             6: 90, //'right-top',
25007 //            7: 'right-bottom',
25008             8: 270 //'left-bottom'
25009     },
25010     
25011     exifTagTypes : {
25012         // byte, 8-bit unsigned int:
25013         1: {
25014             getValue: function (dataView, dataOffset) {
25015                 return dataView.getUint8(dataOffset);
25016             },
25017             size: 1
25018         },
25019         // ascii, 8-bit byte:
25020         2: {
25021             getValue: function (dataView, dataOffset) {
25022                 return String.fromCharCode(dataView.getUint8(dataOffset));
25023             },
25024             size: 1,
25025             ascii: true
25026         },
25027         // short, 16 bit int:
25028         3: {
25029             getValue: function (dataView, dataOffset, littleEndian) {
25030                 return dataView.getUint16(dataOffset, littleEndian);
25031             },
25032             size: 2
25033         },
25034         // long, 32 bit int:
25035         4: {
25036             getValue: function (dataView, dataOffset, littleEndian) {
25037                 return dataView.getUint32(dataOffset, littleEndian);
25038             },
25039             size: 4
25040         },
25041         // rational = two long values, first is numerator, second is denominator:
25042         5: {
25043             getValue: function (dataView, dataOffset, littleEndian) {
25044                 return dataView.getUint32(dataOffset, littleEndian) /
25045                     dataView.getUint32(dataOffset + 4, littleEndian);
25046             },
25047             size: 8
25048         },
25049         // slong, 32 bit signed int:
25050         9: {
25051             getValue: function (dataView, dataOffset, littleEndian) {
25052                 return dataView.getInt32(dataOffset, littleEndian);
25053             },
25054             size: 4
25055         },
25056         // srational, two slongs, first is numerator, second is denominator:
25057         10: {
25058             getValue: function (dataView, dataOffset, littleEndian) {
25059                 return dataView.getInt32(dataOffset, littleEndian) /
25060                     dataView.getInt32(dataOffset + 4, littleEndian);
25061             },
25062             size: 8
25063         }
25064     },
25065     
25066     footer : {
25067         STANDARD : [
25068             {
25069                 tag : 'div',
25070                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25071                 action : 'rotate-left',
25072                 cn : [
25073                     {
25074                         tag : 'button',
25075                         cls : 'btn btn-default',
25076                         html : '<i class="fa fa-undo"></i>'
25077                     }
25078                 ]
25079             },
25080             {
25081                 tag : 'div',
25082                 cls : 'btn-group roo-upload-cropbox-picture',
25083                 action : 'picture',
25084                 cn : [
25085                     {
25086                         tag : 'button',
25087                         cls : 'btn btn-default',
25088                         html : '<i class="fa fa-picture-o"></i>'
25089                     }
25090                 ]
25091             },
25092             {
25093                 tag : 'div',
25094                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25095                 action : 'rotate-right',
25096                 cn : [
25097                     {
25098                         tag : 'button',
25099                         cls : 'btn btn-default',
25100                         html : '<i class="fa fa-repeat"></i>'
25101                     }
25102                 ]
25103             }
25104         ],
25105         DOCUMENT : [
25106             {
25107                 tag : 'div',
25108                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25109                 action : 'rotate-left',
25110                 cn : [
25111                     {
25112                         tag : 'button',
25113                         cls : 'btn btn-default',
25114                         html : '<i class="fa fa-undo"></i>'
25115                     }
25116                 ]
25117             },
25118             {
25119                 tag : 'div',
25120                 cls : 'btn-group roo-upload-cropbox-download',
25121                 action : 'download',
25122                 cn : [
25123                     {
25124                         tag : 'button',
25125                         cls : 'btn btn-default',
25126                         html : '<i class="fa fa-download"></i>'
25127                     }
25128                 ]
25129             },
25130             {
25131                 tag : 'div',
25132                 cls : 'btn-group roo-upload-cropbox-crop',
25133                 action : 'crop',
25134                 cn : [
25135                     {
25136                         tag : 'button',
25137                         cls : 'btn btn-default',
25138                         html : '<i class="fa fa-crop"></i>'
25139                     }
25140                 ]
25141             },
25142             {
25143                 tag : 'div',
25144                 cls : 'btn-group roo-upload-cropbox-trash',
25145                 action : 'trash',
25146                 cn : [
25147                     {
25148                         tag : 'button',
25149                         cls : 'btn btn-default',
25150                         html : '<i class="fa fa-trash"></i>'
25151                     }
25152                 ]
25153             },
25154             {
25155                 tag : 'div',
25156                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25157                 action : 'rotate-right',
25158                 cn : [
25159                     {
25160                         tag : 'button',
25161                         cls : 'btn btn-default',
25162                         html : '<i class="fa fa-repeat"></i>'
25163                     }
25164                 ]
25165             }
25166         ]
25167     }
25168 });
25169
25170 /*
25171 * Licence: LGPL
25172 */
25173
25174 /**
25175  * @class Roo.bootstrap.DocumentManager
25176  * @extends Roo.bootstrap.Component
25177  * Bootstrap DocumentManager class
25178  * @cfg {String} paramName default 'imageUpload'
25179  * @cfg {String} method default POST
25180  * @cfg {String} url action url
25181  * @cfg {Number} boxes number of boxes default 12
25182  * @cfg {Boolean} multiple multiple upload default true
25183  * @cfg {Number} minWidth default 300
25184  * @cfg {Number} minHeight default 300
25185  * @cfg {Number} thumbSize default 300
25186  * @cfg {String} fieldLabel
25187  * @cfg {Number} labelWidth default 4
25188  * @cfg {String} labelAlign (left|top) default left
25189  * 
25190  * @constructor
25191  * Create a new DocumentManager
25192  * @param {Object} config The config object
25193  */
25194
25195 Roo.bootstrap.DocumentManager = function(config){
25196     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25197     
25198     this.addEvents({
25199         /**
25200          * @event initial
25201          * Fire when initial the DocumentManager
25202          * @param {Roo.bootstrap.DocumentManager} this
25203          */
25204         "initial" : true,
25205         /**
25206          * @event inspect
25207          * inspect selected file
25208          * @param {Roo.bootstrap.DocumentManager} this
25209          * @param {File} file
25210          */
25211         "inspect" : true,
25212         /**
25213          * @event exception
25214          * Fire when xhr load exception
25215          * @param {Roo.bootstrap.DocumentManager} this
25216          * @param {XMLHttpRequest} xhr
25217          */
25218         "exception" : true,
25219         /**
25220          * @event prepare
25221          * prepare the form data
25222          * @param {Roo.bootstrap.DocumentManager} this
25223          * @param {Object} formData
25224          */
25225         "prepare" : true,
25226         /**
25227          * @event remove
25228          * Fire when remove the file
25229          * @param {Roo.bootstrap.DocumentManager} this
25230          * @param {Object} file
25231          */
25232         "remove" : true,
25233         /**
25234          * @event refresh
25235          * Fire after refresh the file
25236          * @param {Roo.bootstrap.DocumentManager} this
25237          */
25238         "refresh" : true,
25239         /**
25240          * @event click
25241          * Fire after click the image
25242          * @param {Roo.bootstrap.DocumentManager} this
25243          * @param {Object} file
25244          */
25245         "click" : true
25246         
25247     });
25248 };
25249
25250 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25251     
25252     boxes : 12,
25253     inputName : '',
25254     minWidth : 300,
25255     minHeight : 300,
25256     thumbSize : 300,
25257     multiple : true,
25258     files : [],
25259     method : 'POST',
25260     url : '',
25261     paramName : 'imageUpload',
25262     fieldLabel : '',
25263     labelWidth : 4,
25264     labelAlign : 'left',
25265     
25266     getAutoCreate : function()
25267     {   
25268         var managerWidget = {
25269             tag : 'div',
25270             cls : 'roo-document-manager',
25271             cn : [
25272                 {
25273                     tag : 'input',
25274                     cls : 'roo-document-manager-selector',
25275                     type : 'file'
25276                 },
25277                 {
25278                     tag : 'div',
25279                     cls : 'roo-document-manager-uploader',
25280                     cn : [
25281                         {
25282                             tag : 'div',
25283                             cls : 'roo-document-manager-upload-btn',
25284                             html : '<i class="fa fa-plus"></i>'
25285                         }
25286                     ]
25287                     
25288                 }
25289             ]
25290         };
25291         
25292         var content = [
25293             {
25294                 tag : 'div',
25295                 cls : 'column col-md-12',
25296                 cn : managerWidget
25297             }
25298         ];
25299         
25300         if(this.fieldLabel.length){
25301             
25302             content = [
25303                 {
25304                     tag : 'div',
25305                     cls : 'column col-md-12',
25306                     html : this.fieldLabel
25307                 },
25308                 {
25309                     tag : 'div',
25310                     cls : 'column col-md-12',
25311                     cn : managerWidget
25312                 }
25313             ];
25314
25315             if(this.labelAlign == 'left'){
25316                 content = [
25317                     {
25318                         tag : 'div',
25319                         cls : 'column col-md-' + this.labelWidth,
25320                         html : this.fieldLabel
25321                     },
25322                     {
25323                         tag : 'div',
25324                         cls : 'column col-md-' + (12 - this.labelWidth),
25325                         cn : managerWidget
25326                     }
25327                 ];
25328                 
25329             }
25330         }
25331         
25332         var cfg = {
25333             tag : 'div',
25334             cls : 'row clearfix',
25335             cn : content
25336         };
25337         
25338         return cfg;
25339         
25340     },
25341     
25342     initEvents : function()
25343     {
25344         this.managerEl = this.el.select('.roo-document-manager', true).first();
25345         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25346         
25347         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25348         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25349         this.selectorEl.hide();
25350         
25351         if(this.multiple){
25352             this.selectorEl.attr('multiple', 'multiple');
25353         }
25354         
25355         this.selectorEl.on('change', this.onSelect, this);
25356         
25357         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25358         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25359         
25360         this.uploader.on('click', this.onUpload, this);
25361         
25362         var _this = this;
25363         
25364         window.addEventListener("resize", function() { _this.refresh(); } );
25365         
25366         this.fireEvent('initial', this);
25367     },
25368     
25369     onUpload : function(e)
25370     {
25371         e.preventDefault();
25372         
25373         this.selectorEl.dom.click();
25374         
25375     },
25376     
25377     onSelect : function(e)
25378     {
25379         e.preventDefault();
25380         
25381         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25382             return;
25383         }
25384         
25385         Roo.each(this.selectorEl.dom.files, function(file){
25386             if(this.fireEvent('inspect', this, file) != false){
25387                 this.files.push(file);
25388             }
25389         }, this);
25390         
25391         this.process();
25392         
25393     },
25394     
25395     process : function()
25396     {
25397         this.selectorEl.dom.value = '';
25398         
25399         if(!this.files.length){
25400             return;
25401         }
25402         
25403         if(this.files.length > this.boxes){
25404             this.files = this.files.slice(0, this.boxes);
25405         }
25406         
25407         var xhr = new XMLHttpRequest();
25408         
25409         Roo.each(this.files, function(file, index){
25410             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25411                 return;
25412             }
25413             
25414             file.xhr = xhr;
25415             
25416             this.managerEl.createChild({
25417                 tag : 'div',
25418                 cls : 'roo-document-manager-loading',
25419                 cn : [
25420                     {
25421                         tag : 'div',
25422                         tooltip : file.name,
25423                         cls : 'roo-document-manager-thumb',
25424                         html : '<i class="fa fa-spinner fa-pulse"></i>'
25425                     }
25426                 ]
25427
25428             });
25429             
25430         }, this);
25431         
25432         if(this.files.length > this.boxes - 1 ){
25433             this.uploader.hide();
25434         }
25435         
25436         var headers = {
25437             "Accept": "application/json",
25438             "Cache-Control": "no-cache",
25439             "X-Requested-With": "XMLHttpRequest"
25440         };
25441         
25442         xhr.open(this.method, this.url, true);
25443         
25444         for (var headerName in headers) {
25445             var headerValue = headers[headerName];
25446             if (headerValue) {
25447                 xhr.setRequestHeader(headerName, headerValue);
25448             }
25449         }
25450         
25451         var _this = this;
25452         
25453         xhr.onload = function()
25454         {
25455             _this.xhrOnLoad(xhr);
25456         }
25457         
25458         xhr.onerror = function()
25459         {
25460             _this.xhrOnError(xhr);
25461         }
25462         
25463         var formData = new FormData();
25464
25465         formData.append('returnHTML', 'NO');
25466         
25467         Roo.each(this.files, function(file, index){
25468             
25469             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25470                 return;
25471             }
25472             
25473             formData.append(this.getParamName(index), file, file.name);
25474             
25475         }, this);
25476         
25477         if(this.fireEvent('prepare', this, formData) != false){
25478             xhr.send(formData);
25479         };
25480         
25481     },
25482     
25483     getParamName : function(i)
25484     {
25485         if(!this.multiple){
25486             return this.paramName;
25487         }
25488         
25489         return this.paramName + "_" + i;
25490     },
25491     
25492     refresh : function()
25493     {
25494         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25495             el.remove();
25496         }, this);
25497         
25498         
25499         var files = [];
25500         
25501         Roo.each(this.files, function(file){
25502             
25503             if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25504                 return;
25505             }
25506             
25507             if(file.target){
25508                 files.push(file);
25509                 return;
25510             }
25511             
25512             var previewEl = this.managerEl.createChild({
25513                 tag : 'div',
25514                 cls : 'roo-document-manager-preview',
25515                 cn : [
25516                     {
25517                         tag : 'div',
25518                         tooltip : file.filename,
25519                         cls : 'roo-document-manager-thumb',
25520                         html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25521                     },
25522                     {
25523                         tag : 'button',
25524                         cls : 'close',
25525                         html : 'x'
25526                     }
25527                 ]
25528             });
25529             
25530             var close = previewEl.select('button.close', true).first();
25531             
25532             close.on('click', this.onRemove, this, file);
25533             
25534             file.target = previewEl;
25535             
25536             var image = previewEl.select('img', true).first();
25537             
25538             image.on('click', this.onClick, this, file);
25539             
25540             files.push(file);
25541             
25542             return;
25543             
25544         }, this);
25545         
25546         this.files = files;
25547         
25548         this.uploader.show();
25549         
25550         if(this.files.length > this.boxes - 1){
25551             this.uploader.hide();
25552         }
25553         
25554         Roo.isTouch ? this.closable(false) : this.closable(true);
25555         
25556         this.fireEvent('refresh', this);
25557     },
25558     
25559     onRemove : function(e, el, o)
25560     {
25561         e.preventDefault();
25562         
25563         this.fireEvent('remove', this, o);
25564         
25565     },
25566     
25567     remove : function(o)
25568     {
25569         var files = [];
25570         
25571         Roo.each(this.files, function(file){
25572             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25573                 files.push(file);
25574                 return;
25575             }
25576
25577             o.target.remove();
25578
25579         }, this);
25580         
25581         this.files = files;
25582         
25583         this.refresh();
25584     },
25585     
25586     onClick : function(e, el, o)
25587     {
25588         e.preventDefault();
25589         
25590         this.fireEvent('click', this, o);
25591         
25592     },
25593     
25594     closable : function(closable)
25595     {
25596         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25597             
25598             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25599             
25600             if(closable){
25601                 el.show();
25602                 return;
25603             }
25604             
25605             el.hide();
25606             
25607         }, this);
25608     },
25609     
25610     xhrOnLoad : function(xhr)
25611     {
25612         if (xhr.readyState !== 4) {
25613             this.refresh();
25614             this.fireEvent('exception', this, xhr);
25615             return;
25616         }
25617
25618         var response = Roo.decode(xhr.responseText);
25619         
25620         if(!response.success){
25621             this.refresh();
25622             this.fireEvent('exception', this, xhr);
25623             return;
25624         }
25625         
25626         var i = 0;
25627         
25628         Roo.each(this.files, function(file, index){
25629             
25630             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25631                 return;
25632             }
25633             
25634             this.files[index] = response.data[i];
25635             i++;
25636             
25637             return;
25638             
25639         }, this);
25640         
25641         this.refresh();
25642         
25643     },
25644     
25645     xhrOnError : function()
25646     {
25647         Roo.log('xhr on error');
25648         
25649         var response = Roo.decode(xhr.responseText);
25650           
25651         Roo.log(response);
25652     }
25653     
25654     
25655     
25656 });
25657 /*
25658 * Licence: LGPL
25659 */
25660
25661 /**
25662  * @class Roo.bootstrap.DocumentViewer
25663  * @extends Roo.bootstrap.Component
25664  * Bootstrap DocumentViewer class
25665  * 
25666  * @constructor
25667  * Create a new DocumentViewer
25668  * @param {Object} config The config object
25669  */
25670
25671 Roo.bootstrap.DocumentViewer = function(config){
25672     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25673     
25674     this.addEvents({
25675         /**
25676          * @event initial
25677          * Fire after initEvent
25678          * @param {Roo.bootstrap.DocumentViewer} this
25679          */
25680         "initial" : true,
25681         /**
25682          * @event click
25683          * Fire after click
25684          * @param {Roo.bootstrap.DocumentViewer} this
25685          */
25686         "click" : true,
25687         /**
25688          * @event trash
25689          * Fire after trash button
25690          * @param {Roo.bootstrap.DocumentViewer} this
25691          */
25692         "trash" : true
25693         
25694     });
25695 };
25696
25697 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
25698     
25699     getAutoCreate : function()
25700     {
25701         var cfg = {
25702             tag : 'div',
25703             cls : 'roo-document-viewer',
25704             cn : [
25705                 {
25706                     tag : 'div',
25707                     cls : 'roo-document-viewer-body',
25708                     cn : [
25709                         {
25710                             tag : 'div',
25711                             cls : 'roo-document-viewer-thumb',
25712                             cn : [
25713                                 {
25714                                     tag : 'img',
25715                                     cls : 'roo-document-viewer-image'
25716                                 }
25717                             ]
25718                         }
25719                     ]
25720                 },
25721                 {
25722                     tag : 'div',
25723                     cls : 'roo-document-viewer-footer',
25724                     cn : {
25725                         tag : 'div',
25726                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25727                         cn : [
25728                             {
25729                                 tag : 'div',
25730                                 cls : 'btn-group',
25731                                 cn : [
25732                                     {
25733                                         tag : 'button',
25734                                         cls : 'btn btn-default roo-document-viewer-trash',
25735                                         html : '<i class="fa fa-trash"></i>'
25736                                     }
25737                                 ]
25738                             }
25739                         ]
25740                     }
25741                 }
25742             ]
25743         };
25744         
25745         return cfg;
25746     },
25747     
25748     initEvents : function()
25749     {
25750         
25751         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25752         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25753         
25754         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25755         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25756         
25757         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25758         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25759         
25760         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25761         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25762         
25763         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25764         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25765         
25766         this.bodyEl.on('click', this.onClick, this);
25767         
25768         this.trashBtn.on('click', this.onTrash, this);
25769         
25770     },
25771     
25772     initial : function()
25773     {
25774 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25775         
25776         
25777         this.fireEvent('initial', this);
25778         
25779     },
25780     
25781     onClick : function(e)
25782     {
25783         e.preventDefault();
25784         
25785         this.fireEvent('click', this);
25786     },
25787     
25788     onTrash : function(e)
25789     {
25790         e.preventDefault();
25791         
25792         this.fireEvent('trash', this);
25793     }
25794     
25795 });