59376a63159fe67a981b72d9dd33ace5dc1c1099
[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     },
2553     show : function() {
2554         
2555         if (!this.rendered) {
2556             this.render();
2557         }
2558         
2559         this.el.setStyle('display', 'block');
2560         
2561         if(this.animate){
2562             var _this = this;
2563             (function(){ _this.el.addClass('in'); }).defer(50);
2564         }else{
2565             this.el.addClass('in');
2566         }
2567         
2568         // not sure how we can show data in here.. 
2569         //if (this.tmpl) {
2570         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2571         //}
2572         
2573         Roo.get(document.body).addClass("x-body-masked");
2574         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2575         this.maskEl.show();
2576         this.el.setStyle('zIndex', '10001');
2577        
2578         this.fireEvent('show', this);
2579         
2580         
2581     },
2582     hide : function()
2583     {
2584         this.maskEl.hide();
2585         Roo.get(document.body).removeClass("x-body-masked");
2586         this.el.removeClass('in');
2587         
2588         if(this.animate){
2589             var _this = this;
2590             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2591         }else{
2592             this.el.setStyle('display', 'none');
2593         }
2594         
2595         this.fireEvent('hide', this);
2596     },
2597     
2598     addButton : function(str, cb)
2599     {
2600          
2601         
2602         var b = Roo.apply({}, { html : str } );
2603         b.xns = b.xns || Roo.bootstrap;
2604         b.xtype = b.xtype || 'Button';
2605         if (typeof(b.listeners) == 'undefined') {
2606             b.listeners = { click : cb.createDelegate(this)  };
2607         }
2608         
2609         var btn = Roo.factory(b);
2610            
2611         btn.onRender(this.el.select('.modal-footer div').first());
2612         
2613         return btn;   
2614        
2615     },
2616     
2617     setDefaultButton : function(btn)
2618     {
2619         //this.el.select('.modal-footer').()
2620     },
2621     resizeTo: function(w,h)
2622     {
2623         // skip..
2624     },
2625     setContentSize  : function(w, h)
2626     {
2627         
2628     },
2629     onButtonClick: function(btn,e)
2630     {
2631         //Roo.log([a,b,c]);
2632         this.fireEvent('btnclick', btn.name, e);
2633     },
2634      /**
2635      * Set the title of the Dialog
2636      * @param {String} str new Title
2637      */
2638     setTitle: function(str) {
2639         this.titleEl.dom.innerHTML = str;    
2640     },
2641     /**
2642      * Set the body of the Dialog
2643      * @param {String} str new Title
2644      */
2645     setBody: function(str) {
2646         this.bodyEl.dom.innerHTML = str;    
2647     },
2648     /**
2649      * Set the body of the Dialog using the template
2650      * @param {Obj} data - apply this data to the template and replace the body contents.
2651      */
2652     applyBody: function(obj)
2653     {
2654         if (!this.tmpl) {
2655             Roo.log("Error - using apply Body without a template");
2656             //code
2657         }
2658         this.tmpl.overwrite(this.bodyEl, obj);
2659     }
2660     
2661 });
2662
2663
2664 Roo.apply(Roo.bootstrap.Modal,  {
2665     /**
2666          * Button config that displays a single OK button
2667          * @type Object
2668          */
2669         OK :  [{
2670             name : 'ok',
2671             weight : 'primary',
2672             html : 'OK'
2673         }], 
2674         /**
2675          * Button config that displays Yes and No buttons
2676          * @type Object
2677          */
2678         YESNO : [
2679             {
2680                 name  : 'no',
2681                 html : 'No'
2682             },
2683             {
2684                 name  :'yes',
2685                 weight : 'primary',
2686                 html : 'Yes'
2687             }
2688         ],
2689         
2690         /**
2691          * Button config that displays OK and Cancel buttons
2692          * @type Object
2693          */
2694         OKCANCEL : [
2695             {
2696                name : 'cancel',
2697                 html : 'Cancel'
2698             },
2699             {
2700                 name : 'ok',
2701                 weight : 'primary',
2702                 html : 'OK'
2703             }
2704         ],
2705         /**
2706          * Button config that displays Yes, No and Cancel buttons
2707          * @type Object
2708          */
2709         YESNOCANCEL : [
2710             {
2711                 name : 'yes',
2712                 weight : 'primary',
2713                 html : 'Yes'
2714             },
2715             {
2716                 name : 'no',
2717                 html : 'No'
2718             },
2719             {
2720                 name : 'cancel',
2721                 html : 'Cancel'
2722             }
2723         ]
2724 });
2725  
2726  /*
2727  * - LGPL
2728  *
2729  * messagebox - can be used as a replace
2730  * 
2731  */
2732 /**
2733  * @class Roo.MessageBox
2734  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2735  * Example usage:
2736  *<pre><code>
2737 // Basic alert:
2738 Roo.Msg.alert('Status', 'Changes saved successfully.');
2739
2740 // Prompt for user data:
2741 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2742     if (btn == 'ok'){
2743         // process text value...
2744     }
2745 });
2746
2747 // Show a dialog using config options:
2748 Roo.Msg.show({
2749    title:'Save Changes?',
2750    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2751    buttons: Roo.Msg.YESNOCANCEL,
2752    fn: processResult,
2753    animEl: 'elId'
2754 });
2755 </code></pre>
2756  * @singleton
2757  */
2758 Roo.bootstrap.MessageBox = function(){
2759     var dlg, opt, mask, waitTimer;
2760     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2761     var buttons, activeTextEl, bwidth;
2762
2763     
2764     // private
2765     var handleButton = function(button){
2766         dlg.hide();
2767         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2768     };
2769
2770     // private
2771     var handleHide = function(){
2772         if(opt && opt.cls){
2773             dlg.el.removeClass(opt.cls);
2774         }
2775         //if(waitTimer){
2776         //    Roo.TaskMgr.stop(waitTimer);
2777         //    waitTimer = null;
2778         //}
2779     };
2780
2781     // private
2782     var updateButtons = function(b){
2783         var width = 0;
2784         if(!b){
2785             buttons["ok"].hide();
2786             buttons["cancel"].hide();
2787             buttons["yes"].hide();
2788             buttons["no"].hide();
2789             //dlg.footer.dom.style.display = 'none';
2790             return width;
2791         }
2792         dlg.footerEl.dom.style.display = '';
2793         for(var k in buttons){
2794             if(typeof buttons[k] != "function"){
2795                 if(b[k]){
2796                     buttons[k].show();
2797                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2798                     width += buttons[k].el.getWidth()+15;
2799                 }else{
2800                     buttons[k].hide();
2801                 }
2802             }
2803         }
2804         return width;
2805     };
2806
2807     // private
2808     var handleEsc = function(d, k, e){
2809         if(opt && opt.closable !== false){
2810             dlg.hide();
2811         }
2812         if(e){
2813             e.stopEvent();
2814         }
2815     };
2816
2817     return {
2818         /**
2819          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2820          * @return {Roo.BasicDialog} The BasicDialog element
2821          */
2822         getDialog : function(){
2823            if(!dlg){
2824                 dlg = new Roo.bootstrap.Modal( {
2825                     //draggable: true,
2826                     //resizable:false,
2827                     //constraintoviewport:false,
2828                     //fixedcenter:true,
2829                     //collapsible : false,
2830                     //shim:true,
2831                     //modal: true,
2832                   //  width:400,
2833                   //  height:100,
2834                     //buttonAlign:"center",
2835                     closeClick : function(){
2836                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2837                             handleButton("no");
2838                         }else{
2839                             handleButton("cancel");
2840                         }
2841                     }
2842                 });
2843                 dlg.render();
2844                 dlg.on("hide", handleHide);
2845                 mask = dlg.mask;
2846                 //dlg.addKeyListener(27, handleEsc);
2847                 buttons = {};
2848                 this.buttons = buttons;
2849                 var bt = this.buttonText;
2850                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2851                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2852                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2853                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2854                 Roo.log(buttons)
2855                 bodyEl = dlg.bodyEl.createChild({
2856
2857                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2858                         '<textarea class="roo-mb-textarea"></textarea>' +
2859                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2860                 });
2861                 msgEl = bodyEl.dom.firstChild;
2862                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2863                 textboxEl.enableDisplayMode();
2864                 textboxEl.addKeyListener([10,13], function(){
2865                     if(dlg.isVisible() && opt && opt.buttons){
2866                         if(opt.buttons.ok){
2867                             handleButton("ok");
2868                         }else if(opt.buttons.yes){
2869                             handleButton("yes");
2870                         }
2871                     }
2872                 });
2873                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2874                 textareaEl.enableDisplayMode();
2875                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2876                 progressEl.enableDisplayMode();
2877                 var pf = progressEl.dom.firstChild;
2878                 if (pf) {
2879                     pp = Roo.get(pf.firstChild);
2880                     pp.setHeight(pf.offsetHeight);
2881                 }
2882                 
2883             }
2884             return dlg;
2885         },
2886
2887         /**
2888          * Updates the message box body text
2889          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2890          * the XHTML-compliant non-breaking space character '&amp;#160;')
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         updateText : function(text){
2894             if(!dlg.isVisible() && !opt.width){
2895                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2896             }
2897             msgEl.innerHTML = text || '&#160;';
2898       
2899             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2900             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2901             var w = Math.max(
2902                     Math.min(opt.width || cw , this.maxWidth), 
2903                     Math.max(opt.minWidth || this.minWidth, bwidth)
2904             );
2905             if(opt.prompt){
2906                 activeTextEl.setWidth(w);
2907             }
2908             if(dlg.isVisible()){
2909                 dlg.fixedcenter = false;
2910             }
2911             // to big, make it scroll. = But as usual stupid IE does not support
2912             // !important..
2913             
2914             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2915                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2916                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2917             } else {
2918                 bodyEl.dom.style.height = '';
2919                 bodyEl.dom.style.overflowY = '';
2920             }
2921             if (cw > w) {
2922                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2923             } else {
2924                 bodyEl.dom.style.overflowX = '';
2925             }
2926             
2927             dlg.setContentSize(w, bodyEl.getHeight());
2928             if(dlg.isVisible()){
2929                 dlg.fixedcenter = true;
2930             }
2931             return this;
2932         },
2933
2934         /**
2935          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2936          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2937          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2938          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2939          * @return {Roo.MessageBox} This message box
2940          */
2941         updateProgress : function(value, text){
2942             if(text){
2943                 this.updateText(text);
2944             }
2945             if (pp) { // weird bug on my firefox - for some reason this is not defined
2946                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2947             }
2948             return this;
2949         },        
2950
2951         /**
2952          * Returns true if the message box is currently displayed
2953          * @return {Boolean} True if the message box is visible, else false
2954          */
2955         isVisible : function(){
2956             return dlg && dlg.isVisible();  
2957         },
2958
2959         /**
2960          * Hides the message box if it is displayed
2961          */
2962         hide : function(){
2963             if(this.isVisible()){
2964                 dlg.hide();
2965             }  
2966         },
2967
2968         /**
2969          * Displays a new message box, or reinitializes an existing message box, based on the config options
2970          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2971          * The following config object properties are supported:
2972          * <pre>
2973 Property    Type             Description
2974 ----------  ---------------  ------------------------------------------------------------------------------------
2975 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2976                                    closes (defaults to undefined)
2977 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2978                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2979 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2980                                    progress and wait dialogs will ignore this property and always hide the
2981                                    close button as they can only be closed programmatically.
2982 cls               String           A custom CSS class to apply to the message box element
2983 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2984                                    displayed (defaults to 75)
2985 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2986                                    function will be btn (the name of the button that was clicked, if applicable,
2987                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2988                                    Progress and wait dialogs will ignore this option since they do not respond to
2989                                    user actions and can only be closed programmatically, so any required function
2990                                    should be called by the same code after it closes the dialog.
2991 icon              String           A CSS class that provides a background image to be used as an icon for
2992                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2993 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2994 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2995 modal             Boolean          False to allow user interaction with the page while the message box is
2996                                    displayed (defaults to true)
2997 msg               String           A string that will replace the existing message box body text (defaults
2998                                    to the XHTML-compliant non-breaking space character '&#160;')
2999 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3000 progress          Boolean          True to display a progress bar (defaults to false)
3001 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3002 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3003 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3004 title             String           The title text
3005 value             String           The string value to set into the active textbox element if displayed
3006 wait              Boolean          True to display a progress bar (defaults to false)
3007 width             Number           The width of the dialog in pixels
3008 </pre>
3009          *
3010          * Example usage:
3011          * <pre><code>
3012 Roo.Msg.show({
3013    title: 'Address',
3014    msg: 'Please enter your address:',
3015    width: 300,
3016    buttons: Roo.MessageBox.OKCANCEL,
3017    multiline: true,
3018    fn: saveAddress,
3019    animEl: 'addAddressBtn'
3020 });
3021 </code></pre>
3022          * @param {Object} config Configuration options
3023          * @return {Roo.MessageBox} This message box
3024          */
3025         show : function(options)
3026         {
3027             
3028             // this causes nightmares if you show one dialog after another
3029             // especially on callbacks..
3030              
3031             if(this.isVisible()){
3032                 
3033                 this.hide();
3034                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3035                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3036                 Roo.log("New Dialog Message:" +  options.msg )
3037                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3038                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3039                 
3040             }
3041             var d = this.getDialog();
3042             opt = options;
3043             d.setTitle(opt.title || "&#160;");
3044             d.closeEl.setDisplayed(opt.closable !== false);
3045             activeTextEl = textboxEl;
3046             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3047             if(opt.prompt){
3048                 if(opt.multiline){
3049                     textboxEl.hide();
3050                     textareaEl.show();
3051                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3052                         opt.multiline : this.defaultTextHeight);
3053                     activeTextEl = textareaEl;
3054                 }else{
3055                     textboxEl.show();
3056                     textareaEl.hide();
3057                 }
3058             }else{
3059                 textboxEl.hide();
3060                 textareaEl.hide();
3061             }
3062             progressEl.setDisplayed(opt.progress === true);
3063             this.updateProgress(0);
3064             activeTextEl.dom.value = opt.value || "";
3065             if(opt.prompt){
3066                 dlg.setDefaultButton(activeTextEl);
3067             }else{
3068                 var bs = opt.buttons;
3069                 var db = null;
3070                 if(bs && bs.ok){
3071                     db = buttons["ok"];
3072                 }else if(bs && bs.yes){
3073                     db = buttons["yes"];
3074                 }
3075                 dlg.setDefaultButton(db);
3076             }
3077             bwidth = updateButtons(opt.buttons);
3078             this.updateText(opt.msg);
3079             if(opt.cls){
3080                 d.el.addClass(opt.cls);
3081             }
3082             d.proxyDrag = opt.proxyDrag === true;
3083             d.modal = opt.modal !== false;
3084             d.mask = opt.modal !== false ? mask : false;
3085             if(!d.isVisible()){
3086                 // force it to the end of the z-index stack so it gets a cursor in FF
3087                 document.body.appendChild(dlg.el.dom);
3088                 d.animateTarget = null;
3089                 d.show(options.animEl);
3090             }
3091             return this;
3092         },
3093
3094         /**
3095          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3096          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3097          * and closing the message box when the process is complete.
3098          * @param {String} title The title bar text
3099          * @param {String} msg The message box body text
3100          * @return {Roo.MessageBox} This message box
3101          */
3102         progress : function(title, msg){
3103             this.show({
3104                 title : title,
3105                 msg : msg,
3106                 buttons: false,
3107                 progress:true,
3108                 closable:false,
3109                 minWidth: this.minProgressWidth,
3110                 modal : true
3111             });
3112             return this;
3113         },
3114
3115         /**
3116          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3117          * If a callback function is passed it will be called after the user clicks the button, and the
3118          * id of the button that was clicked will be passed as the only parameter to the callback
3119          * (could also be the top-right close button).
3120          * @param {String} title The title bar text
3121          * @param {String} msg The message box body text
3122          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3123          * @param {Object} scope (optional) The scope of the callback function
3124          * @return {Roo.MessageBox} This message box
3125          */
3126         alert : function(title, msg, fn, scope){
3127             this.show({
3128                 title : title,
3129                 msg : msg,
3130                 buttons: this.OK,
3131                 fn: fn,
3132                 scope : scope,
3133                 modal : true
3134             });
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3140          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3141          * You are responsible for closing the message box when the process is complete.
3142          * @param {String} msg The message box body text
3143          * @param {String} title (optional) The title bar text
3144          * @return {Roo.MessageBox} This message box
3145          */
3146         wait : function(msg, title){
3147             this.show({
3148                 title : title,
3149                 msg : msg,
3150                 buttons: false,
3151                 closable:false,
3152                 progress:true,
3153                 modal:true,
3154                 width:300,
3155                 wait:true
3156             });
3157             waitTimer = Roo.TaskMgr.start({
3158                 run: function(i){
3159                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3160                 },
3161                 interval: 1000
3162             });
3163             return this;
3164         },
3165
3166         /**
3167          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3168          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3169          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3170          * @param {String} title The title bar text
3171          * @param {String} msg The message box body text
3172          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3173          * @param {Object} scope (optional) The scope of the callback function
3174          * @return {Roo.MessageBox} This message box
3175          */
3176         confirm : function(title, msg, fn, scope){
3177             this.show({
3178                 title : title,
3179                 msg : msg,
3180                 buttons: this.YESNO,
3181                 fn: fn,
3182                 scope : scope,
3183                 modal : true
3184             });
3185             return this;
3186         },
3187
3188         /**
3189          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3190          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3191          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3192          * (could also be the top-right close button) and the text that was entered will be passed as the two
3193          * parameters to the callback.
3194          * @param {String} title The title bar text
3195          * @param {String} msg The message box body text
3196          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3197          * @param {Object} scope (optional) The scope of the callback function
3198          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3199          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3200          * @return {Roo.MessageBox} This message box
3201          */
3202         prompt : function(title, msg, fn, scope, multiline){
3203             this.show({
3204                 title : title,
3205                 msg : msg,
3206                 buttons: this.OKCANCEL,
3207                 fn: fn,
3208                 minWidth:250,
3209                 scope : scope,
3210                 prompt:true,
3211                 multiline: multiline,
3212                 modal : true
3213             });
3214             return this;
3215         },
3216
3217         /**
3218          * Button config that displays a single OK button
3219          * @type Object
3220          */
3221         OK : {ok:true},
3222         /**
3223          * Button config that displays Yes and No buttons
3224          * @type Object
3225          */
3226         YESNO : {yes:true, no:true},
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : {ok:true, cancel:true},
3232         /**
3233          * Button config that displays Yes, No and Cancel buttons
3234          * @type Object
3235          */
3236         YESNOCANCEL : {yes:true, no:true, cancel:true},
3237
3238         /**
3239          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3240          * @type Number
3241          */
3242         defaultTextHeight : 75,
3243         /**
3244          * The maximum width in pixels of the message box (defaults to 600)
3245          * @type Number
3246          */
3247         maxWidth : 600,
3248         /**
3249          * The minimum width in pixels of the message box (defaults to 100)
3250          * @type Number
3251          */
3252         minWidth : 100,
3253         /**
3254          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3255          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3256          * @type Number
3257          */
3258         minProgressWidth : 250,
3259         /**
3260          * An object containing the default button text strings that can be overriden for localized language support.
3261          * Supported properties are: ok, cancel, yes and no.
3262          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3263          * @type Object
3264          */
3265         buttonText : {
3266             ok : "OK",
3267             cancel : "Cancel",
3268             yes : "Yes",
3269             no : "No"
3270         }
3271     };
3272 }();
3273
3274 /**
3275  * Shorthand for {@link Roo.MessageBox}
3276  */
3277 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3278 Roo.Msg = Roo.Msg || Roo.MessageBox;
3279 /*
3280  * - LGPL
3281  *
3282  * navbar
3283  * 
3284  */
3285
3286 /**
3287  * @class Roo.bootstrap.Navbar
3288  * @extends Roo.bootstrap.Component
3289  * Bootstrap Navbar class
3290
3291  * @constructor
3292  * Create a new Navbar
3293  * @param {Object} config The config object
3294  */
3295
3296
3297 Roo.bootstrap.Navbar = function(config){
3298     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3299     
3300 };
3301
3302 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3303     
3304     
3305    
3306     // private
3307     navItems : false,
3308     loadMask : false,
3309     
3310     
3311     getAutoCreate : function(){
3312         
3313         
3314         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3315         
3316     },
3317     
3318     initEvents :function ()
3319     {
3320         //Roo.log(this.el.select('.navbar-toggle',true));
3321         this.el.select('.navbar-toggle',true).on('click', function() {
3322            // Roo.log('click');
3323             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3324         }, this);
3325         
3326         var mark = {
3327             tag: "div",
3328             cls:"x-dlg-mask"
3329         }
3330         
3331         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3332         
3333         var size = this.el.getSize();
3334         this.maskEl.setSize(size.width, size.height);
3335         this.maskEl.enableDisplayMode("block");
3336         this.maskEl.hide();
3337         
3338         if(this.loadMask){
3339             this.maskEl.show();
3340         }
3341     },
3342     
3343     
3344     getChildContainer : function()
3345     {
3346         if (this.el.select('.collapse').getCount()) {
3347             return this.el.select('.collapse',true).first();
3348         }
3349         
3350         return this.el;
3351     },
3352     
3353     mask : function()
3354     {
3355         this.maskEl.show();
3356     },
3357     
3358     unmask : function()
3359     {
3360         this.maskEl.hide();
3361     } 
3362     
3363     
3364     
3365     
3366 });
3367
3368
3369
3370  
3371
3372  /*
3373  * - LGPL
3374  *
3375  * navbar
3376  * 
3377  */
3378
3379 /**
3380  * @class Roo.bootstrap.NavSimplebar
3381  * @extends Roo.bootstrap.Navbar
3382  * Bootstrap Sidebar class
3383  *
3384  * @cfg {Boolean} inverse is inverted color
3385  * 
3386  * @cfg {String} type (nav | pills | tabs)
3387  * @cfg {Boolean} arrangement stacked | justified
3388  * @cfg {String} align (left | right) alignment
3389  * 
3390  * @cfg {Boolean} main (true|false) main nav bar? default false
3391  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3392  * 
3393  * @cfg {String} tag (header|footer|nav|div) default is nav 
3394
3395  * 
3396  * 
3397  * 
3398  * @constructor
3399  * Create a new Sidebar
3400  * @param {Object} config The config object
3401  */
3402
3403
3404 Roo.bootstrap.NavSimplebar = function(config){
3405     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3406 };
3407
3408 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3409     
3410     inverse: false,
3411     
3412     type: false,
3413     arrangement: '',
3414     align : false,
3415     
3416     
3417     
3418     main : false,
3419     
3420     
3421     tag : false,
3422     
3423     
3424     getAutoCreate : function(){
3425         
3426         
3427         var cfg = {
3428             tag : this.tag || 'div',
3429             cls : 'navbar'
3430         };
3431           
3432         
3433         cfg.cn = [
3434             {
3435                 cls: 'nav',
3436                 tag : 'ul'
3437             }
3438         ];
3439         
3440          
3441         this.type = this.type || 'nav';
3442         if (['tabs','pills'].indexOf(this.type)!==-1) {
3443             cfg.cn[0].cls += ' nav-' + this.type
3444         
3445         
3446         } else {
3447             if (this.type!=='nav') {
3448                 Roo.log('nav type must be nav/tabs/pills')
3449             }
3450             cfg.cn[0].cls += ' navbar-nav'
3451         }
3452         
3453         
3454         
3455         
3456         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3457             cfg.cn[0].cls += ' nav-' + this.arrangement;
3458         }
3459         
3460         
3461         if (this.align === 'right') {
3462             cfg.cn[0].cls += ' navbar-right';
3463         }
3464         
3465         if (this.inverse) {
3466             cfg.cls += ' navbar-inverse';
3467             
3468         }
3469         
3470         
3471         return cfg;
3472     
3473         
3474     }
3475     
3476     
3477     
3478 });
3479
3480
3481
3482  
3483
3484  
3485        /*
3486  * - LGPL
3487  *
3488  * navbar
3489  * 
3490  */
3491
3492 /**
3493  * @class Roo.bootstrap.NavHeaderbar
3494  * @extends Roo.bootstrap.NavSimplebar
3495  * Bootstrap Sidebar class
3496  *
3497  * @cfg {String} brand what is brand
3498  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3499  * @cfg {String} brand_href href of the brand
3500  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3501  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3502  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3503  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3504  * 
3505  * @constructor
3506  * Create a new Sidebar
3507  * @param {Object} config The config object
3508  */
3509
3510
3511 Roo.bootstrap.NavHeaderbar = function(config){
3512     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3513       
3514 };
3515
3516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3517     
3518     position: '',
3519     brand: '',
3520     brand_href: false,
3521     srButton : true,
3522     autohide : false,
3523     desktopCenter : false,
3524    
3525     
3526     getAutoCreate : function(){
3527         
3528         var   cfg = {
3529             tag: this.nav || 'nav',
3530             cls: 'navbar',
3531             role: 'navigation',
3532             cn: []
3533         };
3534         
3535         var cn = cfg.cn;
3536         if (this.desktopCenter) {
3537             cn.push({cls : 'container', cn : []});
3538             cn = cn[0].cn;
3539         }
3540         
3541         if(this.srButton){
3542             cn.push({
3543                 tag: 'div',
3544                 cls: 'navbar-header',
3545                 cn: [
3546                     {
3547                         tag: 'button',
3548                         type: 'button',
3549                         cls: 'navbar-toggle',
3550                         'data-toggle': 'collapse',
3551                         cn: [
3552                             {
3553                                 tag: 'span',
3554                                 cls: 'sr-only',
3555                                 html: 'Toggle navigation'
3556                             },
3557                             {
3558                                 tag: 'span',
3559                                 cls: 'icon-bar'
3560                             },
3561                             {
3562                                 tag: 'span',
3563                                 cls: 'icon-bar'
3564                             },
3565                             {
3566                                 tag: 'span',
3567                                 cls: 'icon-bar'
3568                             }
3569                         ]
3570                     }
3571                 ]
3572             });
3573         }
3574         
3575         cn.push({
3576             tag: 'div',
3577             cls: 'collapse navbar-collapse',
3578             cn : []
3579         });
3580         
3581         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3582         
3583         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3584             cfg.cls += ' navbar-' + this.position;
3585             
3586             // tag can override this..
3587             
3588             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3589         }
3590         
3591         if (this.brand !== '') {
3592             cn[0].cn.push({
3593                 tag: 'a',
3594                 href: this.brand_href ? this.brand_href : '#',
3595                 cls: 'navbar-brand',
3596                 cn: [
3597                 this.brand
3598                 ]
3599             });
3600         }
3601         
3602         if(this.main){
3603             cfg.cls += ' main-nav';
3604         }
3605         
3606         
3607         return cfg;
3608
3609         
3610     },
3611     getHeaderChildContainer : function()
3612     {
3613         if (this.el.select('.navbar-header').getCount()) {
3614             return this.el.select('.navbar-header',true).first();
3615         }
3616         
3617         return this.getChildContainer();
3618     },
3619     
3620     
3621     initEvents : function()
3622     {
3623         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3624         
3625         if (this.autohide) {
3626             
3627             var prevScroll = 0;
3628             var ft = this.el;
3629             
3630             Roo.get(document).on('scroll',function(e) {
3631                 var ns = Roo.get(document).getScroll().top;
3632                 var os = prevScroll;
3633                 prevScroll = ns;
3634                 
3635                 if(ns > os){
3636                     ft.removeClass('slideDown');
3637                     ft.addClass('slideUp');
3638                     return;
3639                 }
3640                 ft.removeClass('slideUp');
3641                 ft.addClass('slideDown');
3642                  
3643               
3644           },this);
3645         }
3646     }    
3647     
3648 });
3649
3650
3651
3652  
3653
3654  /*
3655  * - LGPL
3656  *
3657  * navbar
3658  * 
3659  */
3660
3661 /**
3662  * @class Roo.bootstrap.NavSidebar
3663  * @extends Roo.bootstrap.Navbar
3664  * Bootstrap Sidebar class
3665  * 
3666  * @constructor
3667  * Create a new Sidebar
3668  * @param {Object} config The config object
3669  */
3670
3671
3672 Roo.bootstrap.NavSidebar = function(config){
3673     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3674 };
3675
3676 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3677     
3678     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3679     
3680     getAutoCreate : function(){
3681         
3682         
3683         return  {
3684             tag: 'div',
3685             cls: 'sidebar sidebar-nav'
3686         };
3687     
3688         
3689     }
3690     
3691     
3692     
3693 });
3694
3695
3696
3697  
3698
3699  /*
3700  * - LGPL
3701  *
3702  * nav group
3703  * 
3704  */
3705
3706 /**
3707  * @class Roo.bootstrap.NavGroup
3708  * @extends Roo.bootstrap.Component
3709  * Bootstrap NavGroup class
3710  * @cfg {String} align (left|right)
3711  * @cfg {Boolean} inverse
3712  * @cfg {String} type (nav|pills|tab) default nav
3713  * @cfg {String} navId - reference Id for navbar.
3714
3715  * 
3716  * @constructor
3717  * Create a new nav group
3718  * @param {Object} config The config object
3719  */
3720
3721 Roo.bootstrap.NavGroup = function(config){
3722     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3723     this.navItems = [];
3724    
3725     Roo.bootstrap.NavGroup.register(this);
3726      this.addEvents({
3727         /**
3728              * @event changed
3729              * Fires when the active item changes
3730              * @param {Roo.bootstrap.NavGroup} this
3731              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3732              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3733          */
3734         'changed': true
3735      });
3736     
3737 };
3738
3739 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3740     
3741     align: '',
3742     inverse: false,
3743     form: false,
3744     type: 'nav',
3745     navId : '',
3746     // private
3747     
3748     navItems : false, 
3749     
3750     getAutoCreate : function()
3751     {
3752         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3753         
3754         cfg = {
3755             tag : 'ul',
3756             cls: 'nav' 
3757         }
3758         
3759         if (['tabs','pills'].indexOf(this.type)!==-1) {
3760             cfg.cls += ' nav-' + this.type
3761         } else {
3762             if (this.type!=='nav') {
3763                 Roo.log('nav type must be nav/tabs/pills')
3764             }
3765             cfg.cls += ' navbar-nav'
3766         }
3767         
3768         if (this.parent().sidebar) {
3769             cfg = {
3770                 tag: 'ul',
3771                 cls: 'dashboard-menu sidebar-menu'
3772             }
3773             
3774             return cfg;
3775         }
3776         
3777         if (this.form === true) {
3778             cfg = {
3779                 tag: 'form',
3780                 cls: 'navbar-form'
3781             }
3782             
3783             if (this.align === 'right') {
3784                 cfg.cls += ' navbar-right';
3785             } else {
3786                 cfg.cls += ' navbar-left';
3787             }
3788         }
3789         
3790         if (this.align === 'right') {
3791             cfg.cls += ' navbar-right';
3792         }
3793         
3794         if (this.inverse) {
3795             cfg.cls += ' navbar-inverse';
3796             
3797         }
3798         
3799         
3800         return cfg;
3801     },
3802     /**
3803     * sets the active Navigation item
3804     * @param {Roo.bootstrap.NavItem} the new current navitem
3805     */
3806     setActiveItem : function(item)
3807     {
3808         var prev = false;
3809         Roo.each(this.navItems, function(v){
3810             if (v == item) {
3811                 return ;
3812             }
3813             if (v.isActive()) {
3814                 v.setActive(false, true);
3815                 prev = v;
3816                 
3817             }
3818             
3819         });
3820
3821         item.setActive(true, true);
3822         this.fireEvent('changed', this, item, prev);
3823         
3824         
3825     },
3826     /**
3827     * gets the active Navigation item
3828     * @return {Roo.bootstrap.NavItem} the current navitem
3829     */
3830     getActive : function()
3831     {
3832         
3833         var prev = false;
3834         Roo.each(this.navItems, function(v){
3835             
3836             if (v.isActive()) {
3837                 prev = v;
3838                 
3839             }
3840             
3841         });
3842         return prev;
3843     },
3844     
3845     indexOfNav : function()
3846     {
3847         
3848         var prev = false;
3849         Roo.each(this.navItems, function(v,i){
3850             
3851             if (v.isActive()) {
3852                 prev = i;
3853                 
3854             }
3855             
3856         });
3857         return prev;
3858     },
3859     /**
3860     * adds a Navigation item
3861     * @param {Roo.bootstrap.NavItem} the navitem to add
3862     */
3863     addItem : function(cfg)
3864     {
3865         var cn = new Roo.bootstrap.NavItem(cfg);
3866         this.register(cn);
3867         cn.parentId = this.id;
3868         cn.onRender(this.el, null);
3869         return cn;
3870     },
3871     /**
3872     * register a Navigation item
3873     * @param {Roo.bootstrap.NavItem} the navitem to add
3874     */
3875     register : function(item)
3876     {
3877         this.navItems.push( item);
3878         item.navId = this.navId;
3879     
3880     },
3881     
3882     /**
3883     * clear all the Navigation item
3884     */
3885    
3886     clearAll : function()
3887     {
3888         this.navItems = [];
3889         this.el.dom.innerHTML = '';
3890     },
3891     
3892     getNavItem: function(tabId)
3893     {
3894         var ret = false;
3895         Roo.each(this.navItems, function(e) {
3896             if (e.tabId == tabId) {
3897                ret =  e;
3898                return false;
3899             }
3900             return true;
3901             
3902         });
3903         return ret;
3904     },
3905     
3906     setActiveNext : function()
3907     {
3908         var i = this.indexOfNav(this.getActive());
3909         if (i > this.navItems.length) {
3910             return;
3911         }
3912         this.setActiveItem(this.navItems[i+1]);
3913     },
3914     setActivePrev : function()
3915     {
3916         var i = this.indexOfNav(this.getActive());
3917         if (i  < 1) {
3918             return;
3919         }
3920         this.setActiveItem(this.navItems[i-1]);
3921     },
3922     clearWasActive : function(except) {
3923         Roo.each(this.navItems, function(e) {
3924             if (e.tabId != except.tabId && e.was_active) {
3925                e.was_active = false;
3926                return false;
3927             }
3928             return true;
3929             
3930         });
3931     },
3932     getWasActive : function ()
3933     {
3934         var r = false;
3935         Roo.each(this.navItems, function(e) {
3936             if (e.was_active) {
3937                r = e;
3938                return false;
3939             }
3940             return true;
3941             
3942         });
3943         return r;
3944     }
3945     
3946     
3947 });
3948
3949  
3950 Roo.apply(Roo.bootstrap.NavGroup, {
3951     
3952     groups: {},
3953      /**
3954     * register a Navigation Group
3955     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3956     */
3957     register : function(navgrp)
3958     {
3959         this.groups[navgrp.navId] = navgrp;
3960         
3961     },
3962     /**
3963     * fetch a Navigation Group based on the navigation ID
3964     * @param {string} the navgroup to add
3965     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3966     */
3967     get: function(navId) {
3968         if (typeof(this.groups[navId]) == 'undefined') {
3969             return false;
3970             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3971         }
3972         return this.groups[navId] ;
3973     }
3974     
3975     
3976     
3977 });
3978
3979  /*
3980  * - LGPL
3981  *
3982  * row
3983  * 
3984  */
3985
3986 /**
3987  * @class Roo.bootstrap.NavItem
3988  * @extends Roo.bootstrap.Component
3989  * Bootstrap Navbar.NavItem class
3990  * @cfg {String} href  link to
3991  * @cfg {String} html content of button
3992  * @cfg {String} badge text inside badge
3993  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3994  * @cfg {String} glyphicon name of glyphicon
3995  * @cfg {String} icon name of font awesome icon
3996  * @cfg {Boolean} active Is item active
3997  * @cfg {Boolean} disabled Is item disabled
3998  
3999  * @cfg {Boolean} preventDefault (true | false) default false
4000  * @cfg {String} tabId the tab that this item activates.
4001  * @cfg {String} tagtype (a|span) render as a href or span?
4002  * @cfg {Boolean} animateRef (true|false) link to element default false  
4003   
4004  * @constructor
4005  * Create a new Navbar Item
4006  * @param {Object} config The config object
4007  */
4008 Roo.bootstrap.NavItem = function(config){
4009     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4010     this.addEvents({
4011         // raw events
4012         /**
4013          * @event click
4014          * The raw click event for the entire grid.
4015          * @param {Roo.EventObject} e
4016          */
4017         "click" : true,
4018          /**
4019             * @event changed
4020             * Fires when the active item active state changes
4021             * @param {Roo.bootstrap.NavItem} this
4022             * @param {boolean} state the new state
4023              
4024          */
4025         'changed': true,
4026         /**
4027             * @event scrollto
4028             * Fires when scroll to element
4029             * @param {Roo.bootstrap.NavItem} this
4030             * @param {Object} options
4031             * @param {Roo.EventObject} e
4032              
4033          */
4034         'scrollto': true
4035     });
4036    
4037 };
4038
4039 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4040     
4041     href: false,
4042     html: '',
4043     badge: '',
4044     icon: false,
4045     glyphicon: false,
4046     active: false,
4047     preventDefault : false,
4048     tabId : false,
4049     tagtype : 'a',
4050     disabled : false,
4051     animateRef : false,
4052     was_active : false,
4053     
4054     getAutoCreate : function(){
4055          
4056         var cfg = {
4057             tag: 'li',
4058             cls: 'nav-item'
4059             
4060         }
4061         if (this.active) {
4062             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4063         }
4064         if (this.disabled) {
4065             cfg.cls += ' disabled';
4066         }
4067         
4068         if (this.href || this.html || this.glyphicon || this.icon) {
4069             cfg.cn = [
4070                 {
4071                     tag: this.tagtype,
4072                     href : this.href || "#",
4073                     html: this.html || ''
4074                 }
4075             ];
4076             
4077             if (this.icon) {
4078                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4079             }
4080
4081             if(this.glyphicon) {
4082                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4083             }
4084             
4085             if (this.menu) {
4086                 
4087                 cfg.cn[0].html += " <span class='caret'></span>";
4088              
4089             }
4090             
4091             if (this.badge !== '') {
4092                  
4093                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4094             }
4095         }
4096         
4097         
4098         
4099         return cfg;
4100     },
4101     initEvents: function() 
4102     {
4103         if (typeof (this.menu) != 'undefined') {
4104             this.menu.parentType = this.xtype;
4105             this.menu.triggerEl = this.el;
4106             this.menu = this.addxtype(Roo.apply({}, this.menu));
4107         }
4108         
4109         this.el.select('a',true).on('click', this.onClick, this);
4110         
4111         if(this.tagtype == 'span'){
4112             this.el.select('span',true).on('click', this.onClick, this);
4113         }
4114        
4115         // at this point parent should be available..
4116         this.parent().register(this);
4117     },
4118     
4119     onClick : function(e)
4120     {
4121         if(
4122                 this.preventDefault || 
4123                 this.href == '#' 
4124         ){
4125             
4126             e.preventDefault();
4127         }
4128         
4129         if (this.disabled) {
4130             return;
4131         }
4132         
4133         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4134         if (tg && tg.transition) {
4135             Roo.log("waiting for the transitionend");
4136             return;
4137         }
4138         
4139         
4140         
4141         //Roo.log("fire event clicked");
4142         if(this.fireEvent('click', this, e) === false){
4143             return;
4144         };
4145         
4146         if(this.tagtype == 'span'){
4147             return;
4148         }
4149         
4150         //Roo.log(this.href);
4151         var ael = this.el.select('a',true).first();
4152         //Roo.log(ael);
4153         
4154         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4155             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4156             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4157                 return; // ignore... - it's a 'hash' to another page.
4158             }
4159             
4160             e.preventDefault();
4161             this.scrollToElement(e);
4162         }
4163         
4164         
4165         var p =  this.parent();
4166    
4167         if (['tabs','pills'].indexOf(p.type)!==-1) {
4168             if (typeof(p.setActiveItem) !== 'undefined') {
4169                 p.setActiveItem(this);
4170             }
4171         }
4172         
4173         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4174         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4175             // remove the collapsed menu expand...
4176             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4177         }
4178     },
4179     
4180     isActive: function () {
4181         return this.active
4182     },
4183     setActive : function(state, fire, is_was_active)
4184     {
4185         if (this.active && !state & this.navId) {
4186             this.was_active = true;
4187             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4188             if (nv) {
4189                 nv.clearWasActive(this);
4190             }
4191             
4192         }
4193         this.active = state;
4194         
4195         if (!state ) {
4196             this.el.removeClass('active');
4197         } else if (!this.el.hasClass('active')) {
4198             this.el.addClass('active');
4199         }
4200         if (fire) {
4201             this.fireEvent('changed', this, state);
4202         }
4203         
4204         // show a panel if it's registered and related..
4205         
4206         if (!this.navId || !this.tabId || !state || is_was_active) {
4207             return;
4208         }
4209         
4210         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4211         if (!tg) {
4212             return;
4213         }
4214         var pan = tg.getPanelByName(this.tabId);
4215         if (!pan) {
4216             return;
4217         }
4218         // if we can not flip to new panel - go back to old nav highlight..
4219         if (false == tg.showPanel(pan)) {
4220             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4221             if (nv) {
4222                 var onav = nv.getWasActive();
4223                 if (onav) {
4224                     onav.setActive(true, false, true);
4225                 }
4226             }
4227             
4228         }
4229         
4230         
4231         
4232     },
4233      // this should not be here...
4234     setDisabled : function(state)
4235     {
4236         this.disabled = state;
4237         if (!state ) {
4238             this.el.removeClass('disabled');
4239         } else if (!this.el.hasClass('disabled')) {
4240             this.el.addClass('disabled');
4241         }
4242         
4243     },
4244     
4245     /**
4246      * Fetch the element to display the tooltip on.
4247      * @return {Roo.Element} defaults to this.el
4248      */
4249     tooltipEl : function()
4250     {
4251         return this.el.select('' + this.tagtype + '', true).first();
4252     },
4253     
4254     scrollToElement : function(e)
4255     {
4256         var c = document.body;
4257         
4258         /*
4259          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4260          */
4261         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4262             c = document.documentElement;
4263         }
4264         
4265         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4266         
4267         if(!target){
4268             return;
4269         }
4270
4271         var o = target.calcOffsetsTo(c);
4272         
4273         var options = {
4274             target : target,
4275             value : o[1]
4276         }
4277         
4278         this.fireEvent('scrollto', this, options, e);
4279         
4280         Roo.get(c).scrollTo('top', options.value, true);
4281         
4282         return;
4283     }
4284 });
4285  
4286
4287  /*
4288  * - LGPL
4289  *
4290  * sidebar item
4291  *
4292  *  li
4293  *    <span> icon </span>
4294  *    <span> text </span>
4295  *    <span>badge </span>
4296  */
4297
4298 /**
4299  * @class Roo.bootstrap.NavSidebarItem
4300  * @extends Roo.bootstrap.NavItem
4301  * Bootstrap Navbar.NavSidebarItem class
4302  * @constructor
4303  * Create a new Navbar Button
4304  * @param {Object} config The config object
4305  */
4306 Roo.bootstrap.NavSidebarItem = function(config){
4307     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4308     this.addEvents({
4309         // raw events
4310         /**
4311          * @event click
4312          * The raw click event for the entire grid.
4313          * @param {Roo.EventObject} e
4314          */
4315         "click" : true,
4316          /**
4317             * @event changed
4318             * Fires when the active item active state changes
4319             * @param {Roo.bootstrap.NavSidebarItem} this
4320             * @param {boolean} state the new state
4321              
4322          */
4323         'changed': true
4324     });
4325    
4326 };
4327
4328 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4329     
4330     
4331     getAutoCreate : function(){
4332         
4333         
4334         var a = {
4335                 tag: 'a',
4336                 href : this.href || '#',
4337                 cls: '',
4338                 html : '',
4339                 cn : []
4340         };
4341         var cfg = {
4342             tag: 'li',
4343             cls: '',
4344             cn: [ a ]
4345         }
4346         var span = {
4347             tag: 'span',
4348             html : this.html || ''
4349         }
4350         
4351         
4352         if (this.active) {
4353             cfg.cls += ' active';
4354         }
4355         
4356         // left icon..
4357         if (this.glyphicon || this.icon) {
4358             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4359             a.cn.push({ tag : 'i', cls : c }) ;
4360         }
4361         // html..
4362         a.cn.push(span);
4363         // then badge..
4364         if (this.badge !== '') {
4365             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4366         }
4367         // fi
4368         if (this.menu) {
4369             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4370             a.cls += 'dropdown-toggle treeview' ;
4371             
4372         }
4373         
4374         
4375         
4376         return cfg;
4377          
4378            
4379     }
4380    
4381      
4382  
4383 });
4384  
4385
4386  /*
4387  * - LGPL
4388  *
4389  * row
4390  * 
4391  */
4392
4393 /**
4394  * @class Roo.bootstrap.Row
4395  * @extends Roo.bootstrap.Component
4396  * Bootstrap Row class (contains columns...)
4397  * 
4398  * @constructor
4399  * Create a new Row
4400  * @param {Object} config The config object
4401  */
4402
4403 Roo.bootstrap.Row = function(config){
4404     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4405 };
4406
4407 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4408     
4409     getAutoCreate : function(){
4410        return {
4411             cls: 'row clearfix'
4412        };
4413     }
4414     
4415     
4416 });
4417
4418  
4419
4420  /*
4421  * - LGPL
4422  *
4423  * element
4424  * 
4425  */
4426
4427 /**
4428  * @class Roo.bootstrap.Element
4429  * @extends Roo.bootstrap.Component
4430  * Bootstrap Element class
4431  * @cfg {String} html contents of the element
4432  * @cfg {String} tag tag of the element
4433  * @cfg {String} cls class of the element
4434  * @cfg {Boolean} preventDefault (true|false) default false
4435  * @cfg {Boolean} clickable (true|false) default false
4436  * 
4437  * @constructor
4438  * Create a new Element
4439  * @param {Object} config The config object
4440  */
4441
4442 Roo.bootstrap.Element = function(config){
4443     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4444     
4445     this.addEvents({
4446         // raw events
4447         /**
4448          * @event click
4449          * When a element is chick
4450          * @param {Roo.bootstrap.Element} this
4451          * @param {Roo.EventObject} e
4452          */
4453         "click" : true
4454     });
4455 };
4456
4457 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4458     
4459     tag: 'div',
4460     cls: '',
4461     html: '',
4462     preventDefault: false, 
4463     clickable: false,
4464     
4465     getAutoCreate : function(){
4466         
4467         var cfg = {
4468             tag: this.tag,
4469             cls: this.cls,
4470             html: this.html
4471         }
4472         
4473         return cfg;
4474     },
4475     
4476     initEvents: function() 
4477     {
4478         Roo.bootstrap.Element.superclass.initEvents.call(this);
4479         
4480         if(this.clickable){
4481             this.el.on('click', this.onClick, this);
4482         }
4483         
4484     },
4485     
4486     onClick : function(e)
4487     {
4488         if(this.preventDefault){
4489             e.preventDefault();
4490         }
4491         
4492         this.fireEvent('click', this, e);
4493     },
4494     
4495     getValue : function()
4496     {
4497         return this.el.dom.innerHTML;
4498     },
4499     
4500     setValue : function(value)
4501     {
4502         this.el.dom.innerHTML = value;
4503     }
4504    
4505 });
4506
4507  
4508
4509  /*
4510  * - LGPL
4511  *
4512  * pagination
4513  * 
4514  */
4515
4516 /**
4517  * @class Roo.bootstrap.Pagination
4518  * @extends Roo.bootstrap.Component
4519  * Bootstrap Pagination class
4520  * @cfg {String} size xs | sm | md | lg
4521  * @cfg {Boolean} inverse false | true
4522  * 
4523  * @constructor
4524  * Create a new Pagination
4525  * @param {Object} config The config object
4526  */
4527
4528 Roo.bootstrap.Pagination = function(config){
4529     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4530 };
4531
4532 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4533     
4534     cls: false,
4535     size: false,
4536     inverse: false,
4537     
4538     getAutoCreate : function(){
4539         var cfg = {
4540             tag: 'ul',
4541                 cls: 'pagination'
4542         };
4543         if (this.inverse) {
4544             cfg.cls += ' inverse';
4545         }
4546         if (this.html) {
4547             cfg.html=this.html;
4548         }
4549         if (this.cls) {
4550             cfg.cls += " " + this.cls;
4551         }
4552         return cfg;
4553     }
4554    
4555 });
4556
4557  
4558
4559  /*
4560  * - LGPL
4561  *
4562  * Pagination item
4563  * 
4564  */
4565
4566
4567 /**
4568  * @class Roo.bootstrap.PaginationItem
4569  * @extends Roo.bootstrap.Component
4570  * Bootstrap PaginationItem class
4571  * @cfg {String} html text
4572  * @cfg {String} href the link
4573  * @cfg {Boolean} preventDefault (true | false) default true
4574  * @cfg {Boolean} active (true | false) default false
4575  * @cfg {Boolean} disabled default false
4576  * 
4577  * 
4578  * @constructor
4579  * Create a new PaginationItem
4580  * @param {Object} config The config object
4581  */
4582
4583
4584 Roo.bootstrap.PaginationItem = function(config){
4585     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4586     this.addEvents({
4587         // raw events
4588         /**
4589          * @event click
4590          * The raw click event for the entire grid.
4591          * @param {Roo.EventObject} e
4592          */
4593         "click" : true
4594     });
4595 };
4596
4597 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4598     
4599     href : false,
4600     html : false,
4601     preventDefault: true,
4602     active : false,
4603     cls : false,
4604     disabled: false,
4605     
4606     getAutoCreate : function(){
4607         var cfg= {
4608             tag: 'li',
4609             cn: [
4610                 {
4611                     tag : 'a',
4612                     href : this.href ? this.href : '#',
4613                     html : this.html ? this.html : ''
4614                 }
4615             ]
4616         };
4617         
4618         if(this.cls){
4619             cfg.cls = this.cls;
4620         }
4621         
4622         if(this.disabled){
4623             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4624         }
4625         
4626         if(this.active){
4627             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4628         }
4629         
4630         return cfg;
4631     },
4632     
4633     initEvents: function() {
4634         
4635         this.el.on('click', this.onClick, this);
4636         
4637     },
4638     onClick : function(e)
4639     {
4640         Roo.log('PaginationItem on click ');
4641         if(this.preventDefault){
4642             e.preventDefault();
4643         }
4644         
4645         if(this.disabled){
4646             return;
4647         }
4648         
4649         this.fireEvent('click', this, e);
4650     }
4651    
4652 });
4653
4654  
4655
4656  /*
4657  * - LGPL
4658  *
4659  * slider
4660  * 
4661  */
4662
4663
4664 /**
4665  * @class Roo.bootstrap.Slider
4666  * @extends Roo.bootstrap.Component
4667  * Bootstrap Slider class
4668  *    
4669  * @constructor
4670  * Create a new Slider
4671  * @param {Object} config The config object
4672  */
4673
4674 Roo.bootstrap.Slider = function(config){
4675     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4676 };
4677
4678 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4679     
4680     getAutoCreate : function(){
4681         
4682         var cfg = {
4683             tag: 'div',
4684             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4685             cn: [
4686                 {
4687                     tag: 'a',
4688                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4689                 }
4690             ]
4691         }
4692         
4693         return cfg;
4694     }
4695    
4696 });
4697
4698  /*
4699  * Based on:
4700  * Ext JS Library 1.1.1
4701  * Copyright(c) 2006-2007, Ext JS, LLC.
4702  *
4703  * Originally Released Under LGPL - original licence link has changed is not relivant.
4704  *
4705  * Fork - LGPL
4706  * <script type="text/javascript">
4707  */
4708  
4709
4710 /**
4711  * @class Roo.grid.ColumnModel
4712  * @extends Roo.util.Observable
4713  * This is the default implementation of a ColumnModel used by the Grid. It defines
4714  * the columns in the grid.
4715  * <br>Usage:<br>
4716  <pre><code>
4717  var colModel = new Roo.grid.ColumnModel([
4718         {header: "Ticker", width: 60, sortable: true, locked: true},
4719         {header: "Company Name", width: 150, sortable: true},
4720         {header: "Market Cap.", width: 100, sortable: true},
4721         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4722         {header: "Employees", width: 100, sortable: true, resizable: false}
4723  ]);
4724  </code></pre>
4725  * <p>
4726  
4727  * The config options listed for this class are options which may appear in each
4728  * individual column definition.
4729  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4730  * @constructor
4731  * @param {Object} config An Array of column config objects. See this class's
4732  * config objects for details.
4733 */
4734 Roo.grid.ColumnModel = function(config){
4735         /**
4736      * The config passed into the constructor
4737      */
4738     this.config = config;
4739     this.lookup = {};
4740
4741     // if no id, create one
4742     // if the column does not have a dataIndex mapping,
4743     // map it to the order it is in the config
4744     for(var i = 0, len = config.length; i < len; i++){
4745         var c = config[i];
4746         if(typeof c.dataIndex == "undefined"){
4747             c.dataIndex = i;
4748         }
4749         if(typeof c.renderer == "string"){
4750             c.renderer = Roo.util.Format[c.renderer];
4751         }
4752         if(typeof c.id == "undefined"){
4753             c.id = Roo.id();
4754         }
4755         if(c.editor && c.editor.xtype){
4756             c.editor  = Roo.factory(c.editor, Roo.grid);
4757         }
4758         if(c.editor && c.editor.isFormField){
4759             c.editor = new Roo.grid.GridEditor(c.editor);
4760         }
4761         this.lookup[c.id] = c;
4762     }
4763
4764     /**
4765      * The width of columns which have no width specified (defaults to 100)
4766      * @type Number
4767      */
4768     this.defaultWidth = 100;
4769
4770     /**
4771      * Default sortable of columns which have no sortable specified (defaults to false)
4772      * @type Boolean
4773      */
4774     this.defaultSortable = false;
4775
4776     this.addEvents({
4777         /**
4778              * @event widthchange
4779              * Fires when the width of a column changes.
4780              * @param {ColumnModel} this
4781              * @param {Number} columnIndex The column index
4782              * @param {Number} newWidth The new width
4783              */
4784             "widthchange": true,
4785         /**
4786              * @event headerchange
4787              * Fires when the text of a header changes.
4788              * @param {ColumnModel} this
4789              * @param {Number} columnIndex The column index
4790              * @param {Number} newText The new header text
4791              */
4792             "headerchange": true,
4793         /**
4794              * @event hiddenchange
4795              * Fires when a column is hidden or "unhidden".
4796              * @param {ColumnModel} this
4797              * @param {Number} columnIndex The column index
4798              * @param {Boolean} hidden true if hidden, false otherwise
4799              */
4800             "hiddenchange": true,
4801             /**
4802          * @event columnmoved
4803          * Fires when a column is moved.
4804          * @param {ColumnModel} this
4805          * @param {Number} oldIndex
4806          * @param {Number} newIndex
4807          */
4808         "columnmoved" : true,
4809         /**
4810          * @event columlockchange
4811          * Fires when a column's locked state is changed
4812          * @param {ColumnModel} this
4813          * @param {Number} colIndex
4814          * @param {Boolean} locked true if locked
4815          */
4816         "columnlockchange" : true
4817     });
4818     Roo.grid.ColumnModel.superclass.constructor.call(this);
4819 };
4820 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4821     /**
4822      * @cfg {String} header The header text to display in the Grid view.
4823      */
4824     /**
4825      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4826      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4827      * specified, the column's index is used as an index into the Record's data Array.
4828      */
4829     /**
4830      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4831      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4832      */
4833     /**
4834      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4835      * Defaults to the value of the {@link #defaultSortable} property.
4836      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4837      */
4838     /**
4839      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4840      */
4841     /**
4842      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4843      */
4844     /**
4845      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4846      */
4847     /**
4848      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4849      */
4850     /**
4851      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4852      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4853      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4854      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4855      */
4856        /**
4857      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4858      */
4859     /**
4860      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4861      */
4862     /**
4863      * @cfg {String} cursor (Optional)
4864      */
4865     /**
4866      * @cfg {String} tooltip (Optional)
4867      */
4868     /**
4869      * Returns the id of the column at the specified index.
4870      * @param {Number} index The column index
4871      * @return {String} the id
4872      */
4873     getColumnId : function(index){
4874         return this.config[index].id;
4875     },
4876
4877     /**
4878      * Returns the column for a specified id.
4879      * @param {String} id The column id
4880      * @return {Object} the column
4881      */
4882     getColumnById : function(id){
4883         return this.lookup[id];
4884     },
4885
4886     
4887     /**
4888      * Returns the column for a specified dataIndex.
4889      * @param {String} dataIndex The column dataIndex
4890      * @return {Object|Boolean} the column or false if not found
4891      */
4892     getColumnByDataIndex: function(dataIndex){
4893         var index = this.findColumnIndex(dataIndex);
4894         return index > -1 ? this.config[index] : false;
4895     },
4896     
4897     /**
4898      * Returns the index for a specified column id.
4899      * @param {String} id The column id
4900      * @return {Number} the index, or -1 if not found
4901      */
4902     getIndexById : function(id){
4903         for(var i = 0, len = this.config.length; i < len; i++){
4904             if(this.config[i].id == id){
4905                 return i;
4906             }
4907         }
4908         return -1;
4909     },
4910     
4911     /**
4912      * Returns the index for a specified column dataIndex.
4913      * @param {String} dataIndex The column dataIndex
4914      * @return {Number} the index, or -1 if not found
4915      */
4916     
4917     findColumnIndex : function(dataIndex){
4918         for(var i = 0, len = this.config.length; i < len; i++){
4919             if(this.config[i].dataIndex == dataIndex){
4920                 return i;
4921             }
4922         }
4923         return -1;
4924     },
4925     
4926     
4927     moveColumn : function(oldIndex, newIndex){
4928         var c = this.config[oldIndex];
4929         this.config.splice(oldIndex, 1);
4930         this.config.splice(newIndex, 0, c);
4931         this.dataMap = null;
4932         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4933     },
4934
4935     isLocked : function(colIndex){
4936         return this.config[colIndex].locked === true;
4937     },
4938
4939     setLocked : function(colIndex, value, suppressEvent){
4940         if(this.isLocked(colIndex) == value){
4941             return;
4942         }
4943         this.config[colIndex].locked = value;
4944         if(!suppressEvent){
4945             this.fireEvent("columnlockchange", this, colIndex, value);
4946         }
4947     },
4948
4949     getTotalLockedWidth : function(){
4950         var totalWidth = 0;
4951         for(var i = 0; i < this.config.length; i++){
4952             if(this.isLocked(i) && !this.isHidden(i)){
4953                 this.totalWidth += this.getColumnWidth(i);
4954             }
4955         }
4956         return totalWidth;
4957     },
4958
4959     getLockedCount : function(){
4960         for(var i = 0, len = this.config.length; i < len; i++){
4961             if(!this.isLocked(i)){
4962                 return i;
4963             }
4964         }
4965     },
4966
4967     /**
4968      * Returns the number of columns.
4969      * @return {Number}
4970      */
4971     getColumnCount : function(visibleOnly){
4972         if(visibleOnly === true){
4973             var c = 0;
4974             for(var i = 0, len = this.config.length; i < len; i++){
4975                 if(!this.isHidden(i)){
4976                     c++;
4977                 }
4978             }
4979             return c;
4980         }
4981         return this.config.length;
4982     },
4983
4984     /**
4985      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4986      * @param {Function} fn
4987      * @param {Object} scope (optional)
4988      * @return {Array} result
4989      */
4990     getColumnsBy : function(fn, scope){
4991         var r = [];
4992         for(var i = 0, len = this.config.length; i < len; i++){
4993             var c = this.config[i];
4994             if(fn.call(scope||this, c, i) === true){
4995                 r[r.length] = c;
4996             }
4997         }
4998         return r;
4999     },
5000
5001     /**
5002      * Returns true if the specified column is sortable.
5003      * @param {Number} col The column index
5004      * @return {Boolean}
5005      */
5006     isSortable : function(col){
5007         if(typeof this.config[col].sortable == "undefined"){
5008             return this.defaultSortable;
5009         }
5010         return this.config[col].sortable;
5011     },
5012
5013     /**
5014      * Returns the rendering (formatting) function defined for the column.
5015      * @param {Number} col The column index.
5016      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5017      */
5018     getRenderer : function(col){
5019         if(!this.config[col].renderer){
5020             return Roo.grid.ColumnModel.defaultRenderer;
5021         }
5022         return this.config[col].renderer;
5023     },
5024
5025     /**
5026      * Sets the rendering (formatting) function for a column.
5027      * @param {Number} col The column index
5028      * @param {Function} fn The function to use to process the cell's raw data
5029      * to return HTML markup for the grid view. The render function is called with
5030      * the following parameters:<ul>
5031      * <li>Data value.</li>
5032      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5033      * <li>css A CSS style string to apply to the table cell.</li>
5034      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5035      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5036      * <li>Row index</li>
5037      * <li>Column index</li>
5038      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5039      */
5040     setRenderer : function(col, fn){
5041         this.config[col].renderer = fn;
5042     },
5043
5044     /**
5045      * Returns the width for the specified column.
5046      * @param {Number} col The column index
5047      * @return {Number}
5048      */
5049     getColumnWidth : function(col){
5050         return this.config[col].width * 1 || this.defaultWidth;
5051     },
5052
5053     /**
5054      * Sets the width for a column.
5055      * @param {Number} col The column index
5056      * @param {Number} width The new width
5057      */
5058     setColumnWidth : function(col, width, suppressEvent){
5059         this.config[col].width = width;
5060         this.totalWidth = null;
5061         if(!suppressEvent){
5062              this.fireEvent("widthchange", this, col, width);
5063         }
5064     },
5065
5066     /**
5067      * Returns the total width of all columns.
5068      * @param {Boolean} includeHidden True to include hidden column widths
5069      * @return {Number}
5070      */
5071     getTotalWidth : function(includeHidden){
5072         if(!this.totalWidth){
5073             this.totalWidth = 0;
5074             for(var i = 0, len = this.config.length; i < len; i++){
5075                 if(includeHidden || !this.isHidden(i)){
5076                     this.totalWidth += this.getColumnWidth(i);
5077                 }
5078             }
5079         }
5080         return this.totalWidth;
5081     },
5082
5083     /**
5084      * Returns the header for the specified column.
5085      * @param {Number} col The column index
5086      * @return {String}
5087      */
5088     getColumnHeader : function(col){
5089         return this.config[col].header;
5090     },
5091
5092     /**
5093      * Sets the header for a column.
5094      * @param {Number} col The column index
5095      * @param {String} header The new header
5096      */
5097     setColumnHeader : function(col, header){
5098         this.config[col].header = header;
5099         this.fireEvent("headerchange", this, col, header);
5100     },
5101
5102     /**
5103      * Returns the tooltip for the specified column.
5104      * @param {Number} col The column index
5105      * @return {String}
5106      */
5107     getColumnTooltip : function(col){
5108             return this.config[col].tooltip;
5109     },
5110     /**
5111      * Sets the tooltip for a column.
5112      * @param {Number} col The column index
5113      * @param {String} tooltip The new tooltip
5114      */
5115     setColumnTooltip : function(col, tooltip){
5116             this.config[col].tooltip = tooltip;
5117     },
5118
5119     /**
5120      * Returns the dataIndex for the specified column.
5121      * @param {Number} col The column index
5122      * @return {Number}
5123      */
5124     getDataIndex : function(col){
5125         return this.config[col].dataIndex;
5126     },
5127
5128     /**
5129      * Sets the dataIndex for a column.
5130      * @param {Number} col The column index
5131      * @param {Number} dataIndex The new dataIndex
5132      */
5133     setDataIndex : function(col, dataIndex){
5134         this.config[col].dataIndex = dataIndex;
5135     },
5136
5137     
5138     
5139     /**
5140      * Returns true if the cell is editable.
5141      * @param {Number} colIndex The column index
5142      * @param {Number} rowIndex The row index
5143      * @return {Boolean}
5144      */
5145     isCellEditable : function(colIndex, rowIndex){
5146         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5147     },
5148
5149     /**
5150      * Returns the editor defined for the cell/column.
5151      * return false or null to disable editing.
5152      * @param {Number} colIndex The column index
5153      * @param {Number} rowIndex The row index
5154      * @return {Object}
5155      */
5156     getCellEditor : function(colIndex, rowIndex){
5157         return this.config[colIndex].editor;
5158     },
5159
5160     /**
5161      * Sets if a column is editable.
5162      * @param {Number} col The column index
5163      * @param {Boolean} editable True if the column is editable
5164      */
5165     setEditable : function(col, editable){
5166         this.config[col].editable = editable;
5167     },
5168
5169
5170     /**
5171      * Returns true if the column is hidden.
5172      * @param {Number} colIndex The column index
5173      * @return {Boolean}
5174      */
5175     isHidden : function(colIndex){
5176         return this.config[colIndex].hidden;
5177     },
5178
5179
5180     /**
5181      * Returns true if the column width cannot be changed
5182      */
5183     isFixed : function(colIndex){
5184         return this.config[colIndex].fixed;
5185     },
5186
5187     /**
5188      * Returns true if the column can be resized
5189      * @return {Boolean}
5190      */
5191     isResizable : function(colIndex){
5192         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5193     },
5194     /**
5195      * Sets if a column is hidden.
5196      * @param {Number} colIndex The column index
5197      * @param {Boolean} hidden True if the column is hidden
5198      */
5199     setHidden : function(colIndex, hidden){
5200         this.config[colIndex].hidden = hidden;
5201         this.totalWidth = null;
5202         this.fireEvent("hiddenchange", this, colIndex, hidden);
5203     },
5204
5205     /**
5206      * Sets the editor for a column.
5207      * @param {Number} col The column index
5208      * @param {Object} editor The editor object
5209      */
5210     setEditor : function(col, editor){
5211         this.config[col].editor = editor;
5212     }
5213 });
5214
5215 Roo.grid.ColumnModel.defaultRenderer = function(value){
5216         if(typeof value == "string" && value.length < 1){
5217             return "&#160;";
5218         }
5219         return value;
5220 };
5221
5222 // Alias for backwards compatibility
5223 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5224 /*
5225  * Based on:
5226  * Ext JS Library 1.1.1
5227  * Copyright(c) 2006-2007, Ext JS, LLC.
5228  *
5229  * Originally Released Under LGPL - original licence link has changed is not relivant.
5230  *
5231  * Fork - LGPL
5232  * <script type="text/javascript">
5233  */
5234  
5235 /**
5236  * @class Roo.LoadMask
5237  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5238  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5239  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5240  * element's UpdateManager load indicator and will be destroyed after the initial load.
5241  * @constructor
5242  * Create a new LoadMask
5243  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5244  * @param {Object} config The config object
5245  */
5246 Roo.LoadMask = function(el, config){
5247     this.el = Roo.get(el);
5248     Roo.apply(this, config);
5249     if(this.store){
5250         this.store.on('beforeload', this.onBeforeLoad, this);
5251         this.store.on('load', this.onLoad, this);
5252         this.store.on('loadexception', this.onLoadException, this);
5253         this.removeMask = false;
5254     }else{
5255         var um = this.el.getUpdateManager();
5256         um.showLoadIndicator = false; // disable the default indicator
5257         um.on('beforeupdate', this.onBeforeLoad, this);
5258         um.on('update', this.onLoad, this);
5259         um.on('failure', this.onLoad, this);
5260         this.removeMask = true;
5261     }
5262 };
5263
5264 Roo.LoadMask.prototype = {
5265     /**
5266      * @cfg {Boolean} removeMask
5267      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5268      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5269      */
5270     /**
5271      * @cfg {String} msg
5272      * The text to display in a centered loading message box (defaults to 'Loading...')
5273      */
5274     msg : 'Loading...',
5275     /**
5276      * @cfg {String} msgCls
5277      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5278      */
5279     msgCls : 'x-mask-loading',
5280
5281     /**
5282      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5283      * @type Boolean
5284      */
5285     disabled: false,
5286
5287     /**
5288      * Disables the mask to prevent it from being displayed
5289      */
5290     disable : function(){
5291        this.disabled = true;
5292     },
5293
5294     /**
5295      * Enables the mask so that it can be displayed
5296      */
5297     enable : function(){
5298         this.disabled = false;
5299     },
5300     
5301     onLoadException : function()
5302     {
5303         Roo.log(arguments);
5304         
5305         if (typeof(arguments[3]) != 'undefined') {
5306             Roo.MessageBox.alert("Error loading",arguments[3]);
5307         } 
5308         /*
5309         try {
5310             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5311                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5312             }   
5313         } catch(e) {
5314             
5315         }
5316         */
5317     
5318         
5319         
5320         this.el.unmask(this.removeMask);
5321     },
5322     // private
5323     onLoad : function()
5324     {
5325         this.el.unmask(this.removeMask);
5326     },
5327
5328     // private
5329     onBeforeLoad : function(){
5330         if(!this.disabled){
5331             this.el.mask(this.msg, this.msgCls);
5332         }
5333     },
5334
5335     // private
5336     destroy : function(){
5337         if(this.store){
5338             this.store.un('beforeload', this.onBeforeLoad, this);
5339             this.store.un('load', this.onLoad, this);
5340             this.store.un('loadexception', this.onLoadException, this);
5341         }else{
5342             var um = this.el.getUpdateManager();
5343             um.un('beforeupdate', this.onBeforeLoad, this);
5344             um.un('update', this.onLoad, this);
5345             um.un('failure', this.onLoad, this);
5346         }
5347     }
5348 };/*
5349  * - LGPL
5350  *
5351  * table
5352  * 
5353  */
5354
5355 /**
5356  * @class Roo.bootstrap.Table
5357  * @extends Roo.bootstrap.Component
5358  * Bootstrap Table class
5359  * @cfg {String} cls table class
5360  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5361  * @cfg {String} bgcolor Specifies the background color for a table
5362  * @cfg {Number} border Specifies whether the table cells should have borders or not
5363  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5364  * @cfg {Number} cellspacing Specifies the space between cells
5365  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5366  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5367  * @cfg {String} sortable Specifies that the table should be sortable
5368  * @cfg {String} summary Specifies a summary of the content of a table
5369  * @cfg {Number} width Specifies the width of a table
5370  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5371  * 
5372  * @cfg {boolean} striped Should the rows be alternative striped
5373  * @cfg {boolean} bordered Add borders to the table
5374  * @cfg {boolean} hover Add hover highlighting
5375  * @cfg {boolean} condensed Format condensed
5376  * @cfg {boolean} responsive Format condensed
5377  * @cfg {Boolean} loadMask (true|false) default false
5378  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5379  * @cfg {Boolean} thead (true|false) generate thead, default true
5380  * @cfg {Boolean} RowSelection (true|false) default false
5381  * @cfg {Boolean} CellSelection (true|false) default false
5382  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5383  
5384  * 
5385  * @constructor
5386  * Create a new Table
5387  * @param {Object} config The config object
5388  */
5389
5390 Roo.bootstrap.Table = function(config){
5391     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5392     
5393     if (this.sm) {
5394         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5395         this.sm = this.selModel;
5396         this.sm.xmodule = this.xmodule || false;
5397     }
5398     if (this.cm && typeof(this.cm.config) == 'undefined') {
5399         this.colModel = new Roo.grid.ColumnModel(this.cm);
5400         this.cm = this.colModel;
5401         this.cm.xmodule = this.xmodule || false;
5402     }
5403     if (this.store) {
5404         this.store= Roo.factory(this.store, Roo.data);
5405         this.ds = this.store;
5406         this.ds.xmodule = this.xmodule || false;
5407          
5408     }
5409     if (this.footer && this.store) {
5410         this.footer.dataSource = this.ds;
5411         this.footer = Roo.factory(this.footer);
5412     }
5413     
5414     /** @private */
5415     this.addEvents({
5416         /**
5417          * @event cellclick
5418          * Fires when a cell is clicked
5419          * @param {Roo.bootstrap.Table} this
5420          * @param {Roo.Element} el
5421          * @param {Number} rowIndex
5422          * @param {Number} columnIndex
5423          * @param {Roo.EventObject} e
5424          */
5425         "cellclick" : true,
5426         /**
5427          * @event celldblclick
5428          * Fires when a cell is double 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         "celldblclick" : true,
5436         /**
5437          * @event rowclick
5438          * Fires when a row is clicked
5439          * @param {Roo.bootstrap.Table} this
5440          * @param {Roo.Element} el
5441          * @param {Number} rowIndex
5442          * @param {Roo.EventObject} e
5443          */
5444         "rowclick" : true,
5445         /**
5446          * @event rowdblclick
5447          * Fires when a row is double clicked
5448          * @param {Roo.bootstrap.Table} this
5449          * @param {Roo.Element} el
5450          * @param {Number} rowIndex
5451          * @param {Roo.EventObject} e
5452          */
5453         "rowdblclick" : true,
5454         /**
5455          * @event mouseover
5456          * Fires when a mouseover occur
5457          * @param {Roo.bootstrap.Table} this
5458          * @param {Roo.Element} el
5459          * @param {Number} rowIndex
5460          * @param {Number} columnIndex
5461          * @param {Roo.EventObject} e
5462          */
5463         "mouseover" : true,
5464         /**
5465          * @event mouseout
5466          * Fires when a mouseout 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         "mouseout" : true,
5474         /**
5475          * @event rowclass
5476          * Fires when a row is rendered, so you can change add a style to it.
5477          * @param {Roo.bootstrap.Table} this
5478          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5479          */
5480         'rowclass' : true,
5481           /**
5482          * @event rowsrendered
5483          * Fires when all the  rows have been rendered
5484          * @param {Roo.bootstrap.Table} this
5485          */
5486         'rowsrendered' : true
5487         
5488     });
5489 };
5490
5491 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5492     
5493     cls: false,
5494     align: false,
5495     bgcolor: false,
5496     border: false,
5497     cellpadding: false,
5498     cellspacing: false,
5499     frame: false,
5500     rules: false,
5501     sortable: false,
5502     summary: false,
5503     width: false,
5504     striped : false,
5505     bordered: false,
5506     hover:  false,
5507     condensed : false,
5508     responsive : false,
5509     sm : false,
5510     cm : false,
5511     store : false,
5512     loadMask : false,
5513     tfoot : true,
5514     thead : true,
5515     RowSelection : false,
5516     CellSelection : false,
5517     layout : false,
5518     
5519     // Roo.Element - the tbody
5520     mainBody: false, 
5521     
5522     getAutoCreate : function(){
5523         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5524         
5525         cfg = {
5526             tag: 'table',
5527             cls : 'table',
5528             cn : []
5529         }
5530             
5531         if (this.striped) {
5532             cfg.cls += ' table-striped';
5533         }
5534         
5535         if (this.hover) {
5536             cfg.cls += ' table-hover';
5537         }
5538         if (this.bordered) {
5539             cfg.cls += ' table-bordered';
5540         }
5541         if (this.condensed) {
5542             cfg.cls += ' table-condensed';
5543         }
5544         if (this.responsive) {
5545             cfg.cls += ' table-responsive';
5546         }
5547         
5548         if (this.cls) {
5549             cfg.cls+=  ' ' +this.cls;
5550         }
5551         
5552         // this lot should be simplifed...
5553         
5554         if (this.align) {
5555             cfg.align=this.align;
5556         }
5557         if (this.bgcolor) {
5558             cfg.bgcolor=this.bgcolor;
5559         }
5560         if (this.border) {
5561             cfg.border=this.border;
5562         }
5563         if (this.cellpadding) {
5564             cfg.cellpadding=this.cellpadding;
5565         }
5566         if (this.cellspacing) {
5567             cfg.cellspacing=this.cellspacing;
5568         }
5569         if (this.frame) {
5570             cfg.frame=this.frame;
5571         }
5572         if (this.rules) {
5573             cfg.rules=this.rules;
5574         }
5575         if (this.sortable) {
5576             cfg.sortable=this.sortable;
5577         }
5578         if (this.summary) {
5579             cfg.summary=this.summary;
5580         }
5581         if (this.width) {
5582             cfg.width=this.width;
5583         }
5584         if (this.layout) {
5585             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5586         }
5587         
5588         if(this.store || this.cm){
5589             if(this.thead){
5590                 cfg.cn.push(this.renderHeader());
5591             }
5592             
5593             cfg.cn.push(this.renderBody());
5594             
5595             if(this.tfoot){
5596                 cfg.cn.push(this.renderFooter());
5597             }
5598             
5599             cfg.cls+=  ' TableGrid';
5600         }
5601         
5602         return { cn : [ cfg ] };
5603     },
5604     
5605     initEvents : function()
5606     {   
5607         if(!this.store || !this.cm){
5608             return;
5609         }
5610         
5611         //Roo.log('initEvents with ds!!!!');
5612         
5613         this.mainBody = this.el.select('tbody', true).first();
5614         
5615         
5616         var _this = this;
5617         
5618         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5619             e.on('click', _this.sort, _this);
5620         });
5621         
5622         this.el.on("click", this.onClick, this);
5623         this.el.on("dblclick", this.onDblClick, this);
5624         
5625         // why is this done????? = it breaks dialogs??
5626         //this.parent().el.setStyle('position', 'relative');
5627         
5628         
5629         if (this.footer) {
5630             this.footer.parentId = this.id;
5631             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5632         }
5633         
5634         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5635         
5636         this.store.on('load', this.onLoad, this);
5637         this.store.on('beforeload', this.onBeforeLoad, this);
5638         this.store.on('update', this.onUpdate, this);
5639         this.store.on('add', this.onAdd, this);
5640         
5641     },
5642     
5643     onMouseover : function(e, el)
5644     {
5645         var cell = Roo.get(el);
5646         
5647         if(!cell){
5648             return;
5649         }
5650         
5651         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5652             cell = cell.findParent('td', false, true);
5653         }
5654         
5655         var row = cell.findParent('tr', false, true);
5656         var cellIndex = cell.dom.cellIndex;
5657         var rowIndex = row.dom.rowIndex - 1; // start from 0
5658         
5659         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5660         
5661     },
5662     
5663     onMouseout : function(e, el)
5664     {
5665         var cell = Roo.get(el);
5666         
5667         if(!cell){
5668             return;
5669         }
5670         
5671         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5672             cell = cell.findParent('td', false, true);
5673         }
5674         
5675         var row = cell.findParent('tr', false, true);
5676         var cellIndex = cell.dom.cellIndex;
5677         var rowIndex = row.dom.rowIndex - 1; // start from 0
5678         
5679         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5680         
5681     },
5682     
5683     onClick : function(e, el)
5684     {
5685         var cell = Roo.get(el);
5686         
5687         if(!cell || (!this.CellSelection && !this.RowSelection)){
5688             return;
5689         }
5690         
5691         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5692             cell = cell.findParent('td', false, true);
5693         }
5694         
5695         if(!cell || typeof(cell) == 'undefined'){
5696             return;
5697         }
5698         
5699         var row = cell.findParent('tr', false, true);
5700         
5701         if(!row || typeof(row) == 'undefined'){
5702             return;
5703         }
5704         
5705         var cellIndex = cell.dom.cellIndex;
5706         var rowIndex = this.getRowIndex(row);
5707         
5708         if(this.CellSelection){
5709             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5710         }
5711         
5712         if(this.RowSelection){
5713             this.fireEvent('rowclick', this, row, rowIndex, e);
5714         }
5715         
5716         
5717     },
5718     
5719     onDblClick : function(e,el)
5720     {
5721         var cell = Roo.get(el);
5722         
5723         if(!cell || (!this.CellSelection && !this.RowSelection)){
5724             return;
5725         }
5726         
5727         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5728             cell = cell.findParent('td', false, true);
5729         }
5730         
5731         if(!cell || typeof(cell) == 'undefined'){
5732             return;
5733         }
5734         
5735         var row = cell.findParent('tr', false, true);
5736         
5737         if(!row || typeof(row) == 'undefined'){
5738             return;
5739         }
5740         
5741         var cellIndex = cell.dom.cellIndex;
5742         var rowIndex = this.getRowIndex(row);
5743         
5744         if(this.CellSelection){
5745             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5746         }
5747         
5748         if(this.RowSelection){
5749             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5750         }
5751     },
5752     
5753     sort : function(e,el)
5754     {
5755         var col = Roo.get(el);
5756         
5757         if(!col.hasClass('sortable')){
5758             return;
5759         }
5760         
5761         var sort = col.attr('sort');
5762         var dir = 'ASC';
5763         
5764         if(col.hasClass('glyphicon-arrow-up')){
5765             dir = 'DESC';
5766         }
5767         
5768         this.store.sortInfo = {field : sort, direction : dir};
5769         
5770         if (this.footer) {
5771             Roo.log("calling footer first");
5772             this.footer.onClick('first');
5773         } else {
5774         
5775             this.store.load({ params : { start : 0 } });
5776         }
5777     },
5778     
5779     renderHeader : function()
5780     {
5781         var header = {
5782             tag: 'thead',
5783             cn : []
5784         };
5785         
5786         var cm = this.cm;
5787         
5788         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5789             
5790             var config = cm.config[i];
5791                     
5792             var c = {
5793                 tag: 'th',
5794                 style : '',
5795                 html: cm.getColumnHeader(i)
5796             };
5797             
5798             if(typeof(config.tooltip) != 'undefined'){
5799                 c.tooltip = config.tooltip;
5800             }
5801             
5802             if(typeof(config.colspan) != 'undefined'){
5803                 c.colspan = config.colspan;
5804             }
5805             
5806             if(typeof(config.hidden) != 'undefined' && config.hidden){
5807                 c.style += ' display:none;';
5808             }
5809             
5810             if(typeof(config.dataIndex) != 'undefined'){
5811                 c.sort = config.dataIndex;
5812             }
5813             
5814             if(typeof(config.sortable) != 'undefined' && config.sortable){
5815                 c.cls = 'sortable';
5816             }
5817             
5818             if(typeof(config.align) != 'undefined' && config.align.length){
5819                 c.style += ' text-align:' + config.align + ';';
5820             }
5821             
5822             if(typeof(config.width) != 'undefined'){
5823                 c.style += ' width:' + config.width + 'px;';
5824             }
5825             
5826             if(typeof(config.cls) != 'undefined'){
5827                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5828             }
5829             
5830             header.cn.push(c)
5831         }
5832         
5833         return header;
5834     },
5835     
5836     renderBody : function()
5837     {
5838         var body = {
5839             tag: 'tbody',
5840             cn : [
5841                 {
5842                     tag: 'tr',
5843                     cn : [
5844                         {
5845                             tag : 'td',
5846                             colspan :  this.cm.getColumnCount()
5847                         }
5848                     ]
5849                 }
5850             ]
5851         };
5852         
5853         return body;
5854     },
5855     
5856     renderFooter : function()
5857     {
5858         var footer = {
5859             tag: 'tfoot',
5860             cn : [
5861                 {
5862                     tag: 'tr',
5863                     cn : [
5864                         {
5865                             tag : 'td',
5866                             colspan :  this.cm.getColumnCount()
5867                         }
5868                     ]
5869                 }
5870             ]
5871         };
5872         
5873         return footer;
5874     },
5875     
5876     
5877     
5878     onLoad : function()
5879     {
5880         Roo.log('ds onload');
5881         this.clear();
5882         
5883         var _this = this;
5884         var cm = this.cm;
5885         var ds = this.store;
5886         
5887         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5888             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5889             
5890             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5891                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5892             }
5893             
5894             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5895                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5896             }
5897         });
5898         
5899         var tbody =  this.mainBody;
5900               
5901         if(ds.getCount() > 0){
5902             ds.data.each(function(d,rowIndex){
5903                 var row =  this.renderRow(cm, ds, rowIndex);
5904                 
5905                 tbody.createChild(row);
5906                 
5907                 var _this = this;
5908                 
5909                 if(row.cellObjects.length){
5910                     Roo.each(row.cellObjects, function(r){
5911                         _this.renderCellObject(r);
5912                     })
5913                 }
5914                 
5915             }, this);
5916         }
5917         
5918         Roo.each(this.el.select('tbody td', true).elements, function(e){
5919             e.on('mouseover', _this.onMouseover, _this);
5920         });
5921         
5922         Roo.each(this.el.select('tbody td', true).elements, function(e){
5923             e.on('mouseout', _this.onMouseout, _this);
5924         });
5925         this.fireEvent('rowsrendered', this);
5926         //if(this.loadMask){
5927         //    this.maskEl.hide();
5928         //}
5929     },
5930     
5931     
5932     onUpdate : function(ds,record)
5933     {
5934         this.refreshRow(record);
5935     },
5936     
5937     onRemove : function(ds, record, index, isUpdate){
5938         if(isUpdate !== true){
5939             this.fireEvent("beforerowremoved", this, index, record);
5940         }
5941         var bt = this.mainBody.dom;
5942         
5943         var rows = this.el.select('tbody > tr', true).elements;
5944         
5945         if(typeof(rows[index]) != 'undefined'){
5946             bt.removeChild(rows[index].dom);
5947         }
5948         
5949 //        if(bt.rows[index]){
5950 //            bt.removeChild(bt.rows[index]);
5951 //        }
5952         
5953         if(isUpdate !== true){
5954             //this.stripeRows(index);
5955             //this.syncRowHeights(index, index);
5956             //this.layout();
5957             this.fireEvent("rowremoved", this, index, record);
5958         }
5959     },
5960     
5961     onAdd : function(ds, records, rowIndex)
5962     {
5963         //Roo.log('on Add called');
5964         // - note this does not handle multiple adding very well..
5965         var bt = this.mainBody.dom;
5966         for (var i =0 ; i < records.length;i++) {
5967             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5968             //Roo.log(records[i]);
5969             //Roo.log(this.store.getAt(rowIndex+i));
5970             this.insertRow(this.store, rowIndex + i, false);
5971             return;
5972         }
5973         
5974     },
5975     
5976     
5977     refreshRow : function(record){
5978         var ds = this.store, index;
5979         if(typeof record == 'number'){
5980             index = record;
5981             record = ds.getAt(index);
5982         }else{
5983             index = ds.indexOf(record);
5984         }
5985         this.insertRow(ds, index, true);
5986         this.onRemove(ds, record, index+1, true);
5987         //this.syncRowHeights(index, index);
5988         //this.layout();
5989         this.fireEvent("rowupdated", this, index, record);
5990     },
5991     
5992     insertRow : function(dm, rowIndex, isUpdate){
5993         
5994         if(!isUpdate){
5995             this.fireEvent("beforerowsinserted", this, rowIndex);
5996         }
5997             //var s = this.getScrollState();
5998         var row = this.renderRow(this.cm, this.store, rowIndex);
5999         // insert before rowIndex..
6000         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6001         
6002         var _this = this;
6003                 
6004         if(row.cellObjects.length){
6005             Roo.each(row.cellObjects, function(r){
6006                 _this.renderCellObject(r);
6007             })
6008         }
6009             
6010         if(!isUpdate){
6011             this.fireEvent("rowsinserted", this, rowIndex);
6012             //this.syncRowHeights(firstRow, lastRow);
6013             //this.stripeRows(firstRow);
6014             //this.layout();
6015         }
6016         
6017     },
6018     
6019     
6020     getRowDom : function(rowIndex)
6021     {
6022         var rows = this.el.select('tbody > tr', true).elements;
6023         
6024         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6025         
6026     },
6027     // returns the object tree for a tr..
6028   
6029     
6030     renderRow : function(cm, ds, rowIndex) 
6031     {
6032         
6033         var d = ds.getAt(rowIndex);
6034         
6035         var row = {
6036             tag : 'tr',
6037             cn : []
6038         };
6039             
6040         var cellObjects = [];
6041         
6042         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6043             var config = cm.config[i];
6044             
6045             var renderer = cm.getRenderer(i);
6046             var value = '';
6047             var id = false;
6048             
6049             if(typeof(renderer) !== 'undefined'){
6050                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6051             }
6052             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6053             // and are rendered into the cells after the row is rendered - using the id for the element.
6054             
6055             if(typeof(value) === 'object'){
6056                 id = Roo.id();
6057                 cellObjects.push({
6058                     container : id,
6059                     cfg : value 
6060                 })
6061             }
6062             
6063             var rowcfg = {
6064                 record: d,
6065                 rowIndex : rowIndex,
6066                 colIndex : i,
6067                 rowClass : ''
6068             }
6069
6070             this.fireEvent('rowclass', this, rowcfg);
6071             
6072             var td = {
6073                 tag: 'td',
6074                 cls : rowcfg.rowClass,
6075                 style: '',
6076                 html: (typeof(value) === 'object') ? '' : value
6077             };
6078             
6079             if (id) {
6080                 td.id = id;
6081             }
6082             
6083             if(typeof(config.colspan) != 'undefined'){
6084                 td.colspan = config.colspan;
6085             }
6086             
6087             if(typeof(config.hidden) != 'undefined' && config.hidden){
6088                 td.style += ' display:none;';
6089             }
6090             
6091             if(typeof(config.align) != 'undefined' && config.align.length){
6092                 td.style += ' text-align:' + config.align + ';';
6093             }
6094             
6095             if(typeof(config.width) != 'undefined'){
6096                 td.style += ' width:' +  config.width + 'px;';
6097             }
6098             
6099             if(typeof(config.cursor) != 'undefined'){
6100                 td.style += ' cursor:' +  config.cursor + ';';
6101             }
6102             
6103             if(typeof(config.cls) != 'undefined'){
6104                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6105             }
6106              
6107             row.cn.push(td);
6108            
6109         }
6110         
6111         row.cellObjects = cellObjects;
6112         
6113         return row;
6114           
6115     },
6116     
6117     
6118     
6119     onBeforeLoad : function()
6120     {
6121         //Roo.log('ds onBeforeLoad');
6122         
6123         //this.clear();
6124         
6125         //if(this.loadMask){
6126         //    this.maskEl.show();
6127         //}
6128     },
6129      /**
6130      * Remove all rows
6131      */
6132     clear : function()
6133     {
6134         this.el.select('tbody', true).first().dom.innerHTML = '';
6135     },
6136     /**
6137      * Show or hide a row.
6138      * @param {Number} rowIndex to show or hide
6139      * @param {Boolean} state hide
6140      */
6141     setRowVisibility : function(rowIndex, state)
6142     {
6143         var bt = this.mainBody.dom;
6144         
6145         var rows = this.el.select('tbody > tr', true).elements;
6146         
6147         if(typeof(rows[rowIndex]) == 'undefined'){
6148             return;
6149         }
6150         rows[rowIndex].dom.style.display = state ? '' : 'none';
6151     },
6152     
6153     
6154     getSelectionModel : function(){
6155         if(!this.selModel){
6156             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6157         }
6158         return this.selModel;
6159     },
6160     /*
6161      * Render the Roo.bootstrap object from renderder
6162      */
6163     renderCellObject : function(r)
6164     {
6165         var _this = this;
6166         
6167         var t = r.cfg.render(r.container);
6168         
6169         if(r.cfg.cn){
6170             Roo.each(r.cfg.cn, function(c){
6171                 var child = {
6172                     container: t.getChildContainer(),
6173                     cfg: c
6174                 }
6175                 _this.renderCellObject(child);
6176             })
6177         }
6178     },
6179     
6180     getRowIndex : function(row)
6181     {
6182         var rowIndex = -1;
6183         
6184         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6185             if(el != row){
6186                 return;
6187             }
6188             
6189             rowIndex = index;
6190         });
6191         
6192         return rowIndex;
6193     }
6194    
6195 });
6196
6197  
6198
6199  /*
6200  * - LGPL
6201  *
6202  * table cell
6203  * 
6204  */
6205
6206 /**
6207  * @class Roo.bootstrap.TableCell
6208  * @extends Roo.bootstrap.Component
6209  * Bootstrap TableCell class
6210  * @cfg {String} html cell contain text
6211  * @cfg {String} cls cell class
6212  * @cfg {String} tag cell tag (td|th) default td
6213  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6214  * @cfg {String} align Aligns the content in a cell
6215  * @cfg {String} axis Categorizes cells
6216  * @cfg {String} bgcolor Specifies the background color of a cell
6217  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6218  * @cfg {Number} colspan Specifies the number of columns a cell should span
6219  * @cfg {String} headers Specifies one or more header cells a cell is related to
6220  * @cfg {Number} height Sets the height of a cell
6221  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6222  * @cfg {Number} rowspan Sets the number of rows a cell should span
6223  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6224  * @cfg {String} valign Vertical aligns the content in a cell
6225  * @cfg {Number} width Specifies the width of a cell
6226  * 
6227  * @constructor
6228  * Create a new TableCell
6229  * @param {Object} config The config object
6230  */
6231
6232 Roo.bootstrap.TableCell = function(config){
6233     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6234 };
6235
6236 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6237     
6238     html: false,
6239     cls: false,
6240     tag: false,
6241     abbr: false,
6242     align: false,
6243     axis: false,
6244     bgcolor: false,
6245     charoff: false,
6246     colspan: false,
6247     headers: false,
6248     height: false,
6249     nowrap: false,
6250     rowspan: false,
6251     scope: false,
6252     valign: false,
6253     width: false,
6254     
6255     
6256     getAutoCreate : function(){
6257         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6258         
6259         cfg = {
6260             tag: 'td'
6261         }
6262         
6263         if(this.tag){
6264             cfg.tag = this.tag;
6265         }
6266         
6267         if (this.html) {
6268             cfg.html=this.html
6269         }
6270         if (this.cls) {
6271             cfg.cls=this.cls
6272         }
6273         if (this.abbr) {
6274             cfg.abbr=this.abbr
6275         }
6276         if (this.align) {
6277             cfg.align=this.align
6278         }
6279         if (this.axis) {
6280             cfg.axis=this.axis
6281         }
6282         if (this.bgcolor) {
6283             cfg.bgcolor=this.bgcolor
6284         }
6285         if (this.charoff) {
6286             cfg.charoff=this.charoff
6287         }
6288         if (this.colspan) {
6289             cfg.colspan=this.colspan
6290         }
6291         if (this.headers) {
6292             cfg.headers=this.headers
6293         }
6294         if (this.height) {
6295             cfg.height=this.height
6296         }
6297         if (this.nowrap) {
6298             cfg.nowrap=this.nowrap
6299         }
6300         if (this.rowspan) {
6301             cfg.rowspan=this.rowspan
6302         }
6303         if (this.scope) {
6304             cfg.scope=this.scope
6305         }
6306         if (this.valign) {
6307             cfg.valign=this.valign
6308         }
6309         if (this.width) {
6310             cfg.width=this.width
6311         }
6312         
6313         
6314         return cfg;
6315     }
6316    
6317 });
6318
6319  
6320
6321  /*
6322  * - LGPL
6323  *
6324  * table row
6325  * 
6326  */
6327
6328 /**
6329  * @class Roo.bootstrap.TableRow
6330  * @extends Roo.bootstrap.Component
6331  * Bootstrap TableRow class
6332  * @cfg {String} cls row class
6333  * @cfg {String} align Aligns the content in a table row
6334  * @cfg {String} bgcolor Specifies a background color for a table row
6335  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6336  * @cfg {String} valign Vertical aligns the content in a table row
6337  * 
6338  * @constructor
6339  * Create a new TableRow
6340  * @param {Object} config The config object
6341  */
6342
6343 Roo.bootstrap.TableRow = function(config){
6344     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6345 };
6346
6347 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6348     
6349     cls: false,
6350     align: false,
6351     bgcolor: false,
6352     charoff: false,
6353     valign: false,
6354     
6355     getAutoCreate : function(){
6356         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6357         
6358         cfg = {
6359             tag: 'tr'
6360         }
6361             
6362         if(this.cls){
6363             cfg.cls = this.cls;
6364         }
6365         if(this.align){
6366             cfg.align = this.align;
6367         }
6368         if(this.bgcolor){
6369             cfg.bgcolor = this.bgcolor;
6370         }
6371         if(this.charoff){
6372             cfg.charoff = this.charoff;
6373         }
6374         if(this.valign){
6375             cfg.valign = this.valign;
6376         }
6377         
6378         return cfg;
6379     }
6380    
6381 });
6382
6383  
6384
6385  /*
6386  * - LGPL
6387  *
6388  * table body
6389  * 
6390  */
6391
6392 /**
6393  * @class Roo.bootstrap.TableBody
6394  * @extends Roo.bootstrap.Component
6395  * Bootstrap TableBody class
6396  * @cfg {String} cls element class
6397  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6398  * @cfg {String} align Aligns the content inside the element
6399  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6400  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6401  * 
6402  * @constructor
6403  * Create a new TableBody
6404  * @param {Object} config The config object
6405  */
6406
6407 Roo.bootstrap.TableBody = function(config){
6408     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6409 };
6410
6411 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6412     
6413     cls: false,
6414     tag: false,
6415     align: false,
6416     charoff: false,
6417     valign: false,
6418     
6419     getAutoCreate : function(){
6420         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6421         
6422         cfg = {
6423             tag: 'tbody'
6424         }
6425             
6426         if (this.cls) {
6427             cfg.cls=this.cls
6428         }
6429         if(this.tag){
6430             cfg.tag = this.tag;
6431         }
6432         
6433         if(this.align){
6434             cfg.align = this.align;
6435         }
6436         if(this.charoff){
6437             cfg.charoff = this.charoff;
6438         }
6439         if(this.valign){
6440             cfg.valign = this.valign;
6441         }
6442         
6443         return cfg;
6444     }
6445     
6446     
6447 //    initEvents : function()
6448 //    {
6449 //        
6450 //        if(!this.store){
6451 //            return;
6452 //        }
6453 //        
6454 //        this.store = Roo.factory(this.store, Roo.data);
6455 //        this.store.on('load', this.onLoad, this);
6456 //        
6457 //        this.store.load();
6458 //        
6459 //    },
6460 //    
6461 //    onLoad: function () 
6462 //    {   
6463 //        this.fireEvent('load', this);
6464 //    }
6465 //    
6466 //   
6467 });
6468
6469  
6470
6471  /*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482 // as we use this in bootstrap.
6483 Roo.namespace('Roo.form');
6484  /**
6485  * @class Roo.form.Action
6486  * Internal Class used to handle form actions
6487  * @constructor
6488  * @param {Roo.form.BasicForm} el The form element or its id
6489  * @param {Object} config Configuration options
6490  */
6491
6492  
6493  
6494 // define the action interface
6495 Roo.form.Action = function(form, options){
6496     this.form = form;
6497     this.options = options || {};
6498 };
6499 /**
6500  * Client Validation Failed
6501  * @const 
6502  */
6503 Roo.form.Action.CLIENT_INVALID = 'client';
6504 /**
6505  * Server Validation Failed
6506  * @const 
6507  */
6508 Roo.form.Action.SERVER_INVALID = 'server';
6509  /**
6510  * Connect to Server Failed
6511  * @const 
6512  */
6513 Roo.form.Action.CONNECT_FAILURE = 'connect';
6514 /**
6515  * Reading Data from Server Failed
6516  * @const 
6517  */
6518 Roo.form.Action.LOAD_FAILURE = 'load';
6519
6520 Roo.form.Action.prototype = {
6521     type : 'default',
6522     failureType : undefined,
6523     response : undefined,
6524     result : undefined,
6525
6526     // interface method
6527     run : function(options){
6528
6529     },
6530
6531     // interface method
6532     success : function(response){
6533
6534     },
6535
6536     // interface method
6537     handleResponse : function(response){
6538
6539     },
6540
6541     // default connection failure
6542     failure : function(response){
6543         
6544         this.response = response;
6545         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6546         this.form.afterAction(this, false);
6547     },
6548
6549     processResponse : function(response){
6550         this.response = response;
6551         if(!response.responseText){
6552             return true;
6553         }
6554         this.result = this.handleResponse(response);
6555         return this.result;
6556     },
6557
6558     // utility functions used internally
6559     getUrl : function(appendParams){
6560         var url = this.options.url || this.form.url || this.form.el.dom.action;
6561         if(appendParams){
6562             var p = this.getParams();
6563             if(p){
6564                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6565             }
6566         }
6567         return url;
6568     },
6569
6570     getMethod : function(){
6571         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6572     },
6573
6574     getParams : function(){
6575         var bp = this.form.baseParams;
6576         var p = this.options.params;
6577         if(p){
6578             if(typeof p == "object"){
6579                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6580             }else if(typeof p == 'string' && bp){
6581                 p += '&' + Roo.urlEncode(bp);
6582             }
6583         }else if(bp){
6584             p = Roo.urlEncode(bp);
6585         }
6586         return p;
6587     },
6588
6589     createCallback : function(){
6590         return {
6591             success: this.success,
6592             failure: this.failure,
6593             scope: this,
6594             timeout: (this.form.timeout*1000),
6595             upload: this.form.fileUpload ? this.success : undefined
6596         };
6597     }
6598 };
6599
6600 Roo.form.Action.Submit = function(form, options){
6601     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6602 };
6603
6604 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6605     type : 'submit',
6606
6607     haveProgress : false,
6608     uploadComplete : false,
6609     
6610     // uploadProgress indicator.
6611     uploadProgress : function()
6612     {
6613         if (!this.form.progressUrl) {
6614             return;
6615         }
6616         
6617         if (!this.haveProgress) {
6618             Roo.MessageBox.progress("Uploading", "Uploading");
6619         }
6620         if (this.uploadComplete) {
6621            Roo.MessageBox.hide();
6622            return;
6623         }
6624         
6625         this.haveProgress = true;
6626    
6627         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6628         
6629         var c = new Roo.data.Connection();
6630         c.request({
6631             url : this.form.progressUrl,
6632             params: {
6633                 id : uid
6634             },
6635             method: 'GET',
6636             success : function(req){
6637                //console.log(data);
6638                 var rdata = false;
6639                 var edata;
6640                 try  {
6641                    rdata = Roo.decode(req.responseText)
6642                 } catch (e) {
6643                     Roo.log("Invalid data from server..");
6644                     Roo.log(edata);
6645                     return;
6646                 }
6647                 if (!rdata || !rdata.success) {
6648                     Roo.log(rdata);
6649                     Roo.MessageBox.alert(Roo.encode(rdata));
6650                     return;
6651                 }
6652                 var data = rdata.data;
6653                 
6654                 if (this.uploadComplete) {
6655                    Roo.MessageBox.hide();
6656                    return;
6657                 }
6658                    
6659                 if (data){
6660                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6661                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6662                     );
6663                 }
6664                 this.uploadProgress.defer(2000,this);
6665             },
6666        
6667             failure: function(data) {
6668                 Roo.log('progress url failed ');
6669                 Roo.log(data);
6670             },
6671             scope : this
6672         });
6673            
6674     },
6675     
6676     
6677     run : function()
6678     {
6679         // run get Values on the form, so it syncs any secondary forms.
6680         this.form.getValues();
6681         
6682         var o = this.options;
6683         var method = this.getMethod();
6684         var isPost = method == 'POST';
6685         if(o.clientValidation === false || this.form.isValid()){
6686             
6687             if (this.form.progressUrl) {
6688                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6689                     (new Date() * 1) + '' + Math.random());
6690                     
6691             } 
6692             
6693             
6694             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6695                 form:this.form.el.dom,
6696                 url:this.getUrl(!isPost),
6697                 method: method,
6698                 params:isPost ? this.getParams() : null,
6699                 isUpload: this.form.fileUpload
6700             }));
6701             
6702             this.uploadProgress();
6703
6704         }else if (o.clientValidation !== false){ // client validation failed
6705             this.failureType = Roo.form.Action.CLIENT_INVALID;
6706             this.form.afterAction(this, false);
6707         }
6708     },
6709
6710     success : function(response)
6711     {
6712         this.uploadComplete= true;
6713         if (this.haveProgress) {
6714             Roo.MessageBox.hide();
6715         }
6716         
6717         
6718         var result = this.processResponse(response);
6719         if(result === true || result.success){
6720             this.form.afterAction(this, true);
6721             return;
6722         }
6723         if(result.errors){
6724             this.form.markInvalid(result.errors);
6725             this.failureType = Roo.form.Action.SERVER_INVALID;
6726         }
6727         this.form.afterAction(this, false);
6728     },
6729     failure : function(response)
6730     {
6731         this.uploadComplete= true;
6732         if (this.haveProgress) {
6733             Roo.MessageBox.hide();
6734         }
6735         
6736         this.response = response;
6737         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6738         this.form.afterAction(this, false);
6739     },
6740     
6741     handleResponse : function(response){
6742         if(this.form.errorReader){
6743             var rs = this.form.errorReader.read(response);
6744             var errors = [];
6745             if(rs.records){
6746                 for(var i = 0, len = rs.records.length; i < len; i++) {
6747                     var r = rs.records[i];
6748                     errors[i] = r.data;
6749                 }
6750             }
6751             if(errors.length < 1){
6752                 errors = null;
6753             }
6754             return {
6755                 success : rs.success,
6756                 errors : errors
6757             };
6758         }
6759         var ret = false;
6760         try {
6761             ret = Roo.decode(response.responseText);
6762         } catch (e) {
6763             ret = {
6764                 success: false,
6765                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6766                 errors : []
6767             };
6768         }
6769         return ret;
6770         
6771     }
6772 });
6773
6774
6775 Roo.form.Action.Load = function(form, options){
6776     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6777     this.reader = this.form.reader;
6778 };
6779
6780 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6781     type : 'load',
6782
6783     run : function(){
6784         
6785         Roo.Ajax.request(Roo.apply(
6786                 this.createCallback(), {
6787                     method:this.getMethod(),
6788                     url:this.getUrl(false),
6789                     params:this.getParams()
6790         }));
6791     },
6792
6793     success : function(response){
6794         
6795         var result = this.processResponse(response);
6796         if(result === true || !result.success || !result.data){
6797             this.failureType = Roo.form.Action.LOAD_FAILURE;
6798             this.form.afterAction(this, false);
6799             return;
6800         }
6801         this.form.clearInvalid();
6802         this.form.setValues(result.data);
6803         this.form.afterAction(this, true);
6804     },
6805
6806     handleResponse : function(response){
6807         if(this.form.reader){
6808             var rs = this.form.reader.read(response);
6809             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6810             return {
6811                 success : rs.success,
6812                 data : data
6813             };
6814         }
6815         return Roo.decode(response.responseText);
6816     }
6817 });
6818
6819 Roo.form.Action.ACTION_TYPES = {
6820     'load' : Roo.form.Action.Load,
6821     'submit' : Roo.form.Action.Submit
6822 };/*
6823  * - LGPL
6824  *
6825  * form
6826  * 
6827  */
6828
6829 /**
6830  * @class Roo.bootstrap.Form
6831  * @extends Roo.bootstrap.Component
6832  * Bootstrap Form class
6833  * @cfg {String} method  GET | POST (default POST)
6834  * @cfg {String} labelAlign top | left (default top)
6835  * @cfg {String} align left  | right - for navbars
6836  * @cfg {Boolean} loadMask load mask when submit (default true)
6837
6838  * 
6839  * @constructor
6840  * Create a new Form
6841  * @param {Object} config The config object
6842  */
6843
6844
6845 Roo.bootstrap.Form = function(config){
6846     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6847     this.addEvents({
6848         /**
6849          * @event clientvalidation
6850          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6851          * @param {Form} this
6852          * @param {Boolean} valid true if the form has passed client-side validation
6853          */
6854         clientvalidation: true,
6855         /**
6856          * @event beforeaction
6857          * Fires before any action is performed. Return false to cancel the action.
6858          * @param {Form} this
6859          * @param {Action} action The action to be performed
6860          */
6861         beforeaction: true,
6862         /**
6863          * @event actionfailed
6864          * Fires when an action fails.
6865          * @param {Form} this
6866          * @param {Action} action The action that failed
6867          */
6868         actionfailed : true,
6869         /**
6870          * @event actioncomplete
6871          * Fires when an action is completed.
6872          * @param {Form} this
6873          * @param {Action} action The action that completed
6874          */
6875         actioncomplete : true
6876     });
6877     
6878 };
6879
6880 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6881       
6882      /**
6883      * @cfg {String} method
6884      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6885      */
6886     method : 'POST',
6887     /**
6888      * @cfg {String} url
6889      * The URL to use for form actions if one isn't supplied in the action options.
6890      */
6891     /**
6892      * @cfg {Boolean} fileUpload
6893      * Set to true if this form is a file upload.
6894      */
6895      
6896     /**
6897      * @cfg {Object} baseParams
6898      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6899      */
6900       
6901     /**
6902      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6903      */
6904     timeout: 30,
6905     /**
6906      * @cfg {Sting} align (left|right) for navbar forms
6907      */
6908     align : 'left',
6909
6910     // private
6911     activeAction : null,
6912  
6913     /**
6914      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6915      * element by passing it or its id or mask the form itself by passing in true.
6916      * @type Mixed
6917      */
6918     waitMsgTarget : false,
6919     
6920     loadMask : true,
6921     
6922     getAutoCreate : function(){
6923         
6924         var cfg = {
6925             tag: 'form',
6926             method : this.method || 'POST',
6927             id : this.id || Roo.id(),
6928             cls : ''
6929         }
6930         if (this.parent().xtype.match(/^Nav/)) {
6931             cfg.cls = 'navbar-form navbar-' + this.align;
6932             
6933         }
6934         
6935         if (this.labelAlign == 'left' ) {
6936             cfg.cls += ' form-horizontal';
6937         }
6938         
6939         
6940         return cfg;
6941     },
6942     initEvents : function()
6943     {
6944         this.el.on('submit', this.onSubmit, this);
6945         // this was added as random key presses on the form where triggering form submit.
6946         this.el.on('keypress', function(e) {
6947             if (e.getCharCode() != 13) {
6948                 return true;
6949             }
6950             // we might need to allow it for textareas.. and some other items.
6951             // check e.getTarget().
6952             
6953             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6954                 return true;
6955             }
6956         
6957             Roo.log("keypress blocked");
6958             
6959             e.preventDefault();
6960             return false;
6961         });
6962         
6963     },
6964     // private
6965     onSubmit : function(e){
6966         e.stopEvent();
6967     },
6968     
6969      /**
6970      * Returns true if client-side validation on the form is successful.
6971      * @return Boolean
6972      */
6973     isValid : function(){
6974         var items = this.getItems();
6975         var valid = true;
6976         items.each(function(f){
6977            if(!f.validate()){
6978                valid = false;
6979                
6980            }
6981         });
6982         return valid;
6983     },
6984     /**
6985      * Returns true if any fields in this form have changed since their original load.
6986      * @return Boolean
6987      */
6988     isDirty : function(){
6989         var dirty = false;
6990         var items = this.getItems();
6991         items.each(function(f){
6992            if(f.isDirty()){
6993                dirty = true;
6994                return false;
6995            }
6996            return true;
6997         });
6998         return dirty;
6999     },
7000      /**
7001      * Performs a predefined action (submit or load) or custom actions you define on this form.
7002      * @param {String} actionName The name of the action type
7003      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7004      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7005      * accept other config options):
7006      * <pre>
7007 Property          Type             Description
7008 ----------------  ---------------  ----------------------------------------------------------------------------------
7009 url               String           The url for the action (defaults to the form's url)
7010 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7011 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7012 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7013                                    validate the form on the client (defaults to false)
7014      * </pre>
7015      * @return {BasicForm} this
7016      */
7017     doAction : function(action, options){
7018         if(typeof action == 'string'){
7019             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7020         }
7021         if(this.fireEvent('beforeaction', this, action) !== false){
7022             this.beforeAction(action);
7023             action.run.defer(100, action);
7024         }
7025         return this;
7026     },
7027     
7028     // private
7029     beforeAction : function(action){
7030         var o = action.options;
7031         
7032         if(this.loadMask){
7033             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7034         }
7035         // not really supported yet.. ??
7036         
7037         //if(this.waitMsgTarget === true){
7038         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7039         //}else if(this.waitMsgTarget){
7040         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7041         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7042         //}else {
7043         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7044        // }
7045          
7046     },
7047
7048     // private
7049     afterAction : function(action, success){
7050         this.activeAction = null;
7051         var o = action.options;
7052         
7053         //if(this.waitMsgTarget === true){
7054             this.el.unmask();
7055         //}else if(this.waitMsgTarget){
7056         //    this.waitMsgTarget.unmask();
7057         //}else{
7058         //    Roo.MessageBox.updateProgress(1);
7059         //    Roo.MessageBox.hide();
7060        // }
7061         // 
7062         if(success){
7063             if(o.reset){
7064                 this.reset();
7065             }
7066             Roo.callback(o.success, o.scope, [this, action]);
7067             this.fireEvent('actioncomplete', this, action);
7068             
7069         }else{
7070             
7071             // failure condition..
7072             // we have a scenario where updates need confirming.
7073             // eg. if a locking scenario exists..
7074             // we look for { errors : { needs_confirm : true }} in the response.
7075             if (
7076                 (typeof(action.result) != 'undefined')  &&
7077                 (typeof(action.result.errors) != 'undefined')  &&
7078                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7079            ){
7080                 var _t = this;
7081                 Roo.log("not supported yet");
7082                  /*
7083                 
7084                 Roo.MessageBox.confirm(
7085                     "Change requires confirmation",
7086                     action.result.errorMsg,
7087                     function(r) {
7088                         if (r != 'yes') {
7089                             return;
7090                         }
7091                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7092                     }
7093                     
7094                 );
7095                 */
7096                 
7097                 
7098                 return;
7099             }
7100             
7101             Roo.callback(o.failure, o.scope, [this, action]);
7102             // show an error message if no failed handler is set..
7103             if (!this.hasListener('actionfailed')) {
7104                 Roo.log("need to add dialog support");
7105                 /*
7106                 Roo.MessageBox.alert("Error",
7107                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7108                         action.result.errorMsg :
7109                         "Saving Failed, please check your entries or try again"
7110                 );
7111                 */
7112             }
7113             
7114             this.fireEvent('actionfailed', this, action);
7115         }
7116         
7117     },
7118     /**
7119      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7120      * @param {String} id The value to search for
7121      * @return Field
7122      */
7123     findField : function(id){
7124         var items = this.getItems();
7125         var field = items.get(id);
7126         if(!field){
7127              items.each(function(f){
7128                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7129                     field = f;
7130                     return false;
7131                 }
7132                 return true;
7133             });
7134         }
7135         return field || null;
7136     },
7137      /**
7138      * Mark fields in this form invalid in bulk.
7139      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7140      * @return {BasicForm} this
7141      */
7142     markInvalid : function(errors){
7143         if(errors instanceof Array){
7144             for(var i = 0, len = errors.length; i < len; i++){
7145                 var fieldError = errors[i];
7146                 var f = this.findField(fieldError.id);
7147                 if(f){
7148                     f.markInvalid(fieldError.msg);
7149                 }
7150             }
7151         }else{
7152             var field, id;
7153             for(id in errors){
7154                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7155                     field.markInvalid(errors[id]);
7156                 }
7157             }
7158         }
7159         //Roo.each(this.childForms || [], function (f) {
7160         //    f.markInvalid(errors);
7161         //});
7162         
7163         return this;
7164     },
7165
7166     /**
7167      * Set values for fields in this form in bulk.
7168      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7169      * @return {BasicForm} this
7170      */
7171     setValues : function(values){
7172         if(values instanceof Array){ // array of objects
7173             for(var i = 0, len = values.length; i < len; i++){
7174                 var v = values[i];
7175                 var f = this.findField(v.id);
7176                 if(f){
7177                     f.setValue(v.value);
7178                     if(this.trackResetOnLoad){
7179                         f.originalValue = f.getValue();
7180                     }
7181                 }
7182             }
7183         }else{ // object hash
7184             var field, id;
7185             for(id in values){
7186                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7187                     
7188                     if (field.setFromData && 
7189                         field.valueField && 
7190                         field.displayField &&
7191                         // combos' with local stores can 
7192                         // be queried via setValue()
7193                         // to set their value..
7194                         (field.store && !field.store.isLocal)
7195                         ) {
7196                         // it's a combo
7197                         var sd = { };
7198                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7199                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7200                         field.setFromData(sd);
7201                         
7202                     } else {
7203                         field.setValue(values[id]);
7204                     }
7205                     
7206                     
7207                     if(this.trackResetOnLoad){
7208                         field.originalValue = field.getValue();
7209                     }
7210                 }
7211             }
7212         }
7213          
7214         //Roo.each(this.childForms || [], function (f) {
7215         //    f.setValues(values);
7216         //});
7217                 
7218         return this;
7219     },
7220
7221     /**
7222      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7223      * they are returned as an array.
7224      * @param {Boolean} asString
7225      * @return {Object}
7226      */
7227     getValues : function(asString){
7228         //if (this.childForms) {
7229             // copy values from the child forms
7230         //    Roo.each(this.childForms, function (f) {
7231         //        this.setValues(f.getValues());
7232         //    }, this);
7233         //}
7234         
7235         
7236         
7237         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7238         if(asString === true){
7239             return fs;
7240         }
7241         return Roo.urlDecode(fs);
7242     },
7243     
7244     /**
7245      * Returns the fields in this form as an object with key/value pairs. 
7246      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7247      * @return {Object}
7248      */
7249     getFieldValues : function(with_hidden)
7250     {
7251         var items = this.getItems();
7252         var ret = {};
7253         items.each(function(f){
7254             if (!f.getName()) {
7255                 return;
7256             }
7257             var v = f.getValue();
7258             if (f.inputType =='radio') {
7259                 if (typeof(ret[f.getName()]) == 'undefined') {
7260                     ret[f.getName()] = ''; // empty..
7261                 }
7262                 
7263                 if (!f.el.dom.checked) {
7264                     return;
7265                     
7266                 }
7267                 v = f.el.dom.value;
7268                 
7269             }
7270             
7271             // not sure if this supported any more..
7272             if ((typeof(v) == 'object') && f.getRawValue) {
7273                 v = f.getRawValue() ; // dates..
7274             }
7275             // combo boxes where name != hiddenName...
7276             if (f.name != f.getName()) {
7277                 ret[f.name] = f.getRawValue();
7278             }
7279             ret[f.getName()] = v;
7280         });
7281         
7282         return ret;
7283     },
7284
7285     /**
7286      * Clears all invalid messages in this form.
7287      * @return {BasicForm} this
7288      */
7289     clearInvalid : function(){
7290         var items = this.getItems();
7291         
7292         items.each(function(f){
7293            f.clearInvalid();
7294         });
7295         
7296         
7297         
7298         return this;
7299     },
7300
7301     /**
7302      * Resets this form.
7303      * @return {BasicForm} this
7304      */
7305     reset : function(){
7306         var items = this.getItems();
7307         items.each(function(f){
7308             f.reset();
7309         });
7310         
7311         Roo.each(this.childForms || [], function (f) {
7312             f.reset();
7313         });
7314        
7315         
7316         return this;
7317     },
7318     getItems : function()
7319     {
7320         var r=new Roo.util.MixedCollection(false, function(o){
7321             return o.id || (o.id = Roo.id());
7322         });
7323         var iter = function(el) {
7324             if (el.inputEl) {
7325                 r.add(el);
7326             }
7327             if (!el.items) {
7328                 return;
7329             }
7330             Roo.each(el.items,function(e) {
7331                 iter(e);
7332             });
7333             
7334             
7335         };
7336         
7337         iter(this);
7338         return r;
7339         
7340         
7341         
7342         
7343     }
7344     
7345 });
7346
7347  
7348 /*
7349  * Based on:
7350  * Ext JS Library 1.1.1
7351  * Copyright(c) 2006-2007, Ext JS, LLC.
7352  *
7353  * Originally Released Under LGPL - original licence link has changed is not relivant.
7354  *
7355  * Fork - LGPL
7356  * <script type="text/javascript">
7357  */
7358 /**
7359  * @class Roo.form.VTypes
7360  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7361  * @singleton
7362  */
7363 Roo.form.VTypes = function(){
7364     // closure these in so they are only created once.
7365     var alpha = /^[a-zA-Z_]+$/;
7366     var alphanum = /^[a-zA-Z0-9_]+$/;
7367     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7368     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7369
7370     // All these messages and functions are configurable
7371     return {
7372         /**
7373          * The function used to validate email addresses
7374          * @param {String} value The email address
7375          */
7376         'email' : function(v){
7377             return email.test(v);
7378         },
7379         /**
7380          * The error text to display when the email validation function returns false
7381          * @type String
7382          */
7383         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7384         /**
7385          * The keystroke filter mask to be applied on email input
7386          * @type RegExp
7387          */
7388         'emailMask' : /[a-z0-9_\.\-@]/i,
7389
7390         /**
7391          * The function used to validate URLs
7392          * @param {String} value The URL
7393          */
7394         'url' : function(v){
7395             return url.test(v);
7396         },
7397         /**
7398          * The error text to display when the url validation function returns false
7399          * @type String
7400          */
7401         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7402         
7403         /**
7404          * The function used to validate alpha values
7405          * @param {String} value The value
7406          */
7407         'alpha' : function(v){
7408             return alpha.test(v);
7409         },
7410         /**
7411          * The error text to display when the alpha validation function returns false
7412          * @type String
7413          */
7414         'alphaText' : 'This field should only contain letters and _',
7415         /**
7416          * The keystroke filter mask to be applied on alpha input
7417          * @type RegExp
7418          */
7419         'alphaMask' : /[a-z_]/i,
7420
7421         /**
7422          * The function used to validate alphanumeric values
7423          * @param {String} value The value
7424          */
7425         'alphanum' : function(v){
7426             return alphanum.test(v);
7427         },
7428         /**
7429          * The error text to display when the alphanumeric validation function returns false
7430          * @type String
7431          */
7432         'alphanumText' : 'This field should only contain letters, numbers and _',
7433         /**
7434          * The keystroke filter mask to be applied on alphanumeric input
7435          * @type RegExp
7436          */
7437         'alphanumMask' : /[a-z0-9_]/i
7438     };
7439 }();/*
7440  * - LGPL
7441  *
7442  * Input
7443  * 
7444  */
7445
7446 /**
7447  * @class Roo.bootstrap.Input
7448  * @extends Roo.bootstrap.Component
7449  * Bootstrap Input class
7450  * @cfg {Boolean} disabled is it disabled
7451  * @cfg {String} fieldLabel - the label associated
7452  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7453  * @cfg {String} name name of the input
7454  * @cfg {string} fieldLabel - the label associated
7455  * @cfg {string}  inputType - input / file submit ...
7456  * @cfg {string} placeholder - placeholder to put in text.
7457  * @cfg {string}  before - input group add on before
7458  * @cfg {string} after - input group add on after
7459  * @cfg {string} size - (lg|sm) or leave empty..
7460  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7461  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7462  * @cfg {Number} md colspan out of 12 for computer-sized screens
7463  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7464  * @cfg {string} value default value of the input
7465  * @cfg {Number} labelWidth set the width of label (0-12)
7466  * @cfg {String} labelAlign (top|left)
7467  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7468  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7469
7470  * @cfg {String} align (left|center|right) Default left
7471  * 
7472  * 
7473  * 
7474  * @constructor
7475  * Create a new Input
7476  * @param {Object} config The config object
7477  */
7478
7479 Roo.bootstrap.Input = function(config){
7480     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7481    
7482         this.addEvents({
7483             /**
7484              * @event focus
7485              * Fires when this field receives input focus.
7486              * @param {Roo.form.Field} this
7487              */
7488             focus : true,
7489             /**
7490              * @event blur
7491              * Fires when this field loses input focus.
7492              * @param {Roo.form.Field} this
7493              */
7494             blur : true,
7495             /**
7496              * @event specialkey
7497              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7498              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7499              * @param {Roo.form.Field} this
7500              * @param {Roo.EventObject} e The event object
7501              */
7502             specialkey : true,
7503             /**
7504              * @event change
7505              * Fires just before the field blurs if the field value has changed.
7506              * @param {Roo.form.Field} this
7507              * @param {Mixed} newValue The new value
7508              * @param {Mixed} oldValue The original value
7509              */
7510             change : true,
7511             /**
7512              * @event invalid
7513              * Fires after the field has been marked as invalid.
7514              * @param {Roo.form.Field} this
7515              * @param {String} msg The validation message
7516              */
7517             invalid : true,
7518             /**
7519              * @event valid
7520              * Fires after the field has been validated with no errors.
7521              * @param {Roo.form.Field} this
7522              */
7523             valid : true,
7524              /**
7525              * @event keyup
7526              * Fires after the key up
7527              * @param {Roo.form.Field} this
7528              * @param {Roo.EventObject}  e The event Object
7529              */
7530             keyup : true
7531         });
7532 };
7533
7534 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7535      /**
7536      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7537       automatic validation (defaults to "keyup").
7538      */
7539     validationEvent : "keyup",
7540      /**
7541      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7542      */
7543     validateOnBlur : true,
7544     /**
7545      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7546      */
7547     validationDelay : 250,
7548      /**
7549      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7550      */
7551     focusClass : "x-form-focus",  // not needed???
7552     
7553        
7554     /**
7555      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7556      */
7557     invalidClass : "has-warning",
7558     
7559     /**
7560      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7561      */
7562     validClass : "has-success",
7563     
7564     /**
7565      * @cfg {Boolean} hasFeedback (true|false) default true
7566      */
7567     hasFeedback : true,
7568     
7569     /**
7570      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7571      */
7572     invalidFeedbackClass : "glyphicon-warning-sign",
7573     
7574     /**
7575      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7576      */
7577     validFeedbackClass : "glyphicon-ok",
7578     
7579     /**
7580      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7581      */
7582     selectOnFocus : false,
7583     
7584      /**
7585      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7586      */
7587     maskRe : null,
7588        /**
7589      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7590      */
7591     vtype : null,
7592     
7593       /**
7594      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7595      */
7596     disableKeyFilter : false,
7597     
7598        /**
7599      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7600      */
7601     disabled : false,
7602      /**
7603      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7604      */
7605     allowBlank : true,
7606     /**
7607      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7608      */
7609     blankText : "This field is required",
7610     
7611      /**
7612      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7613      */
7614     minLength : 0,
7615     /**
7616      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7617      */
7618     maxLength : Number.MAX_VALUE,
7619     /**
7620      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7621      */
7622     minLengthText : "The minimum length for this field is {0}",
7623     /**
7624      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7625      */
7626     maxLengthText : "The maximum length for this field is {0}",
7627   
7628     
7629     /**
7630      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7631      * If available, this function will be called only after the basic validators all return true, and will be passed the
7632      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7633      */
7634     validator : null,
7635     /**
7636      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7637      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7638      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7639      */
7640     regex : null,
7641     /**
7642      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7643      */
7644     regexText : "",
7645     
7646     autocomplete: false,
7647     
7648     
7649     fieldLabel : '',
7650     inputType : 'text',
7651     
7652     name : false,
7653     placeholder: false,
7654     before : false,
7655     after : false,
7656     size : false,
7657     hasFocus : false,
7658     preventMark: false,
7659     isFormField : true,
7660     value : '',
7661     labelWidth : 2,
7662     labelAlign : false,
7663     readOnly : false,
7664     align : false,
7665     formatedValue : false,
7666     
7667     parentLabelAlign : function()
7668     {
7669         var parent = this;
7670         while (parent.parent()) {
7671             parent = parent.parent();
7672             if (typeof(parent.labelAlign) !='undefined') {
7673                 return parent.labelAlign;
7674             }
7675         }
7676         return 'left';
7677         
7678     },
7679     
7680     getAutoCreate : function(){
7681         
7682         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7683         
7684         var id = Roo.id();
7685         
7686         var cfg = {};
7687         
7688         if(this.inputType != 'hidden'){
7689             cfg.cls = 'form-group' //input-group
7690         }
7691         
7692         var input =  {
7693             tag: 'input',
7694             id : id,
7695             type : this.inputType,
7696             value : this.value,
7697             cls : 'form-control',
7698             placeholder : this.placeholder || '',
7699             autocomplete : this.autocomplete || 'new-password'
7700         };
7701         
7702         
7703         if(this.align){
7704             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7705         }
7706         
7707         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7708             input.maxLength = this.maxLength;
7709         }
7710         
7711         if (this.disabled) {
7712             input.disabled=true;
7713         }
7714         
7715         if (this.readOnly) {
7716             input.readonly=true;
7717         }
7718         
7719         if (this.name) {
7720             input.name = this.name;
7721         }
7722         if (this.size) {
7723             input.cls += ' input-' + this.size;
7724         }
7725         var settings=this;
7726         ['xs','sm','md','lg'].map(function(size){
7727             if (settings[size]) {
7728                 cfg.cls += ' col-' + size + '-' + settings[size];
7729             }
7730         });
7731         
7732         var inputblock = input;
7733         
7734         var feedback = {
7735             tag: 'span',
7736             cls: 'glyphicon form-control-feedback'
7737         };
7738             
7739         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7740             
7741             inputblock = {
7742                 cls : 'has-feedback',
7743                 cn :  [
7744                     input,
7745                     feedback
7746                 ] 
7747             };  
7748         }
7749         
7750         if (this.before || this.after) {
7751             
7752             inputblock = {
7753                 cls : 'input-group',
7754                 cn :  [] 
7755             };
7756             
7757             if (this.before && typeof(this.before) == 'string') {
7758                 
7759                 inputblock.cn.push({
7760                     tag :'span',
7761                     cls : 'roo-input-before input-group-addon',
7762                     html : this.before
7763                 });
7764             }
7765             if (this.before && typeof(this.before) == 'object') {
7766                 this.before = Roo.factory(this.before);
7767                 Roo.log(this.before);
7768                 inputblock.cn.push({
7769                     tag :'span',
7770                     cls : 'roo-input-before input-group-' +
7771                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7772                 });
7773             }
7774             
7775             inputblock.cn.push(input);
7776             
7777             if (this.after && typeof(this.after) == 'string') {
7778                 inputblock.cn.push({
7779                     tag :'span',
7780                     cls : 'roo-input-after input-group-addon',
7781                     html : this.after
7782                 });
7783             }
7784             if (this.after && typeof(this.after) == 'object') {
7785                 this.after = Roo.factory(this.after);
7786                 Roo.log(this.after);
7787                 inputblock.cn.push({
7788                     tag :'span',
7789                     cls : 'roo-input-after input-group-' +
7790                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7791                 });
7792             }
7793             
7794             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7795                 inputblock.cls += ' has-feedback';
7796                 inputblock.cn.push(feedback);
7797             }
7798         };
7799         
7800         if (align ==='left' && this.fieldLabel.length) {
7801                 Roo.log("left and has label");
7802                 cfg.cn = [
7803                     
7804                     {
7805                         tag: 'label',
7806                         'for' :  id,
7807                         cls : 'control-label col-sm-' + this.labelWidth,
7808                         html : this.fieldLabel
7809                         
7810                     },
7811                     {
7812                         cls : "col-sm-" + (12 - this.labelWidth), 
7813                         cn: [
7814                             inputblock
7815                         ]
7816                     }
7817                     
7818                 ];
7819         } else if ( this.fieldLabel.length) {
7820                 Roo.log(" label");
7821                  cfg.cn = [
7822                    
7823                     {
7824                         tag: 'label',
7825                         //cls : 'input-group-addon',
7826                         html : this.fieldLabel
7827                         
7828                     },
7829                     
7830                     inputblock
7831                     
7832                 ];
7833
7834         } else {
7835             
7836                 Roo.log(" no label && no align");
7837                 cfg.cn = [
7838                     
7839                         inputblock
7840                     
7841                 ];
7842                 
7843                 
7844         };
7845         Roo.log('input-parentType: ' + this.parentType);
7846         
7847         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7848            cfg.cls += ' navbar-form';
7849            Roo.log(cfg);
7850         }
7851         
7852         return cfg;
7853         
7854     },
7855     /**
7856      * return the real input element.
7857      */
7858     inputEl: function ()
7859     {
7860         return this.el.select('input.form-control',true).first();
7861     },
7862     
7863     tooltipEl : function()
7864     {
7865         return this.inputEl();
7866     },
7867     
7868     setDisabled : function(v)
7869     {
7870         var i  = this.inputEl().dom;
7871         if (!v) {
7872             i.removeAttribute('disabled');
7873             return;
7874             
7875         }
7876         i.setAttribute('disabled','true');
7877     },
7878     initEvents : function()
7879     {
7880           
7881         this.inputEl().on("keydown" , this.fireKey,  this);
7882         this.inputEl().on("focus", this.onFocus,  this);
7883         this.inputEl().on("blur", this.onBlur,  this);
7884         
7885         this.inputEl().relayEvent('keyup', this);
7886  
7887         // reference to original value for reset
7888         this.originalValue = this.getValue();
7889         //Roo.form.TextField.superclass.initEvents.call(this);
7890         if(this.validationEvent == 'keyup'){
7891             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7892             this.inputEl().on('keyup', this.filterValidation, this);
7893         }
7894         else if(this.validationEvent !== false){
7895             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7896         }
7897         
7898         if(this.selectOnFocus){
7899             this.on("focus", this.preFocus, this);
7900             
7901         }
7902         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7903             this.inputEl().on("keypress", this.filterKeys, this);
7904         }
7905        /* if(this.grow){
7906             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7907             this.el.on("click", this.autoSize,  this);
7908         }
7909         */
7910         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7911             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7912         }
7913         
7914         if (typeof(this.before) == 'object') {
7915             this.before.render(this.el.select('.roo-input-before',true).first());
7916         }
7917         if (typeof(this.after) == 'object') {
7918             this.after.render(this.el.select('.roo-input-after',true).first());
7919         }
7920         
7921         
7922     },
7923     filterValidation : function(e){
7924         if(!e.isNavKeyPress()){
7925             this.validationTask.delay(this.validationDelay);
7926         }
7927     },
7928      /**
7929      * Validates the field value
7930      * @return {Boolean} True if the value is valid, else false
7931      */
7932     validate : function(){
7933         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7934         if(this.disabled || this.validateValue(this.getRawValue())){
7935             this.markValid();
7936             return true;
7937         }
7938         
7939         this.markInvalid();
7940         return false;
7941     },
7942     
7943     
7944     /**
7945      * Validates a value according to the field's validation rules and marks the field as invalid
7946      * if the validation fails
7947      * @param {Mixed} value The value to validate
7948      * @return {Boolean} True if the value is valid, else false
7949      */
7950     validateValue : function(value){
7951         if(value.length < 1)  { // if it's blank
7952             if(this.allowBlank){
7953                 return true;
7954             }
7955             return false;
7956         }
7957         
7958         if(value.length < this.minLength){
7959             return false;
7960         }
7961         if(value.length > this.maxLength){
7962             return false;
7963         }
7964         if(this.vtype){
7965             var vt = Roo.form.VTypes;
7966             if(!vt[this.vtype](value, this)){
7967                 return false;
7968             }
7969         }
7970         if(typeof this.validator == "function"){
7971             var msg = this.validator(value);
7972             if(msg !== true){
7973                 return false;
7974             }
7975         }
7976         
7977         if(this.regex && !this.regex.test(value)){
7978             return false;
7979         }
7980         
7981         return true;
7982     },
7983
7984     
7985     
7986      // private
7987     fireKey : function(e){
7988         //Roo.log('field ' + e.getKey());
7989         if(e.isNavKeyPress()){
7990             this.fireEvent("specialkey", this, e);
7991         }
7992     },
7993     focus : function (selectText){
7994         if(this.rendered){
7995             this.inputEl().focus();
7996             if(selectText === true){
7997                 this.inputEl().dom.select();
7998             }
7999         }
8000         return this;
8001     } ,
8002     
8003     onFocus : function(){
8004         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8005            // this.el.addClass(this.focusClass);
8006         }
8007         if(!this.hasFocus){
8008             this.hasFocus = true;
8009             this.startValue = this.getValue();
8010             this.fireEvent("focus", this);
8011         }
8012     },
8013     
8014     beforeBlur : Roo.emptyFn,
8015
8016     
8017     // private
8018     onBlur : function(){
8019         this.beforeBlur();
8020         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8021             //this.el.removeClass(this.focusClass);
8022         }
8023         this.hasFocus = false;
8024         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8025             this.validate();
8026         }
8027         var v = this.getValue();
8028         if(String(v) !== String(this.startValue)){
8029             this.fireEvent('change', this, v, this.startValue);
8030         }
8031         this.fireEvent("blur", this);
8032     },
8033     
8034     /**
8035      * Resets the current field value to the originally loaded value and clears any validation messages
8036      */
8037     reset : function(){
8038         this.setValue(this.originalValue);
8039         this.validate();
8040     },
8041      /**
8042      * Returns the name of the field
8043      * @return {Mixed} name The name field
8044      */
8045     getName: function(){
8046         return this.name;
8047     },
8048      /**
8049      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8050      * @return {Mixed} value The field value
8051      */
8052     getValue : function(){
8053         
8054         var v = this.inputEl().getValue();
8055         
8056         return v;
8057     },
8058     /**
8059      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8060      * @return {Mixed} value The field value
8061      */
8062     getRawValue : function(){
8063         var v = this.inputEl().getValue();
8064         
8065         return v;
8066     },
8067     
8068     /**
8069      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8070      * @param {Mixed} value The value to set
8071      */
8072     setRawValue : function(v){
8073         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8074     },
8075     
8076     selectText : function(start, end){
8077         var v = this.getRawValue();
8078         if(v.length > 0){
8079             start = start === undefined ? 0 : start;
8080             end = end === undefined ? v.length : end;
8081             var d = this.inputEl().dom;
8082             if(d.setSelectionRange){
8083                 d.setSelectionRange(start, end);
8084             }else if(d.createTextRange){
8085                 var range = d.createTextRange();
8086                 range.moveStart("character", start);
8087                 range.moveEnd("character", v.length-end);
8088                 range.select();
8089             }
8090         }
8091     },
8092     
8093     /**
8094      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8095      * @param {Mixed} value The value to set
8096      */
8097     setValue : function(v){
8098         this.value = v;
8099         if(this.rendered){
8100             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8101             this.validate();
8102         }
8103     },
8104     
8105     /*
8106     processValue : function(value){
8107         if(this.stripCharsRe){
8108             var newValue = value.replace(this.stripCharsRe, '');
8109             if(newValue !== value){
8110                 this.setRawValue(newValue);
8111                 return newValue;
8112             }
8113         }
8114         return value;
8115     },
8116   */
8117     preFocus : function(){
8118         
8119         if(this.selectOnFocus){
8120             this.inputEl().dom.select();
8121         }
8122     },
8123     filterKeys : function(e){
8124         var k = e.getKey();
8125         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8126             return;
8127         }
8128         var c = e.getCharCode(), cc = String.fromCharCode(c);
8129         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8130             return;
8131         }
8132         if(!this.maskRe.test(cc)){
8133             e.stopEvent();
8134         }
8135     },
8136      /**
8137      * Clear any invalid styles/messages for this field
8138      */
8139     clearInvalid : function(){
8140         
8141         if(!this.el || this.preventMark){ // not rendered
8142             return;
8143         }
8144         this.el.removeClass(this.invalidClass);
8145         
8146         this.fireEvent('valid', this);
8147     },
8148     
8149      /**
8150      * Mark this field as valid
8151      */
8152     markValid : function(){
8153         if(!this.el  || this.preventMark){ // not rendered
8154             return;
8155         }
8156         
8157         this.el.removeClass([this.invalidClass, this.validClass]);
8158         
8159         if(this.disabled || this.allowBlank){
8160             return;
8161         }
8162         
8163         this.el.addClass(this.validClass);
8164         
8165         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8166             
8167             var feedback = this.el.select('.form-control-feedback', true).first();
8168             
8169             if(feedback){
8170                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8171                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8172             }
8173             
8174         }
8175         
8176         this.fireEvent('valid', this);
8177     },
8178     
8179      /**
8180      * Mark this field as invalid
8181      * @param {String} msg The validation message
8182      */
8183     markInvalid : function(msg){
8184         if(!this.el  || this.preventMark){ // not rendered
8185             return;
8186         }
8187         
8188         this.el.removeClass([this.invalidClass, this.validClass]);
8189         
8190         if(this.disabled || this.allowBlank){
8191             return;
8192         }
8193         
8194         this.el.addClass(this.invalidClass);
8195         
8196         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8197             
8198             var feedback = this.el.select('.form-control-feedback', true).first();
8199             
8200             if(feedback){
8201                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8202                 
8203                 if(this.getValue().length){
8204                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8205                 }
8206                 
8207             }
8208             
8209         }
8210         
8211         this.fireEvent('invalid', this, msg);
8212     },
8213     // private
8214     SafariOnKeyDown : function(event)
8215     {
8216         // this is a workaround for a password hang bug on chrome/ webkit.
8217         
8218         var isSelectAll = false;
8219         
8220         if(this.inputEl().dom.selectionEnd > 0){
8221             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8222         }
8223         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8224             event.preventDefault();
8225             this.setValue('');
8226             return;
8227         }
8228         
8229         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8230             
8231             event.preventDefault();
8232             // this is very hacky as keydown always get's upper case.
8233             //
8234             var cc = String.fromCharCode(event.getCharCode());
8235             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8236             
8237         }
8238     },
8239     adjustWidth : function(tag, w){
8240         tag = tag.toLowerCase();
8241         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8242             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8243                 if(tag == 'input'){
8244                     return w + 2;
8245                 }
8246                 if(tag == 'textarea'){
8247                     return w-2;
8248                 }
8249             }else if(Roo.isOpera){
8250                 if(tag == 'input'){
8251                     return w + 2;
8252                 }
8253                 if(tag == 'textarea'){
8254                     return w-2;
8255                 }
8256             }
8257         }
8258         return w;
8259     }
8260     
8261 });
8262
8263  
8264 /*
8265  * - LGPL
8266  *
8267  * Input
8268  * 
8269  */
8270
8271 /**
8272  * @class Roo.bootstrap.TextArea
8273  * @extends Roo.bootstrap.Input
8274  * Bootstrap TextArea class
8275  * @cfg {Number} cols Specifies the visible width of a text area
8276  * @cfg {Number} rows Specifies the visible number of lines in a text area
8277  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8278  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8279  * @cfg {string} html text
8280  * 
8281  * @constructor
8282  * Create a new TextArea
8283  * @param {Object} config The config object
8284  */
8285
8286 Roo.bootstrap.TextArea = function(config){
8287     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8288    
8289 };
8290
8291 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8292      
8293     cols : false,
8294     rows : 5,
8295     readOnly : false,
8296     warp : 'soft',
8297     resize : false,
8298     value: false,
8299     html: false,
8300     
8301     getAutoCreate : function(){
8302         
8303         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8304         
8305         var id = Roo.id();
8306         
8307         var cfg = {};
8308         
8309         var input =  {
8310             tag: 'textarea',
8311             id : id,
8312             warp : this.warp,
8313             rows : this.rows,
8314             value : this.value || '',
8315             html: this.html || '',
8316             cls : 'form-control',
8317             placeholder : this.placeholder || '' 
8318             
8319         };
8320         
8321         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8322             input.maxLength = this.maxLength;
8323         }
8324         
8325         if(this.resize){
8326             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8327         }
8328         
8329         if(this.cols){
8330             input.cols = this.cols;
8331         }
8332         
8333         if (this.readOnly) {
8334             input.readonly = true;
8335         }
8336         
8337         if (this.name) {
8338             input.name = this.name;
8339         }
8340         
8341         if (this.size) {
8342             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8343         }
8344         
8345         var settings=this;
8346         ['xs','sm','md','lg'].map(function(size){
8347             if (settings[size]) {
8348                 cfg.cls += ' col-' + size + '-' + settings[size];
8349             }
8350         });
8351         
8352         var inputblock = input;
8353         
8354         if(this.hasFeedback && !this.allowBlank){
8355             
8356             var feedback = {
8357                 tag: 'span',
8358                 cls: 'glyphicon form-control-feedback'
8359             };
8360
8361             inputblock = {
8362                 cls : 'has-feedback',
8363                 cn :  [
8364                     input,
8365                     feedback
8366                 ] 
8367             };  
8368         }
8369         
8370         
8371         if (this.before || this.after) {
8372             
8373             inputblock = {
8374                 cls : 'input-group',
8375                 cn :  [] 
8376             };
8377             if (this.before) {
8378                 inputblock.cn.push({
8379                     tag :'span',
8380                     cls : 'input-group-addon',
8381                     html : this.before
8382                 });
8383             }
8384             
8385             inputblock.cn.push(input);
8386             
8387             if(this.hasFeedback && !this.allowBlank){
8388                 inputblock.cls += ' has-feedback';
8389                 inputblock.cn.push(feedback);
8390             }
8391             
8392             if (this.after) {
8393                 inputblock.cn.push({
8394                     tag :'span',
8395                     cls : 'input-group-addon',
8396                     html : this.after
8397                 });
8398             }
8399             
8400         }
8401         
8402         if (align ==='left' && this.fieldLabel.length) {
8403                 Roo.log("left and has label");
8404                 cfg.cn = [
8405                     
8406                     {
8407                         tag: 'label',
8408                         'for' :  id,
8409                         cls : 'control-label col-sm-' + this.labelWidth,
8410                         html : this.fieldLabel
8411                         
8412                     },
8413                     {
8414                         cls : "col-sm-" + (12 - this.labelWidth), 
8415                         cn: [
8416                             inputblock
8417                         ]
8418                     }
8419                     
8420                 ];
8421         } else if ( this.fieldLabel.length) {
8422                 Roo.log(" label");
8423                  cfg.cn = [
8424                    
8425                     {
8426                         tag: 'label',
8427                         //cls : 'input-group-addon',
8428                         html : this.fieldLabel
8429                         
8430                     },
8431                     
8432                     inputblock
8433                     
8434                 ];
8435
8436         } else {
8437             
8438                    Roo.log(" no label && no align");
8439                 cfg.cn = [
8440                     
8441                         inputblock
8442                     
8443                 ];
8444                 
8445                 
8446         }
8447         
8448         if (this.disabled) {
8449             input.disabled=true;
8450         }
8451         
8452         return cfg;
8453         
8454     },
8455     /**
8456      * return the real textarea element.
8457      */
8458     inputEl: function ()
8459     {
8460         return this.el.select('textarea.form-control',true).first();
8461     }
8462 });
8463
8464  
8465 /*
8466  * - LGPL
8467  *
8468  * trigger field - base class for combo..
8469  * 
8470  */
8471  
8472 /**
8473  * @class Roo.bootstrap.TriggerField
8474  * @extends Roo.bootstrap.Input
8475  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8476  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8477  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8478  * for which you can provide a custom implementation.  For example:
8479  * <pre><code>
8480 var trigger = new Roo.bootstrap.TriggerField();
8481 trigger.onTriggerClick = myTriggerFn;
8482 trigger.applyTo('my-field');
8483 </code></pre>
8484  *
8485  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8486  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8487  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8488  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8489  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8490
8491  * @constructor
8492  * Create a new TriggerField.
8493  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8494  * to the base TextField)
8495  */
8496 Roo.bootstrap.TriggerField = function(config){
8497     this.mimicing = false;
8498     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8499 };
8500
8501 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8502     /**
8503      * @cfg {String} triggerClass A CSS class to apply to the trigger
8504      */
8505      /**
8506      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8507      */
8508     hideTrigger:false,
8509
8510     /**
8511      * @cfg {Boolean} removable (true|false) special filter default false
8512      */
8513     removable : false,
8514     
8515     /** @cfg {Boolean} grow @hide */
8516     /** @cfg {Number} growMin @hide */
8517     /** @cfg {Number} growMax @hide */
8518
8519     /**
8520      * @hide 
8521      * @method
8522      */
8523     autoSize: Roo.emptyFn,
8524     // private
8525     monitorTab : true,
8526     // private
8527     deferHeight : true,
8528
8529     
8530     actionMode : 'wrap',
8531     
8532     caret : false,
8533     
8534     
8535     getAutoCreate : function(){
8536        
8537         var align = this.labelAlign || this.parentLabelAlign();
8538         
8539         var id = Roo.id();
8540         
8541         var cfg = {
8542             cls: 'form-group' //input-group
8543         };
8544         
8545         
8546         var input =  {
8547             tag: 'input',
8548             id : id,
8549             type : this.inputType,
8550             cls : 'form-control',
8551             autocomplete: 'new-password',
8552             placeholder : this.placeholder || '' 
8553             
8554         };
8555         if (this.name) {
8556             input.name = this.name;
8557         }
8558         if (this.size) {
8559             input.cls += ' input-' + this.size;
8560         }
8561         
8562         if (this.disabled) {
8563             input.disabled=true;
8564         }
8565         
8566         var inputblock = input;
8567         
8568         if(this.hasFeedback && !this.allowBlank){
8569             
8570             var feedback = {
8571                 tag: 'span',
8572                 cls: 'glyphicon form-control-feedback'
8573             };
8574             
8575             if(this.removable && !this.editable && !this.tickable){
8576                 inputblock = {
8577                     cls : 'has-feedback',
8578                     cn :  [
8579                         inputblock,
8580                         {
8581                             tag: 'button',
8582                             html : 'x',
8583                             cls : 'roo-combo-removable-btn close'
8584                         },
8585                         feedback
8586                     ] 
8587                 };
8588             } else {
8589                 inputblock = {
8590                     cls : 'has-feedback',
8591                     cn :  [
8592                         inputblock,
8593                         feedback
8594                     ] 
8595                 };
8596             }
8597
8598         } else {
8599             if(this.removable && !this.editable && !this.tickable){
8600                 inputblock = {
8601                     cls : 'roo-removable',
8602                     cn :  [
8603                         inputblock,
8604                         {
8605                             tag: 'button',
8606                             html : 'x',
8607                             cls : 'roo-combo-removable-btn close'
8608                         }
8609                     ] 
8610                 };
8611             }
8612         }
8613         
8614         if (this.before || this.after) {
8615             
8616             inputblock = {
8617                 cls : 'input-group',
8618                 cn :  [] 
8619             };
8620             if (this.before) {
8621                 inputblock.cn.push({
8622                     tag :'span',
8623                     cls : 'input-group-addon',
8624                     html : this.before
8625                 });
8626             }
8627             
8628             inputblock.cn.push(input);
8629             
8630             if(this.hasFeedback && !this.allowBlank){
8631                 inputblock.cls += ' has-feedback';
8632                 inputblock.cn.push(feedback);
8633             }
8634             
8635             if (this.after) {
8636                 inputblock.cn.push({
8637                     tag :'span',
8638                     cls : 'input-group-addon',
8639                     html : this.after
8640                 });
8641             }
8642             
8643         };
8644         
8645         var box = {
8646             tag: 'div',
8647             cn: [
8648                 {
8649                     tag: 'input',
8650                     type : 'hidden',
8651                     cls: 'form-hidden-field'
8652                 },
8653                 inputblock
8654             ]
8655             
8656         };
8657         
8658         if(this.multiple){
8659             Roo.log('multiple');
8660             
8661             box = {
8662                 tag: 'div',
8663                 cn: [
8664                     {
8665                         tag: 'input',
8666                         type : 'hidden',
8667                         cls: 'form-hidden-field'
8668                     },
8669                     {
8670                         tag: 'ul',
8671                         cls: 'select2-choices',
8672                         cn:[
8673                             {
8674                                 tag: 'li',
8675                                 cls: 'select2-search-field',
8676                                 cn: [
8677
8678                                     inputblock
8679                                 ]
8680                             }
8681                         ]
8682                     }
8683                 ]
8684             }
8685         };
8686         
8687         var combobox = {
8688             cls: 'select2-container input-group',
8689             cn: [
8690                 box
8691 //                {
8692 //                    tag: 'ul',
8693 //                    cls: 'typeahead typeahead-long dropdown-menu',
8694 //                    style: 'display:none'
8695 //                }
8696             ]
8697         };
8698         
8699         if(!this.multiple && this.showToggleBtn){
8700             
8701             var caret = {
8702                         tag: 'span',
8703                         cls: 'caret'
8704              };
8705             if (this.caret != false) {
8706                 caret = {
8707                      tag: 'i',
8708                      cls: 'fa fa-' + this.caret
8709                 };
8710                 
8711             }
8712             
8713             combobox.cn.push({
8714                 tag :'span',
8715                 cls : 'input-group-addon btn dropdown-toggle',
8716                 cn : [
8717                     caret,
8718                     {
8719                         tag: 'span',
8720                         cls: 'combobox-clear',
8721                         cn  : [
8722                             {
8723                                 tag : 'i',
8724                                 cls: 'icon-remove'
8725                             }
8726                         ]
8727                     }
8728                 ]
8729
8730             })
8731         }
8732         
8733         if(this.multiple){
8734             combobox.cls += ' select2-container-multi';
8735         }
8736         
8737         if (align ==='left' && this.fieldLabel.length) {
8738             
8739                 Roo.log("left and has label");
8740                 cfg.cn = [
8741                     
8742                     {
8743                         tag: 'label',
8744                         'for' :  id,
8745                         cls : 'control-label col-sm-' + this.labelWidth,
8746                         html : this.fieldLabel
8747                         
8748                     },
8749                     {
8750                         cls : "col-sm-" + (12 - this.labelWidth), 
8751                         cn: [
8752                             combobox
8753                         ]
8754                     }
8755                     
8756                 ];
8757         } else if ( this.fieldLabel.length) {
8758                 Roo.log(" label");
8759                  cfg.cn = [
8760                    
8761                     {
8762                         tag: 'label',
8763                         //cls : 'input-group-addon',
8764                         html : this.fieldLabel
8765                         
8766                     },
8767                     
8768                     combobox
8769                     
8770                 ];
8771
8772         } else {
8773             
8774                 Roo.log(" no label && no align");
8775                 cfg = combobox
8776                      
8777                 
8778         }
8779          
8780         var settings=this;
8781         ['xs','sm','md','lg'].map(function(size){
8782             if (settings[size]) {
8783                 cfg.cls += ' col-' + size + '-' + settings[size];
8784             }
8785         });
8786         Roo.log(cfg);
8787         return cfg;
8788         
8789     },
8790     
8791     
8792     
8793     // private
8794     onResize : function(w, h){
8795 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8796 //        if(typeof w == 'number'){
8797 //            var x = w - this.trigger.getWidth();
8798 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8799 //            this.trigger.setStyle('left', x+'px');
8800 //        }
8801     },
8802
8803     // private
8804     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8805
8806     // private
8807     getResizeEl : function(){
8808         return this.inputEl();
8809     },
8810
8811     // private
8812     getPositionEl : function(){
8813         return this.inputEl();
8814     },
8815
8816     // private
8817     alignErrorIcon : function(){
8818         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8819     },
8820
8821     // private
8822     initEvents : function(){
8823         
8824         this.createList();
8825         
8826         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8827         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8828         if(!this.multiple && this.showToggleBtn){
8829             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8830             if(this.hideTrigger){
8831                 this.trigger.setDisplayed(false);
8832             }
8833             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8834         }
8835         
8836         if(this.multiple){
8837             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8838         }
8839         
8840         if(this.removable && !this.editable && !this.tickable){
8841             var close = this.closeTriggerEl();
8842             
8843             if(close){
8844                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8845                 close.on('click', this.removeBtnClick, this, close);
8846             }
8847         }
8848         
8849         //this.trigger.addClassOnOver('x-form-trigger-over');
8850         //this.trigger.addClassOnClick('x-form-trigger-click');
8851         
8852         //if(!this.width){
8853         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8854         //}
8855     },
8856     
8857     closeTriggerEl : function()
8858     {
8859         var close = this.el.select('.roo-combo-removable-btn', true).first();
8860         return close ? close : false;
8861     },
8862     
8863     removeBtnClick : function(e, h, el)
8864     {
8865         e.preventDefault();
8866         
8867         if(this.fireEvent("remove", this) !== false){
8868             this.reset();
8869         }
8870     },
8871     
8872     createList : function()
8873     {
8874         this.list = Roo.get(document.body).createChild({
8875             tag: 'ul',
8876             cls: 'typeahead typeahead-long dropdown-menu',
8877             style: 'display:none'
8878         });
8879         
8880         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8881         
8882     },
8883
8884     // private
8885     initTrigger : function(){
8886        
8887     },
8888
8889     // private
8890     onDestroy : function(){
8891         if(this.trigger){
8892             this.trigger.removeAllListeners();
8893           //  this.trigger.remove();
8894         }
8895         //if(this.wrap){
8896         //    this.wrap.remove();
8897         //}
8898         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8899     },
8900
8901     // private
8902     onFocus : function(){
8903         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8904         /*
8905         if(!this.mimicing){
8906             this.wrap.addClass('x-trigger-wrap-focus');
8907             this.mimicing = true;
8908             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8909             if(this.monitorTab){
8910                 this.el.on("keydown", this.checkTab, this);
8911             }
8912         }
8913         */
8914     },
8915
8916     // private
8917     checkTab : function(e){
8918         if(e.getKey() == e.TAB){
8919             this.triggerBlur();
8920         }
8921     },
8922
8923     // private
8924     onBlur : function(){
8925         // do nothing
8926     },
8927
8928     // private
8929     mimicBlur : function(e, t){
8930         /*
8931         if(!this.wrap.contains(t) && this.validateBlur()){
8932             this.triggerBlur();
8933         }
8934         */
8935     },
8936
8937     // private
8938     triggerBlur : function(){
8939         this.mimicing = false;
8940         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8941         if(this.monitorTab){
8942             this.el.un("keydown", this.checkTab, this);
8943         }
8944         //this.wrap.removeClass('x-trigger-wrap-focus');
8945         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8946     },
8947
8948     // private
8949     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8950     validateBlur : function(e, t){
8951         return true;
8952     },
8953
8954     // private
8955     onDisable : function(){
8956         this.inputEl().dom.disabled = true;
8957         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8958         //if(this.wrap){
8959         //    this.wrap.addClass('x-item-disabled');
8960         //}
8961     },
8962
8963     // private
8964     onEnable : function(){
8965         this.inputEl().dom.disabled = false;
8966         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8967         //if(this.wrap){
8968         //    this.el.removeClass('x-item-disabled');
8969         //}
8970     },
8971
8972     // private
8973     onShow : function(){
8974         var ae = this.getActionEl();
8975         
8976         if(ae){
8977             ae.dom.style.display = '';
8978             ae.dom.style.visibility = 'visible';
8979         }
8980     },
8981
8982     // private
8983     
8984     onHide : function(){
8985         var ae = this.getActionEl();
8986         ae.dom.style.display = 'none';
8987     },
8988
8989     /**
8990      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8991      * by an implementing function.
8992      * @method
8993      * @param {EventObject} e
8994      */
8995     onTriggerClick : Roo.emptyFn
8996 });
8997  /*
8998  * Based on:
8999  * Ext JS Library 1.1.1
9000  * Copyright(c) 2006-2007, Ext JS, LLC.
9001  *
9002  * Originally Released Under LGPL - original licence link has changed is not relivant.
9003  *
9004  * Fork - LGPL
9005  * <script type="text/javascript">
9006  */
9007
9008
9009 /**
9010  * @class Roo.data.SortTypes
9011  * @singleton
9012  * Defines the default sorting (casting?) comparison functions used when sorting data.
9013  */
9014 Roo.data.SortTypes = {
9015     /**
9016      * Default sort that does nothing
9017      * @param {Mixed} s The value being converted
9018      * @return {Mixed} The comparison value
9019      */
9020     none : function(s){
9021         return s;
9022     },
9023     
9024     /**
9025      * The regular expression used to strip tags
9026      * @type {RegExp}
9027      * @property
9028      */
9029     stripTagsRE : /<\/?[^>]+>/gi,
9030     
9031     /**
9032      * Strips all HTML tags to sort on text only
9033      * @param {Mixed} s The value being converted
9034      * @return {String} The comparison value
9035      */
9036     asText : function(s){
9037         return String(s).replace(this.stripTagsRE, "");
9038     },
9039     
9040     /**
9041      * Strips all HTML tags to sort on text only - Case insensitive
9042      * @param {Mixed} s The value being converted
9043      * @return {String} The comparison value
9044      */
9045     asUCText : function(s){
9046         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9047     },
9048     
9049     /**
9050      * Case insensitive string
9051      * @param {Mixed} s The value being converted
9052      * @return {String} The comparison value
9053      */
9054     asUCString : function(s) {
9055         return String(s).toUpperCase();
9056     },
9057     
9058     /**
9059      * Date sorting
9060      * @param {Mixed} s The value being converted
9061      * @return {Number} The comparison value
9062      */
9063     asDate : function(s) {
9064         if(!s){
9065             return 0;
9066         }
9067         if(s instanceof Date){
9068             return s.getTime();
9069         }
9070         return Date.parse(String(s));
9071     },
9072     
9073     /**
9074      * Float sorting
9075      * @param {Mixed} s The value being converted
9076      * @return {Float} The comparison value
9077      */
9078     asFloat : function(s) {
9079         var val = parseFloat(String(s).replace(/,/g, ""));
9080         if(isNaN(val)) val = 0;
9081         return val;
9082     },
9083     
9084     /**
9085      * Integer sorting
9086      * @param {Mixed} s The value being converted
9087      * @return {Number} The comparison value
9088      */
9089     asInt : function(s) {
9090         var val = parseInt(String(s).replace(/,/g, ""));
9091         if(isNaN(val)) val = 0;
9092         return val;
9093     }
9094 };/*
9095  * Based on:
9096  * Ext JS Library 1.1.1
9097  * Copyright(c) 2006-2007, Ext JS, LLC.
9098  *
9099  * Originally Released Under LGPL - original licence link has changed is not relivant.
9100  *
9101  * Fork - LGPL
9102  * <script type="text/javascript">
9103  */
9104
9105 /**
9106 * @class Roo.data.Record
9107  * Instances of this class encapsulate both record <em>definition</em> information, and record
9108  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9109  * to access Records cached in an {@link Roo.data.Store} object.<br>
9110  * <p>
9111  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9112  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9113  * objects.<br>
9114  * <p>
9115  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9116  * @constructor
9117  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9118  * {@link #create}. The parameters are the same.
9119  * @param {Array} data An associative Array of data values keyed by the field name.
9120  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9121  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9122  * not specified an integer id is generated.
9123  */
9124 Roo.data.Record = function(data, id){
9125     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9126     this.data = data;
9127 };
9128
9129 /**
9130  * Generate a constructor for a specific record layout.
9131  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9132  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9133  * Each field definition object may contain the following properties: <ul>
9134  * <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,
9135  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9136  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9137  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9138  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9139  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9140  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9141  * this may be omitted.</p></li>
9142  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9143  * <ul><li>auto (Default, implies no conversion)</li>
9144  * <li>string</li>
9145  * <li>int</li>
9146  * <li>float</li>
9147  * <li>boolean</li>
9148  * <li>date</li></ul></p></li>
9149  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9150  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9151  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9152  * by the Reader into an object that will be stored in the Record. It is passed the
9153  * following parameters:<ul>
9154  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9155  * </ul></p></li>
9156  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9157  * </ul>
9158  * <br>usage:<br><pre><code>
9159 var TopicRecord = Roo.data.Record.create(
9160     {name: 'title', mapping: 'topic_title'},
9161     {name: 'author', mapping: 'username'},
9162     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9163     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9164     {name: 'lastPoster', mapping: 'user2'},
9165     {name: 'excerpt', mapping: 'post_text'}
9166 );
9167
9168 var myNewRecord = new TopicRecord({
9169     title: 'Do my job please',
9170     author: 'noobie',
9171     totalPosts: 1,
9172     lastPost: new Date(),
9173     lastPoster: 'Animal',
9174     excerpt: 'No way dude!'
9175 });
9176 myStore.add(myNewRecord);
9177 </code></pre>
9178  * @method create
9179  * @static
9180  */
9181 Roo.data.Record.create = function(o){
9182     var f = function(){
9183         f.superclass.constructor.apply(this, arguments);
9184     };
9185     Roo.extend(f, Roo.data.Record);
9186     var p = f.prototype;
9187     p.fields = new Roo.util.MixedCollection(false, function(field){
9188         return field.name;
9189     });
9190     for(var i = 0, len = o.length; i < len; i++){
9191         p.fields.add(new Roo.data.Field(o[i]));
9192     }
9193     f.getField = function(name){
9194         return p.fields.get(name);  
9195     };
9196     return f;
9197 };
9198
9199 Roo.data.Record.AUTO_ID = 1000;
9200 Roo.data.Record.EDIT = 'edit';
9201 Roo.data.Record.REJECT = 'reject';
9202 Roo.data.Record.COMMIT = 'commit';
9203
9204 Roo.data.Record.prototype = {
9205     /**
9206      * Readonly flag - true if this record has been modified.
9207      * @type Boolean
9208      */
9209     dirty : false,
9210     editing : false,
9211     error: null,
9212     modified: null,
9213
9214     // private
9215     join : function(store){
9216         this.store = store;
9217     },
9218
9219     /**
9220      * Set the named field to the specified value.
9221      * @param {String} name The name of the field to set.
9222      * @param {Object} value The value to set the field to.
9223      */
9224     set : function(name, value){
9225         if(this.data[name] == value){
9226             return;
9227         }
9228         this.dirty = true;
9229         if(!this.modified){
9230             this.modified = {};
9231         }
9232         if(typeof this.modified[name] == 'undefined'){
9233             this.modified[name] = this.data[name];
9234         }
9235         this.data[name] = value;
9236         if(!this.editing && this.store){
9237             this.store.afterEdit(this);
9238         }       
9239     },
9240
9241     /**
9242      * Get the value of the named field.
9243      * @param {String} name The name of the field to get the value of.
9244      * @return {Object} The value of the field.
9245      */
9246     get : function(name){
9247         return this.data[name]; 
9248     },
9249
9250     // private
9251     beginEdit : function(){
9252         this.editing = true;
9253         this.modified = {}; 
9254     },
9255
9256     // private
9257     cancelEdit : function(){
9258         this.editing = false;
9259         delete this.modified;
9260     },
9261
9262     // private
9263     endEdit : function(){
9264         this.editing = false;
9265         if(this.dirty && this.store){
9266             this.store.afterEdit(this);
9267         }
9268     },
9269
9270     /**
9271      * Usually called by the {@link Roo.data.Store} which owns the Record.
9272      * Rejects all changes made to the Record since either creation, or the last commit operation.
9273      * Modified fields are reverted to their original values.
9274      * <p>
9275      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9276      * of reject operations.
9277      */
9278     reject : function(){
9279         var m = this.modified;
9280         for(var n in m){
9281             if(typeof m[n] != "function"){
9282                 this.data[n] = m[n];
9283             }
9284         }
9285         this.dirty = false;
9286         delete this.modified;
9287         this.editing = false;
9288         if(this.store){
9289             this.store.afterReject(this);
9290         }
9291     },
9292
9293     /**
9294      * Usually called by the {@link Roo.data.Store} which owns the Record.
9295      * Commits all changes made to the Record since either creation, or the last commit operation.
9296      * <p>
9297      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9298      * of commit operations.
9299      */
9300     commit : function(){
9301         this.dirty = false;
9302         delete this.modified;
9303         this.editing = false;
9304         if(this.store){
9305             this.store.afterCommit(this);
9306         }
9307     },
9308
9309     // private
9310     hasError : function(){
9311         return this.error != null;
9312     },
9313
9314     // private
9315     clearError : function(){
9316         this.error = null;
9317     },
9318
9319     /**
9320      * Creates a copy of this record.
9321      * @param {String} id (optional) A new record id if you don't want to use this record's id
9322      * @return {Record}
9323      */
9324     copy : function(newId) {
9325         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9326     }
9327 };/*
9328  * Based on:
9329  * Ext JS Library 1.1.1
9330  * Copyright(c) 2006-2007, Ext JS, LLC.
9331  *
9332  * Originally Released Under LGPL - original licence link has changed is not relivant.
9333  *
9334  * Fork - LGPL
9335  * <script type="text/javascript">
9336  */
9337
9338
9339
9340 /**
9341  * @class Roo.data.Store
9342  * @extends Roo.util.Observable
9343  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9344  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9345  * <p>
9346  * 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
9347  * has no knowledge of the format of the data returned by the Proxy.<br>
9348  * <p>
9349  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9350  * instances from the data object. These records are cached and made available through accessor functions.
9351  * @constructor
9352  * Creates a new Store.
9353  * @param {Object} config A config object containing the objects needed for the Store to access data,
9354  * and read the data into Records.
9355  */
9356 Roo.data.Store = function(config){
9357     this.data = new Roo.util.MixedCollection(false);
9358     this.data.getKey = function(o){
9359         return o.id;
9360     };
9361     this.baseParams = {};
9362     // private
9363     this.paramNames = {
9364         "start" : "start",
9365         "limit" : "limit",
9366         "sort" : "sort",
9367         "dir" : "dir",
9368         "multisort" : "_multisort"
9369     };
9370
9371     if(config && config.data){
9372         this.inlineData = config.data;
9373         delete config.data;
9374     }
9375
9376     Roo.apply(this, config);
9377     
9378     if(this.reader){ // reader passed
9379         this.reader = Roo.factory(this.reader, Roo.data);
9380         this.reader.xmodule = this.xmodule || false;
9381         if(!this.recordType){
9382             this.recordType = this.reader.recordType;
9383         }
9384         if(this.reader.onMetaChange){
9385             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9386         }
9387     }
9388
9389     if(this.recordType){
9390         this.fields = this.recordType.prototype.fields;
9391     }
9392     this.modified = [];
9393
9394     this.addEvents({
9395         /**
9396          * @event datachanged
9397          * Fires when the data cache has changed, and a widget which is using this Store
9398          * as a Record cache should refresh its view.
9399          * @param {Store} this
9400          */
9401         datachanged : true,
9402         /**
9403          * @event metachange
9404          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9405          * @param {Store} this
9406          * @param {Object} meta The JSON metadata
9407          */
9408         metachange : true,
9409         /**
9410          * @event add
9411          * Fires when Records have been added to the Store
9412          * @param {Store} this
9413          * @param {Roo.data.Record[]} records The array of Records added
9414          * @param {Number} index The index at which the record(s) were added
9415          */
9416         add : true,
9417         /**
9418          * @event remove
9419          * Fires when a Record has been removed from the Store
9420          * @param {Store} this
9421          * @param {Roo.data.Record} record The Record that was removed
9422          * @param {Number} index The index at which the record was removed
9423          */
9424         remove : true,
9425         /**
9426          * @event update
9427          * Fires when a Record has been updated
9428          * @param {Store} this
9429          * @param {Roo.data.Record} record The Record that was updated
9430          * @param {String} operation The update operation being performed.  Value may be one of:
9431          * <pre><code>
9432  Roo.data.Record.EDIT
9433  Roo.data.Record.REJECT
9434  Roo.data.Record.COMMIT
9435          * </code></pre>
9436          */
9437         update : true,
9438         /**
9439          * @event clear
9440          * Fires when the data cache has been cleared.
9441          * @param {Store} this
9442          */
9443         clear : true,
9444         /**
9445          * @event beforeload
9446          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9447          * the load action will be canceled.
9448          * @param {Store} this
9449          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9450          */
9451         beforeload : true,
9452         /**
9453          * @event beforeloadadd
9454          * Fires after a new set of Records has been loaded.
9455          * @param {Store} this
9456          * @param {Roo.data.Record[]} records The Records that were loaded
9457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9458          */
9459         beforeloadadd : true,
9460         /**
9461          * @event load
9462          * Fires after a new set of Records has been loaded, before they are added to the store.
9463          * @param {Store} this
9464          * @param {Roo.data.Record[]} records The Records that were loaded
9465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9466          * @params {Object} return from reader
9467          */
9468         load : true,
9469         /**
9470          * @event loadexception
9471          * Fires if an exception occurs in the Proxy during loading.
9472          * Called with the signature of the Proxy's "loadexception" event.
9473          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9474          * 
9475          * @param {Proxy} 
9476          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9477          * @param {Object} load options 
9478          * @param {Object} jsonData from your request (normally this contains the Exception)
9479          */
9480         loadexception : true
9481     });
9482     
9483     if(this.proxy){
9484         this.proxy = Roo.factory(this.proxy, Roo.data);
9485         this.proxy.xmodule = this.xmodule || false;
9486         this.relayEvents(this.proxy,  ["loadexception"]);
9487     }
9488     this.sortToggle = {};
9489     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9490
9491     Roo.data.Store.superclass.constructor.call(this);
9492
9493     if(this.inlineData){
9494         this.loadData(this.inlineData);
9495         delete this.inlineData;
9496     }
9497 };
9498
9499 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9500      /**
9501     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9502     * without a remote query - used by combo/forms at present.
9503     */
9504     
9505     /**
9506     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9507     */
9508     /**
9509     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9510     */
9511     /**
9512     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9513     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9514     */
9515     /**
9516     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9517     * on any HTTP request
9518     */
9519     /**
9520     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9521     */
9522     /**
9523     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9524     */
9525     multiSort: false,
9526     /**
9527     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9528     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9529     */
9530     remoteSort : false,
9531
9532     /**
9533     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9534      * loaded or when a record is removed. (defaults to false).
9535     */
9536     pruneModifiedRecords : false,
9537
9538     // private
9539     lastOptions : null,
9540
9541     /**
9542      * Add Records to the Store and fires the add event.
9543      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9544      */
9545     add : function(records){
9546         records = [].concat(records);
9547         for(var i = 0, len = records.length; i < len; i++){
9548             records[i].join(this);
9549         }
9550         var index = this.data.length;
9551         this.data.addAll(records);
9552         this.fireEvent("add", this, records, index);
9553     },
9554
9555     /**
9556      * Remove a Record from the Store and fires the remove event.
9557      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9558      */
9559     remove : function(record){
9560         var index = this.data.indexOf(record);
9561         this.data.removeAt(index);
9562         if(this.pruneModifiedRecords){
9563             this.modified.remove(record);
9564         }
9565         this.fireEvent("remove", this, record, index);
9566     },
9567
9568     /**
9569      * Remove all Records from the Store and fires the clear event.
9570      */
9571     removeAll : function(){
9572         this.data.clear();
9573         if(this.pruneModifiedRecords){
9574             this.modified = [];
9575         }
9576         this.fireEvent("clear", this);
9577     },
9578
9579     /**
9580      * Inserts Records to the Store at the given index and fires the add event.
9581      * @param {Number} index The start index at which to insert the passed Records.
9582      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9583      */
9584     insert : function(index, records){
9585         records = [].concat(records);
9586         for(var i = 0, len = records.length; i < len; i++){
9587             this.data.insert(index, records[i]);
9588             records[i].join(this);
9589         }
9590         this.fireEvent("add", this, records, index);
9591     },
9592
9593     /**
9594      * Get the index within the cache of the passed Record.
9595      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9596      * @return {Number} The index of the passed Record. Returns -1 if not found.
9597      */
9598     indexOf : function(record){
9599         return this.data.indexOf(record);
9600     },
9601
9602     /**
9603      * Get the index within the cache of the Record with the passed id.
9604      * @param {String} id The id of the Record to find.
9605      * @return {Number} The index of the Record. Returns -1 if not found.
9606      */
9607     indexOfId : function(id){
9608         return this.data.indexOfKey(id);
9609     },
9610
9611     /**
9612      * Get the Record with the specified id.
9613      * @param {String} id The id of the Record to find.
9614      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9615      */
9616     getById : function(id){
9617         return this.data.key(id);
9618     },
9619
9620     /**
9621      * Get the Record at the specified index.
9622      * @param {Number} index The index of the Record to find.
9623      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9624      */
9625     getAt : function(index){
9626         return this.data.itemAt(index);
9627     },
9628
9629     /**
9630      * Returns a range of Records between specified indices.
9631      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9632      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9633      * @return {Roo.data.Record[]} An array of Records
9634      */
9635     getRange : function(start, end){
9636         return this.data.getRange(start, end);
9637     },
9638
9639     // private
9640     storeOptions : function(o){
9641         o = Roo.apply({}, o);
9642         delete o.callback;
9643         delete o.scope;
9644         this.lastOptions = o;
9645     },
9646
9647     /**
9648      * Loads the Record cache from the configured Proxy using the configured Reader.
9649      * <p>
9650      * If using remote paging, then the first load call must specify the <em>start</em>
9651      * and <em>limit</em> properties in the options.params property to establish the initial
9652      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9653      * <p>
9654      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9655      * and this call will return before the new data has been loaded. Perform any post-processing
9656      * in a callback function, or in a "load" event handler.</strong>
9657      * <p>
9658      * @param {Object} options An object containing properties which control loading options:<ul>
9659      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9660      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9661      * passed the following arguments:<ul>
9662      * <li>r : Roo.data.Record[]</li>
9663      * <li>options: Options object from the load call</li>
9664      * <li>success: Boolean success indicator</li></ul></li>
9665      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9666      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9667      * </ul>
9668      */
9669     load : function(options){
9670         options = options || {};
9671         if(this.fireEvent("beforeload", this, options) !== false){
9672             this.storeOptions(options);
9673             var p = Roo.apply(options.params || {}, this.baseParams);
9674             // if meta was not loaded from remote source.. try requesting it.
9675             if (!this.reader.metaFromRemote) {
9676                 p._requestMeta = 1;
9677             }
9678             if(this.sortInfo && this.remoteSort){
9679                 var pn = this.paramNames;
9680                 p[pn["sort"]] = this.sortInfo.field;
9681                 p[pn["dir"]] = this.sortInfo.direction;
9682             }
9683             if (this.multiSort) {
9684                 var pn = this.paramNames;
9685                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9686             }
9687             
9688             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9689         }
9690     },
9691
9692     /**
9693      * Reloads the Record cache from the configured Proxy using the configured Reader and
9694      * the options from the last load operation performed.
9695      * @param {Object} options (optional) An object containing properties which may override the options
9696      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9697      * the most recently used options are reused).
9698      */
9699     reload : function(options){
9700         this.load(Roo.applyIf(options||{}, this.lastOptions));
9701     },
9702
9703     // private
9704     // Called as a callback by the Reader during a load operation.
9705     loadRecords : function(o, options, success){
9706         if(!o || success === false){
9707             if(success !== false){
9708                 this.fireEvent("load", this, [], options, o);
9709             }
9710             if(options.callback){
9711                 options.callback.call(options.scope || this, [], options, false);
9712             }
9713             return;
9714         }
9715         // if data returned failure - throw an exception.
9716         if (o.success === false) {
9717             // show a message if no listener is registered.
9718             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9719                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9720             }
9721             // loadmask wil be hooked into this..
9722             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9723             return;
9724         }
9725         var r = o.records, t = o.totalRecords || r.length;
9726         
9727         this.fireEvent("beforeloadadd", this, r, options, o);
9728         
9729         if(!options || options.add !== true){
9730             if(this.pruneModifiedRecords){
9731                 this.modified = [];
9732             }
9733             for(var i = 0, len = r.length; i < len; i++){
9734                 r[i].join(this);
9735             }
9736             if(this.snapshot){
9737                 this.data = this.snapshot;
9738                 delete this.snapshot;
9739             }
9740             this.data.clear();
9741             this.data.addAll(r);
9742             this.totalLength = t;
9743             this.applySort();
9744             this.fireEvent("datachanged", this);
9745         }else{
9746             this.totalLength = Math.max(t, this.data.length+r.length);
9747             this.add(r);
9748         }
9749         this.fireEvent("load", this, r, options, o);
9750         if(options.callback){
9751             options.callback.call(options.scope || this, r, options, true);
9752         }
9753     },
9754
9755
9756     /**
9757      * Loads data from a passed data block. A Reader which understands the format of the data
9758      * must have been configured in the constructor.
9759      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9760      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9761      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9762      */
9763     loadData : function(o, append){
9764         var r = this.reader.readRecords(o);
9765         this.loadRecords(r, {add: append}, true);
9766     },
9767
9768     /**
9769      * Gets the number of cached records.
9770      * <p>
9771      * <em>If using paging, this may not be the total size of the dataset. If the data object
9772      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9773      * the data set size</em>
9774      */
9775     getCount : function(){
9776         return this.data.length || 0;
9777     },
9778
9779     /**
9780      * Gets the total number of records in the dataset as returned by the server.
9781      * <p>
9782      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9783      * the dataset size</em>
9784      */
9785     getTotalCount : function(){
9786         return this.totalLength || 0;
9787     },
9788
9789     /**
9790      * Returns the sort state of the Store as an object with two properties:
9791      * <pre><code>
9792  field {String} The name of the field by which the Records are sorted
9793  direction {String} The sort order, "ASC" or "DESC"
9794      * </code></pre>
9795      */
9796     getSortState : function(){
9797         return this.sortInfo;
9798     },
9799
9800     // private
9801     applySort : function(){
9802         if(this.sortInfo && !this.remoteSort){
9803             var s = this.sortInfo, f = s.field;
9804             var st = this.fields.get(f).sortType;
9805             var fn = function(r1, r2){
9806                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9807                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9808             };
9809             this.data.sort(s.direction, fn);
9810             if(this.snapshot && this.snapshot != this.data){
9811                 this.snapshot.sort(s.direction, fn);
9812             }
9813         }
9814     },
9815
9816     /**
9817      * Sets the default sort column and order to be used by the next load operation.
9818      * @param {String} fieldName The name of the field to sort by.
9819      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9820      */
9821     setDefaultSort : function(field, dir){
9822         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9823     },
9824
9825     /**
9826      * Sort the Records.
9827      * If remote sorting is used, the sort is performed on the server, and the cache is
9828      * reloaded. If local sorting is used, the cache is sorted internally.
9829      * @param {String} fieldName The name of the field to sort by.
9830      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9831      */
9832     sort : function(fieldName, dir){
9833         var f = this.fields.get(fieldName);
9834         if(!dir){
9835             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9836             
9837             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9838                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9839             }else{
9840                 dir = f.sortDir;
9841             }
9842         }
9843         this.sortToggle[f.name] = dir;
9844         this.sortInfo = {field: f.name, direction: dir};
9845         if(!this.remoteSort){
9846             this.applySort();
9847             this.fireEvent("datachanged", this);
9848         }else{
9849             this.load(this.lastOptions);
9850         }
9851     },
9852
9853     /**
9854      * Calls the specified function for each of the Records in the cache.
9855      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9856      * Returning <em>false</em> aborts and exits the iteration.
9857      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9858      */
9859     each : function(fn, scope){
9860         this.data.each(fn, scope);
9861     },
9862
9863     /**
9864      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9865      * (e.g., during paging).
9866      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9867      */
9868     getModifiedRecords : function(){
9869         return this.modified;
9870     },
9871
9872     // private
9873     createFilterFn : function(property, value, anyMatch){
9874         if(!value.exec){ // not a regex
9875             value = String(value);
9876             if(value.length == 0){
9877                 return false;
9878             }
9879             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9880         }
9881         return function(r){
9882             return value.test(r.data[property]);
9883         };
9884     },
9885
9886     /**
9887      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9888      * @param {String} property A field on your records
9889      * @param {Number} start The record index to start at (defaults to 0)
9890      * @param {Number} end The last record index to include (defaults to length - 1)
9891      * @return {Number} The sum
9892      */
9893     sum : function(property, start, end){
9894         var rs = this.data.items, v = 0;
9895         start = start || 0;
9896         end = (end || end === 0) ? end : rs.length-1;
9897
9898         for(var i = start; i <= end; i++){
9899             v += (rs[i].data[property] || 0);
9900         }
9901         return v;
9902     },
9903
9904     /**
9905      * Filter the records by a specified property.
9906      * @param {String} field A field on your records
9907      * @param {String/RegExp} value Either a string that the field
9908      * should start with or a RegExp to test against the field
9909      * @param {Boolean} anyMatch True to match any part not just the beginning
9910      */
9911     filter : function(property, value, anyMatch){
9912         var fn = this.createFilterFn(property, value, anyMatch);
9913         return fn ? this.filterBy(fn) : this.clearFilter();
9914     },
9915
9916     /**
9917      * Filter by a function. The specified function will be called with each
9918      * record in this data source. If the function returns true the record is included,
9919      * otherwise it is filtered.
9920      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9921      * @param {Object} scope (optional) The scope of the function (defaults to this)
9922      */
9923     filterBy : function(fn, scope){
9924         this.snapshot = this.snapshot || this.data;
9925         this.data = this.queryBy(fn, scope||this);
9926         this.fireEvent("datachanged", this);
9927     },
9928
9929     /**
9930      * Query the records by a specified property.
9931      * @param {String} field A field on your records
9932      * @param {String/RegExp} value Either a string that the field
9933      * should start with or a RegExp to test against the field
9934      * @param {Boolean} anyMatch True to match any part not just the beginning
9935      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9936      */
9937     query : function(property, value, anyMatch){
9938         var fn = this.createFilterFn(property, value, anyMatch);
9939         return fn ? this.queryBy(fn) : this.data.clone();
9940     },
9941
9942     /**
9943      * Query by a function. The specified function will be called with each
9944      * record in this data source. If the function returns true the record is included
9945      * in the results.
9946      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9947      * @param {Object} scope (optional) The scope of the function (defaults to this)
9948       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9949      **/
9950     queryBy : function(fn, scope){
9951         var data = this.snapshot || this.data;
9952         return data.filterBy(fn, scope||this);
9953     },
9954
9955     /**
9956      * Collects unique values for a particular dataIndex from this store.
9957      * @param {String} dataIndex The property to collect
9958      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9959      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9960      * @return {Array} An array of the unique values
9961      **/
9962     collect : function(dataIndex, allowNull, bypassFilter){
9963         var d = (bypassFilter === true && this.snapshot) ?
9964                 this.snapshot.items : this.data.items;
9965         var v, sv, r = [], l = {};
9966         for(var i = 0, len = d.length; i < len; i++){
9967             v = d[i].data[dataIndex];
9968             sv = String(v);
9969             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9970                 l[sv] = true;
9971                 r[r.length] = v;
9972             }
9973         }
9974         return r;
9975     },
9976
9977     /**
9978      * Revert to a view of the Record cache with no filtering applied.
9979      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9980      */
9981     clearFilter : function(suppressEvent){
9982         if(this.snapshot && this.snapshot != this.data){
9983             this.data = this.snapshot;
9984             delete this.snapshot;
9985             if(suppressEvent !== true){
9986                 this.fireEvent("datachanged", this);
9987             }
9988         }
9989     },
9990
9991     // private
9992     afterEdit : function(record){
9993         if(this.modified.indexOf(record) == -1){
9994             this.modified.push(record);
9995         }
9996         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9997     },
9998     
9999     // private
10000     afterReject : function(record){
10001         this.modified.remove(record);
10002         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10003     },
10004
10005     // private
10006     afterCommit : function(record){
10007         this.modified.remove(record);
10008         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10009     },
10010
10011     /**
10012      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10013      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10014      */
10015     commitChanges : function(){
10016         var m = this.modified.slice(0);
10017         this.modified = [];
10018         for(var i = 0, len = m.length; i < len; i++){
10019             m[i].commit();
10020         }
10021     },
10022
10023     /**
10024      * Cancel outstanding changes on all changed records.
10025      */
10026     rejectChanges : function(){
10027         var m = this.modified.slice(0);
10028         this.modified = [];
10029         for(var i = 0, len = m.length; i < len; i++){
10030             m[i].reject();
10031         }
10032     },
10033
10034     onMetaChange : function(meta, rtype, o){
10035         this.recordType = rtype;
10036         this.fields = rtype.prototype.fields;
10037         delete this.snapshot;
10038         this.sortInfo = meta.sortInfo || this.sortInfo;
10039         this.modified = [];
10040         this.fireEvent('metachange', this, this.reader.meta);
10041     },
10042     
10043     moveIndex : function(data, type)
10044     {
10045         var index = this.indexOf(data);
10046         
10047         var newIndex = index + type;
10048         
10049         this.remove(data);
10050         
10051         this.insert(newIndex, data);
10052         
10053     }
10054 });/*
10055  * Based on:
10056  * Ext JS Library 1.1.1
10057  * Copyright(c) 2006-2007, Ext JS, LLC.
10058  *
10059  * Originally Released Under LGPL - original licence link has changed is not relivant.
10060  *
10061  * Fork - LGPL
10062  * <script type="text/javascript">
10063  */
10064
10065 /**
10066  * @class Roo.data.SimpleStore
10067  * @extends Roo.data.Store
10068  * Small helper class to make creating Stores from Array data easier.
10069  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10070  * @cfg {Array} fields An array of field definition objects, or field name strings.
10071  * @cfg {Array} data The multi-dimensional array of data
10072  * @constructor
10073  * @param {Object} config
10074  */
10075 Roo.data.SimpleStore = function(config){
10076     Roo.data.SimpleStore.superclass.constructor.call(this, {
10077         isLocal : true,
10078         reader: new Roo.data.ArrayReader({
10079                 id: config.id
10080             },
10081             Roo.data.Record.create(config.fields)
10082         ),
10083         proxy : new Roo.data.MemoryProxy(config.data)
10084     });
10085     this.load();
10086 };
10087 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10088  * Based on:
10089  * Ext JS Library 1.1.1
10090  * Copyright(c) 2006-2007, Ext JS, LLC.
10091  *
10092  * Originally Released Under LGPL - original licence link has changed is not relivant.
10093  *
10094  * Fork - LGPL
10095  * <script type="text/javascript">
10096  */
10097
10098 /**
10099 /**
10100  * @extends Roo.data.Store
10101  * @class Roo.data.JsonStore
10102  * Small helper class to make creating Stores for JSON data easier. <br/>
10103 <pre><code>
10104 var store = new Roo.data.JsonStore({
10105     url: 'get-images.php',
10106     root: 'images',
10107     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10108 });
10109 </code></pre>
10110  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10111  * JsonReader and HttpProxy (unless inline data is provided).</b>
10112  * @cfg {Array} fields An array of field definition objects, or field name strings.
10113  * @constructor
10114  * @param {Object} config
10115  */
10116 Roo.data.JsonStore = function(c){
10117     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10118         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10119         reader: new Roo.data.JsonReader(c, c.fields)
10120     }));
10121 };
10122 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10123  * Based on:
10124  * Ext JS Library 1.1.1
10125  * Copyright(c) 2006-2007, Ext JS, LLC.
10126  *
10127  * Originally Released Under LGPL - original licence link has changed is not relivant.
10128  *
10129  * Fork - LGPL
10130  * <script type="text/javascript">
10131  */
10132
10133  
10134 Roo.data.Field = function(config){
10135     if(typeof config == "string"){
10136         config = {name: config};
10137     }
10138     Roo.apply(this, config);
10139     
10140     if(!this.type){
10141         this.type = "auto";
10142     }
10143     
10144     var st = Roo.data.SortTypes;
10145     // named sortTypes are supported, here we look them up
10146     if(typeof this.sortType == "string"){
10147         this.sortType = st[this.sortType];
10148     }
10149     
10150     // set default sortType for strings and dates
10151     if(!this.sortType){
10152         switch(this.type){
10153             case "string":
10154                 this.sortType = st.asUCString;
10155                 break;
10156             case "date":
10157                 this.sortType = st.asDate;
10158                 break;
10159             default:
10160                 this.sortType = st.none;
10161         }
10162     }
10163
10164     // define once
10165     var stripRe = /[\$,%]/g;
10166
10167     // prebuilt conversion function for this field, instead of
10168     // switching every time we're reading a value
10169     if(!this.convert){
10170         var cv, dateFormat = this.dateFormat;
10171         switch(this.type){
10172             case "":
10173             case "auto":
10174             case undefined:
10175                 cv = function(v){ return v; };
10176                 break;
10177             case "string":
10178                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10179                 break;
10180             case "int":
10181                 cv = function(v){
10182                     return v !== undefined && v !== null && v !== '' ?
10183                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10184                     };
10185                 break;
10186             case "float":
10187                 cv = function(v){
10188                     return v !== undefined && v !== null && v !== '' ?
10189                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10190                     };
10191                 break;
10192             case "bool":
10193             case "boolean":
10194                 cv = function(v){ return v === true || v === "true" || v == 1; };
10195                 break;
10196             case "date":
10197                 cv = function(v){
10198                     if(!v){
10199                         return '';
10200                     }
10201                     if(v instanceof Date){
10202                         return v;
10203                     }
10204                     if(dateFormat){
10205                         if(dateFormat == "timestamp"){
10206                             return new Date(v*1000);
10207                         }
10208                         return Date.parseDate(v, dateFormat);
10209                     }
10210                     var parsed = Date.parse(v);
10211                     return parsed ? new Date(parsed) : null;
10212                 };
10213              break;
10214             
10215         }
10216         this.convert = cv;
10217     }
10218 };
10219
10220 Roo.data.Field.prototype = {
10221     dateFormat: null,
10222     defaultValue: "",
10223     mapping: null,
10224     sortType : null,
10225     sortDir : "ASC"
10226 };/*
10227  * Based on:
10228  * Ext JS Library 1.1.1
10229  * Copyright(c) 2006-2007, Ext JS, LLC.
10230  *
10231  * Originally Released Under LGPL - original licence link has changed is not relivant.
10232  *
10233  * Fork - LGPL
10234  * <script type="text/javascript">
10235  */
10236  
10237 // Base class for reading structured data from a data source.  This class is intended to be
10238 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10239
10240 /**
10241  * @class Roo.data.DataReader
10242  * Base class for reading structured data from a data source.  This class is intended to be
10243  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10244  */
10245
10246 Roo.data.DataReader = function(meta, recordType){
10247     
10248     this.meta = meta;
10249     
10250     this.recordType = recordType instanceof Array ? 
10251         Roo.data.Record.create(recordType) : recordType;
10252 };
10253
10254 Roo.data.DataReader.prototype = {
10255      /**
10256      * Create an empty record
10257      * @param {Object} data (optional) - overlay some values
10258      * @return {Roo.data.Record} record created.
10259      */
10260     newRow :  function(d) {
10261         var da =  {};
10262         this.recordType.prototype.fields.each(function(c) {
10263             switch( c.type) {
10264                 case 'int' : da[c.name] = 0; break;
10265                 case 'date' : da[c.name] = new Date(); break;
10266                 case 'float' : da[c.name] = 0.0; break;
10267                 case 'boolean' : da[c.name] = false; break;
10268                 default : da[c.name] = ""; break;
10269             }
10270             
10271         });
10272         return new this.recordType(Roo.apply(da, d));
10273     }
10274     
10275 };/*
10276  * Based on:
10277  * Ext JS Library 1.1.1
10278  * Copyright(c) 2006-2007, Ext JS, LLC.
10279  *
10280  * Originally Released Under LGPL - original licence link has changed is not relivant.
10281  *
10282  * Fork - LGPL
10283  * <script type="text/javascript">
10284  */
10285
10286 /**
10287  * @class Roo.data.DataProxy
10288  * @extends Roo.data.Observable
10289  * This class is an abstract base class for implementations which provide retrieval of
10290  * unformatted data objects.<br>
10291  * <p>
10292  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10293  * (of the appropriate type which knows how to parse the data object) to provide a block of
10294  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10295  * <p>
10296  * Custom implementations must implement the load method as described in
10297  * {@link Roo.data.HttpProxy#load}.
10298  */
10299 Roo.data.DataProxy = function(){
10300     this.addEvents({
10301         /**
10302          * @event beforeload
10303          * Fires before a network request is made to retrieve a data object.
10304          * @param {Object} This DataProxy object.
10305          * @param {Object} params The params parameter to the load function.
10306          */
10307         beforeload : true,
10308         /**
10309          * @event load
10310          * Fires before the load method's callback is called.
10311          * @param {Object} This DataProxy object.
10312          * @param {Object} o The data object.
10313          * @param {Object} arg The callback argument object passed to the load function.
10314          */
10315         load : true,
10316         /**
10317          * @event loadexception
10318          * Fires if an Exception occurs during data retrieval.
10319          * @param {Object} This DataProxy object.
10320          * @param {Object} o The data object.
10321          * @param {Object} arg The callback argument object passed to the load function.
10322          * @param {Object} e The Exception.
10323          */
10324         loadexception : true
10325     });
10326     Roo.data.DataProxy.superclass.constructor.call(this);
10327 };
10328
10329 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10330
10331     /**
10332      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10333      */
10334 /*
10335  * Based on:
10336  * Ext JS Library 1.1.1
10337  * Copyright(c) 2006-2007, Ext JS, LLC.
10338  *
10339  * Originally Released Under LGPL - original licence link has changed is not relivant.
10340  *
10341  * Fork - LGPL
10342  * <script type="text/javascript">
10343  */
10344 /**
10345  * @class Roo.data.MemoryProxy
10346  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10347  * to the Reader when its load method is called.
10348  * @constructor
10349  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10350  */
10351 Roo.data.MemoryProxy = function(data){
10352     if (data.data) {
10353         data = data.data;
10354     }
10355     Roo.data.MemoryProxy.superclass.constructor.call(this);
10356     this.data = data;
10357 };
10358
10359 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10360     /**
10361      * Load data from the requested source (in this case an in-memory
10362      * data object passed to the constructor), read the data object into
10363      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10364      * process that block using the passed callback.
10365      * @param {Object} params This parameter is not used by the MemoryProxy class.
10366      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10367      * object into a block of Roo.data.Records.
10368      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10369      * The function must be passed <ul>
10370      * <li>The Record block object</li>
10371      * <li>The "arg" argument from the load function</li>
10372      * <li>A boolean success indicator</li>
10373      * </ul>
10374      * @param {Object} scope The scope in which to call the callback
10375      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10376      */
10377     load : function(params, reader, callback, scope, arg){
10378         params = params || {};
10379         var result;
10380         try {
10381             result = reader.readRecords(this.data);
10382         }catch(e){
10383             this.fireEvent("loadexception", this, arg, null, e);
10384             callback.call(scope, null, arg, false);
10385             return;
10386         }
10387         callback.call(scope, result, arg, true);
10388     },
10389     
10390     // private
10391     update : function(params, records){
10392         
10393     }
10394 });/*
10395  * Based on:
10396  * Ext JS Library 1.1.1
10397  * Copyright(c) 2006-2007, Ext JS, LLC.
10398  *
10399  * Originally Released Under LGPL - original licence link has changed is not relivant.
10400  *
10401  * Fork - LGPL
10402  * <script type="text/javascript">
10403  */
10404 /**
10405  * @class Roo.data.HttpProxy
10406  * @extends Roo.data.DataProxy
10407  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10408  * configured to reference a certain URL.<br><br>
10409  * <p>
10410  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10411  * from which the running page was served.<br><br>
10412  * <p>
10413  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10414  * <p>
10415  * Be aware that to enable the browser to parse an XML document, the server must set
10416  * the Content-Type header in the HTTP response to "text/xml".
10417  * @constructor
10418  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10419  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10420  * will be used to make the request.
10421  */
10422 Roo.data.HttpProxy = function(conn){
10423     Roo.data.HttpProxy.superclass.constructor.call(this);
10424     // is conn a conn config or a real conn?
10425     this.conn = conn;
10426     this.useAjax = !conn || !conn.events;
10427   
10428 };
10429
10430 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10431     // thse are take from connection...
10432     
10433     /**
10434      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10435      */
10436     /**
10437      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10438      * extra parameters to each request made by this object. (defaults to undefined)
10439      */
10440     /**
10441      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10442      *  to each request made by this object. (defaults to undefined)
10443      */
10444     /**
10445      * @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)
10446      */
10447     /**
10448      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10449      */
10450      /**
10451      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10452      * @type Boolean
10453      */
10454   
10455
10456     /**
10457      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10458      * @type Boolean
10459      */
10460     /**
10461      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10462      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10463      * a finer-grained basis than the DataProxy events.
10464      */
10465     getConnection : function(){
10466         return this.useAjax ? Roo.Ajax : this.conn;
10467     },
10468
10469     /**
10470      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10471      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10472      * process that block using the passed callback.
10473      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10474      * for the request to the remote server.
10475      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10476      * object into a block of Roo.data.Records.
10477      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10478      * The function must be passed <ul>
10479      * <li>The Record block object</li>
10480      * <li>The "arg" argument from the load function</li>
10481      * <li>A boolean success indicator</li>
10482      * </ul>
10483      * @param {Object} scope The scope in which to call the callback
10484      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10485      */
10486     load : function(params, reader, callback, scope, arg){
10487         if(this.fireEvent("beforeload", this, params) !== false){
10488             var  o = {
10489                 params : params || {},
10490                 request: {
10491                     callback : callback,
10492                     scope : scope,
10493                     arg : arg
10494                 },
10495                 reader: reader,
10496                 callback : this.loadResponse,
10497                 scope: this
10498             };
10499             if(this.useAjax){
10500                 Roo.applyIf(o, this.conn);
10501                 if(this.activeRequest){
10502                     Roo.Ajax.abort(this.activeRequest);
10503                 }
10504                 this.activeRequest = Roo.Ajax.request(o);
10505             }else{
10506                 this.conn.request(o);
10507             }
10508         }else{
10509             callback.call(scope||this, null, arg, false);
10510         }
10511     },
10512
10513     // private
10514     loadResponse : function(o, success, response){
10515         delete this.activeRequest;
10516         if(!success){
10517             this.fireEvent("loadexception", this, o, response);
10518             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10519             return;
10520         }
10521         var result;
10522         try {
10523             result = o.reader.read(response);
10524         }catch(e){
10525             this.fireEvent("loadexception", this, o, response, e);
10526             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10527             return;
10528         }
10529         
10530         this.fireEvent("load", this, o, o.request.arg);
10531         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10532     },
10533
10534     // private
10535     update : function(dataSet){
10536
10537     },
10538
10539     // private
10540     updateResponse : function(dataSet){
10541
10542     }
10543 });/*
10544  * Based on:
10545  * Ext JS Library 1.1.1
10546  * Copyright(c) 2006-2007, Ext JS, LLC.
10547  *
10548  * Originally Released Under LGPL - original licence link has changed is not relivant.
10549  *
10550  * Fork - LGPL
10551  * <script type="text/javascript">
10552  */
10553
10554 /**
10555  * @class Roo.data.ScriptTagProxy
10556  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10557  * other than the originating domain of the running page.<br><br>
10558  * <p>
10559  * <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
10560  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10561  * <p>
10562  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10563  * source code that is used as the source inside a &lt;script> tag.<br><br>
10564  * <p>
10565  * In order for the browser to process the returned data, the server must wrap the data object
10566  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10567  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10568  * depending on whether the callback name was passed:
10569  * <p>
10570  * <pre><code>
10571 boolean scriptTag = false;
10572 String cb = request.getParameter("callback");
10573 if (cb != null) {
10574     scriptTag = true;
10575     response.setContentType("text/javascript");
10576 } else {
10577     response.setContentType("application/x-json");
10578 }
10579 Writer out = response.getWriter();
10580 if (scriptTag) {
10581     out.write(cb + "(");
10582 }
10583 out.print(dataBlock.toJsonString());
10584 if (scriptTag) {
10585     out.write(");");
10586 }
10587 </pre></code>
10588  *
10589  * @constructor
10590  * @param {Object} config A configuration object.
10591  */
10592 Roo.data.ScriptTagProxy = function(config){
10593     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10594     Roo.apply(this, config);
10595     this.head = document.getElementsByTagName("head")[0];
10596 };
10597
10598 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10599
10600 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10601     /**
10602      * @cfg {String} url The URL from which to request the data object.
10603      */
10604     /**
10605      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10606      */
10607     timeout : 30000,
10608     /**
10609      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10610      * the server the name of the callback function set up by the load call to process the returned data object.
10611      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10612      * javascript output which calls this named function passing the data object as its only parameter.
10613      */
10614     callbackParam : "callback",
10615     /**
10616      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10617      * name to the request.
10618      */
10619     nocache : true,
10620
10621     /**
10622      * Load data from the configured URL, read the data object into
10623      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10624      * process that block using the passed callback.
10625      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10626      * for the request to the remote server.
10627      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10628      * object into a block of Roo.data.Records.
10629      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10630      * The function must be passed <ul>
10631      * <li>The Record block object</li>
10632      * <li>The "arg" argument from the load function</li>
10633      * <li>A boolean success indicator</li>
10634      * </ul>
10635      * @param {Object} scope The scope in which to call the callback
10636      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10637      */
10638     load : function(params, reader, callback, scope, arg){
10639         if(this.fireEvent("beforeload", this, params) !== false){
10640
10641             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10642
10643             var url = this.url;
10644             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10645             if(this.nocache){
10646                 url += "&_dc=" + (new Date().getTime());
10647             }
10648             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10649             var trans = {
10650                 id : transId,
10651                 cb : "stcCallback"+transId,
10652                 scriptId : "stcScript"+transId,
10653                 params : params,
10654                 arg : arg,
10655                 url : url,
10656                 callback : callback,
10657                 scope : scope,
10658                 reader : reader
10659             };
10660             var conn = this;
10661
10662             window[trans.cb] = function(o){
10663                 conn.handleResponse(o, trans);
10664             };
10665
10666             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10667
10668             if(this.autoAbort !== false){
10669                 this.abort();
10670             }
10671
10672             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10673
10674             var script = document.createElement("script");
10675             script.setAttribute("src", url);
10676             script.setAttribute("type", "text/javascript");
10677             script.setAttribute("id", trans.scriptId);
10678             this.head.appendChild(script);
10679
10680             this.trans = trans;
10681         }else{
10682             callback.call(scope||this, null, arg, false);
10683         }
10684     },
10685
10686     // private
10687     isLoading : function(){
10688         return this.trans ? true : false;
10689     },
10690
10691     /**
10692      * Abort the current server request.
10693      */
10694     abort : function(){
10695         if(this.isLoading()){
10696             this.destroyTrans(this.trans);
10697         }
10698     },
10699
10700     // private
10701     destroyTrans : function(trans, isLoaded){
10702         this.head.removeChild(document.getElementById(trans.scriptId));
10703         clearTimeout(trans.timeoutId);
10704         if(isLoaded){
10705             window[trans.cb] = undefined;
10706             try{
10707                 delete window[trans.cb];
10708             }catch(e){}
10709         }else{
10710             // if hasn't been loaded, wait for load to remove it to prevent script error
10711             window[trans.cb] = function(){
10712                 window[trans.cb] = undefined;
10713                 try{
10714                     delete window[trans.cb];
10715                 }catch(e){}
10716             };
10717         }
10718     },
10719
10720     // private
10721     handleResponse : function(o, trans){
10722         this.trans = false;
10723         this.destroyTrans(trans, true);
10724         var result;
10725         try {
10726             result = trans.reader.readRecords(o);
10727         }catch(e){
10728             this.fireEvent("loadexception", this, o, trans.arg, e);
10729             trans.callback.call(trans.scope||window, null, trans.arg, false);
10730             return;
10731         }
10732         this.fireEvent("load", this, o, trans.arg);
10733         trans.callback.call(trans.scope||window, result, trans.arg, true);
10734     },
10735
10736     // private
10737     handleFailure : function(trans){
10738         this.trans = false;
10739         this.destroyTrans(trans, false);
10740         this.fireEvent("loadexception", this, null, trans.arg);
10741         trans.callback.call(trans.scope||window, null, trans.arg, false);
10742     }
10743 });/*
10744  * Based on:
10745  * Ext JS Library 1.1.1
10746  * Copyright(c) 2006-2007, Ext JS, LLC.
10747  *
10748  * Originally Released Under LGPL - original licence link has changed is not relivant.
10749  *
10750  * Fork - LGPL
10751  * <script type="text/javascript">
10752  */
10753
10754 /**
10755  * @class Roo.data.JsonReader
10756  * @extends Roo.data.DataReader
10757  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10758  * based on mappings in a provided Roo.data.Record constructor.
10759  * 
10760  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10761  * in the reply previously. 
10762  * 
10763  * <p>
10764  * Example code:
10765  * <pre><code>
10766 var RecordDef = Roo.data.Record.create([
10767     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10768     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10769 ]);
10770 var myReader = new Roo.data.JsonReader({
10771     totalProperty: "results",    // The property which contains the total dataset size (optional)
10772     root: "rows",                // The property which contains an Array of row objects
10773     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10774 }, RecordDef);
10775 </code></pre>
10776  * <p>
10777  * This would consume a JSON file like this:
10778  * <pre><code>
10779 { 'results': 2, 'rows': [
10780     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10781     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10782 }
10783 </code></pre>
10784  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10785  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10786  * paged from the remote server.
10787  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10788  * @cfg {String} root name of the property which contains the Array of row objects.
10789  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10790  * @cfg {Array} fields Array of field definition objects
10791  * @constructor
10792  * Create a new JsonReader
10793  * @param {Object} meta Metadata configuration options
10794  * @param {Object} recordType Either an Array of field definition objects,
10795  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10796  */
10797 Roo.data.JsonReader = function(meta, recordType){
10798     
10799     meta = meta || {};
10800     // set some defaults:
10801     Roo.applyIf(meta, {
10802         totalProperty: 'total',
10803         successProperty : 'success',
10804         root : 'data',
10805         id : 'id'
10806     });
10807     
10808     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10809 };
10810 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10811     
10812     /**
10813      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10814      * Used by Store query builder to append _requestMeta to params.
10815      * 
10816      */
10817     metaFromRemote : false,
10818     /**
10819      * This method is only used by a DataProxy which has retrieved data from a remote server.
10820      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10821      * @return {Object} data A data block which is used by an Roo.data.Store object as
10822      * a cache of Roo.data.Records.
10823      */
10824     read : function(response){
10825         var json = response.responseText;
10826        
10827         var o = /* eval:var:o */ eval("("+json+")");
10828         if(!o) {
10829             throw {message: "JsonReader.read: Json object not found"};
10830         }
10831         
10832         if(o.metaData){
10833             
10834             delete this.ef;
10835             this.metaFromRemote = true;
10836             this.meta = o.metaData;
10837             this.recordType = Roo.data.Record.create(o.metaData.fields);
10838             this.onMetaChange(this.meta, this.recordType, o);
10839         }
10840         return this.readRecords(o);
10841     },
10842
10843     // private function a store will implement
10844     onMetaChange : function(meta, recordType, o){
10845
10846     },
10847
10848     /**
10849          * @ignore
10850          */
10851     simpleAccess: function(obj, subsc) {
10852         return obj[subsc];
10853     },
10854
10855         /**
10856          * @ignore
10857          */
10858     getJsonAccessor: function(){
10859         var re = /[\[\.]/;
10860         return function(expr) {
10861             try {
10862                 return(re.test(expr))
10863                     ? new Function("obj", "return obj." + expr)
10864                     : function(obj){
10865                         return obj[expr];
10866                     };
10867             } catch(e){}
10868             return Roo.emptyFn;
10869         };
10870     }(),
10871
10872     /**
10873      * Create a data block containing Roo.data.Records from an XML document.
10874      * @param {Object} o An object which contains an Array of row objects in the property specified
10875      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10876      * which contains the total size of the dataset.
10877      * @return {Object} data A data block which is used by an Roo.data.Store object as
10878      * a cache of Roo.data.Records.
10879      */
10880     readRecords : function(o){
10881         /**
10882          * After any data loads, the raw JSON data is available for further custom processing.
10883          * @type Object
10884          */
10885         this.o = o;
10886         var s = this.meta, Record = this.recordType,
10887             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10888
10889 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10890         if (!this.ef) {
10891             if(s.totalProperty) {
10892                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10893                 }
10894                 if(s.successProperty) {
10895                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10896                 }
10897                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10898                 if (s.id) {
10899                         var g = this.getJsonAccessor(s.id);
10900                         this.getId = function(rec) {
10901                                 var r = g(rec);  
10902                                 return (r === undefined || r === "") ? null : r;
10903                         };
10904                 } else {
10905                         this.getId = function(){return null;};
10906                 }
10907             this.ef = [];
10908             for(var jj = 0; jj < fl; jj++){
10909                 f = fi[jj];
10910                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10911                 this.ef[jj] = this.getJsonAccessor(map);
10912             }
10913         }
10914
10915         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10916         if(s.totalProperty){
10917             var vt = parseInt(this.getTotal(o), 10);
10918             if(!isNaN(vt)){
10919                 totalRecords = vt;
10920             }
10921         }
10922         if(s.successProperty){
10923             var vs = this.getSuccess(o);
10924             if(vs === false || vs === 'false'){
10925                 success = false;
10926             }
10927         }
10928         var records = [];
10929         for(var i = 0; i < c; i++){
10930                 var n = root[i];
10931             var values = {};
10932             var id = this.getId(n);
10933             for(var j = 0; j < fl; j++){
10934                 f = fi[j];
10935             var v = this.ef[j](n);
10936             if (!f.convert) {
10937                 Roo.log('missing convert for ' + f.name);
10938                 Roo.log(f);
10939                 continue;
10940             }
10941             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10942             }
10943             var record = new Record(values, id);
10944             record.json = n;
10945             records[i] = record;
10946         }
10947         return {
10948             raw : o,
10949             success : success,
10950             records : records,
10951             totalRecords : totalRecords
10952         };
10953     }
10954 });/*
10955  * Based on:
10956  * Ext JS Library 1.1.1
10957  * Copyright(c) 2006-2007, Ext JS, LLC.
10958  *
10959  * Originally Released Under LGPL - original licence link has changed is not relivant.
10960  *
10961  * Fork - LGPL
10962  * <script type="text/javascript">
10963  */
10964
10965 /**
10966  * @class Roo.data.ArrayReader
10967  * @extends Roo.data.DataReader
10968  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10969  * Each element of that Array represents a row of data fields. The
10970  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10971  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10972  * <p>
10973  * Example code:.
10974  * <pre><code>
10975 var RecordDef = Roo.data.Record.create([
10976     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10977     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10978 ]);
10979 var myReader = new Roo.data.ArrayReader({
10980     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10981 }, RecordDef);
10982 </code></pre>
10983  * <p>
10984  * This would consume an Array like this:
10985  * <pre><code>
10986 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10987   </code></pre>
10988  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10989  * @constructor
10990  * Create a new JsonReader
10991  * @param {Object} meta Metadata configuration options.
10992  * @param {Object} recordType Either an Array of field definition objects
10993  * as specified to {@link Roo.data.Record#create},
10994  * or an {@link Roo.data.Record} object
10995  * created using {@link Roo.data.Record#create}.
10996  */
10997 Roo.data.ArrayReader = function(meta, recordType){
10998     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10999 };
11000
11001 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11002     /**
11003      * Create a data block containing Roo.data.Records from an XML document.
11004      * @param {Object} o An Array of row objects which represents the dataset.
11005      * @return {Object} data A data block which is used by an Roo.data.Store object as
11006      * a cache of Roo.data.Records.
11007      */
11008     readRecords : function(o){
11009         var sid = this.meta ? this.meta.id : null;
11010         var recordType = this.recordType, fields = recordType.prototype.fields;
11011         var records = [];
11012         var root = o;
11013             for(var i = 0; i < root.length; i++){
11014                     var n = root[i];
11015                 var values = {};
11016                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11017                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11018                 var f = fields.items[j];
11019                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11020                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11021                 v = f.convert(v);
11022                 values[f.name] = v;
11023             }
11024                 var record = new recordType(values, id);
11025                 record.json = n;
11026                 records[records.length] = record;
11027             }
11028             return {
11029                 records : records,
11030                 totalRecords : records.length
11031             };
11032     }
11033 });/*
11034  * - LGPL
11035  * * 
11036  */
11037
11038 /**
11039  * @class Roo.bootstrap.ComboBox
11040  * @extends Roo.bootstrap.TriggerField
11041  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11042  * @cfg {Boolean} append (true|false) default false
11043  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11044  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11045  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11046  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11047  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11048  * @cfg {Boolean} animate default true
11049  * @cfg {Boolean} emptyResultText only for touch device
11050  * @constructor
11051  * Create a new ComboBox.
11052  * @param {Object} config Configuration options
11053  */
11054 Roo.bootstrap.ComboBox = function(config){
11055     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11056     this.addEvents({
11057         /**
11058          * @event expand
11059          * Fires when the dropdown list is expanded
11060              * @param {Roo.bootstrap.ComboBox} combo This combo box
11061              */
11062         'expand' : true,
11063         /**
11064          * @event collapse
11065          * Fires when the dropdown list is collapsed
11066              * @param {Roo.bootstrap.ComboBox} combo This combo box
11067              */
11068         'collapse' : true,
11069         /**
11070          * @event beforeselect
11071          * Fires before a list item is selected. Return false to cancel the selection.
11072              * @param {Roo.bootstrap.ComboBox} combo This combo box
11073              * @param {Roo.data.Record} record The data record returned from the underlying store
11074              * @param {Number} index The index of the selected item in the dropdown list
11075              */
11076         'beforeselect' : true,
11077         /**
11078          * @event select
11079          * Fires when a list item is selected
11080              * @param {Roo.bootstrap.ComboBox} combo This combo box
11081              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11082              * @param {Number} index The index of the selected item in the dropdown list
11083              */
11084         'select' : true,
11085         /**
11086          * @event beforequery
11087          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11088          * The event object passed has these properties:
11089              * @param {Roo.bootstrap.ComboBox} combo This combo box
11090              * @param {String} query The query
11091              * @param {Boolean} forceAll true to force "all" query
11092              * @param {Boolean} cancel true to cancel the query
11093              * @param {Object} e The query event object
11094              */
11095         'beforequery': true,
11096          /**
11097          * @event add
11098          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11099              * @param {Roo.bootstrap.ComboBox} combo This combo box
11100              */
11101         'add' : true,
11102         /**
11103          * @event edit
11104          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11105              * @param {Roo.bootstrap.ComboBox} combo This combo box
11106              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11107              */
11108         'edit' : true,
11109         /**
11110          * @event remove
11111          * Fires when the remove value from the combobox array
11112              * @param {Roo.bootstrap.ComboBox} combo This combo box
11113              */
11114         'remove' : true,
11115         /**
11116          * @event specialfilter
11117          * Fires when specialfilter
11118             * @param {Roo.bootstrap.ComboBox} combo This combo box
11119             */
11120         'specialfilter' : true
11121         
11122     });
11123     
11124     this.item = [];
11125     this.tickItems = [];
11126     
11127     this.selectedIndex = -1;
11128     if(this.mode == 'local'){
11129         if(config.queryDelay === undefined){
11130             this.queryDelay = 10;
11131         }
11132         if(config.minChars === undefined){
11133             this.minChars = 0;
11134         }
11135     }
11136 };
11137
11138 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11139      
11140     /**
11141      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11142      * rendering into an Roo.Editor, defaults to false)
11143      */
11144     /**
11145      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11146      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11147      */
11148     /**
11149      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11150      */
11151     /**
11152      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11153      * the dropdown list (defaults to undefined, with no header element)
11154      */
11155
11156      /**
11157      * @cfg {String/Roo.Template} tpl The template to use to render the output
11158      */
11159      
11160      /**
11161      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11162      */
11163     listWidth: undefined,
11164     /**
11165      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11166      * mode = 'remote' or 'text' if mode = 'local')
11167      */
11168     displayField: undefined,
11169     
11170     /**
11171      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11172      * mode = 'remote' or 'value' if mode = 'local'). 
11173      * Note: use of a valueField requires the user make a selection
11174      * in order for a value to be mapped.
11175      */
11176     valueField: undefined,
11177     
11178     
11179     /**
11180      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11181      * field's data value (defaults to the underlying DOM element's name)
11182      */
11183     hiddenName: undefined,
11184     /**
11185      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11186      */
11187     listClass: '',
11188     /**
11189      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11190      */
11191     selectedClass: 'active',
11192     
11193     /**
11194      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11195      */
11196     shadow:'sides',
11197     /**
11198      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11199      * anchor positions (defaults to 'tl-bl')
11200      */
11201     listAlign: 'tl-bl?',
11202     /**
11203      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11204      */
11205     maxHeight: 300,
11206     /**
11207      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11208      * query specified by the allQuery config option (defaults to 'query')
11209      */
11210     triggerAction: 'query',
11211     /**
11212      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11213      * (defaults to 4, does not apply if editable = false)
11214      */
11215     minChars : 4,
11216     /**
11217      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11218      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11219      */
11220     typeAhead: false,
11221     /**
11222      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11223      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11224      */
11225     queryDelay: 500,
11226     /**
11227      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11228      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11229      */
11230     pageSize: 0,
11231     /**
11232      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11233      * when editable = true (defaults to false)
11234      */
11235     selectOnFocus:false,
11236     /**
11237      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11238      */
11239     queryParam: 'query',
11240     /**
11241      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11242      * when mode = 'remote' (defaults to 'Loading...')
11243      */
11244     loadingText: 'Loading...',
11245     /**
11246      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11247      */
11248     resizable: false,
11249     /**
11250      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11251      */
11252     handleHeight : 8,
11253     /**
11254      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11255      * traditional select (defaults to true)
11256      */
11257     editable: true,
11258     /**
11259      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11260      */
11261     allQuery: '',
11262     /**
11263      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11264      */
11265     mode: 'remote',
11266     /**
11267      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11268      * listWidth has a higher value)
11269      */
11270     minListWidth : 70,
11271     /**
11272      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11273      * allow the user to set arbitrary text into the field (defaults to false)
11274      */
11275     forceSelection:false,
11276     /**
11277      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11278      * if typeAhead = true (defaults to 250)
11279      */
11280     typeAheadDelay : 250,
11281     /**
11282      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11283      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11284      */
11285     valueNotFoundText : undefined,
11286     /**
11287      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11288      */
11289     blockFocus : false,
11290     
11291     /**
11292      * @cfg {Boolean} disableClear Disable showing of clear button.
11293      */
11294     disableClear : false,
11295     /**
11296      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11297      */
11298     alwaysQuery : false,
11299     
11300     /**
11301      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11302      */
11303     multiple : false,
11304     
11305     /**
11306      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11307      */
11308     invalidClass : "has-warning",
11309     
11310     /**
11311      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11312      */
11313     validClass : "has-success",
11314     
11315     /**
11316      * @cfg {Boolean} specialFilter (true|false) special filter default false
11317      */
11318     specialFilter : false,
11319     
11320     /**
11321      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11322      */
11323     mobileTouchView : true,
11324     
11325     //private
11326     addicon : false,
11327     editicon: false,
11328     
11329     page: 0,
11330     hasQuery: false,
11331     append: false,
11332     loadNext: false,
11333     autoFocus : true,
11334     tickable : false,
11335     btnPosition : 'right',
11336     triggerList : true,
11337     showToggleBtn : true,
11338     animate : true,
11339     emptyResultText: 'Empty',
11340     // element that contains real text value.. (when hidden is used..)
11341     
11342     getAutoCreate : function()
11343     {
11344         var cfg = false;
11345         
11346         /*
11347          * Touch Devices
11348          */
11349         
11350         if(Roo.isTouch && this.mobileTouchView){
11351             cfg = this.getAutoCreateTouchView();
11352             return cfg;;
11353         }
11354         
11355         /*
11356          *  Normal ComboBox
11357          */
11358         if(!this.tickable){
11359             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11360             return cfg;
11361         }
11362         
11363         /*
11364          *  ComboBox with tickable selections
11365          */
11366              
11367         var align = this.labelAlign || this.parentLabelAlign();
11368         
11369         cfg = {
11370             cls : 'form-group roo-combobox-tickable' //input-group
11371         };
11372         
11373         var buttons = {
11374             tag : 'div',
11375             cls : 'tickable-buttons',
11376             cn : [
11377                 {
11378                     tag : 'button',
11379                     type : 'button',
11380                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11381                     html : 'Edit'
11382                 },
11383                 {
11384                     tag : 'button',
11385                     type : 'button',
11386                     name : 'ok',
11387                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11388                     html : 'Done'
11389                 },
11390                 {
11391                     tag : 'button',
11392                     type : 'button',
11393                     name : 'cancel',
11394                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11395                     html : 'Cancel'
11396                 }
11397             ]
11398         };
11399         
11400         if(this.editable){
11401             buttons.cn.unshift({
11402                 tag: 'input',
11403                 cls: 'select2-search-field-input'
11404             });
11405         }
11406         
11407         var _this = this;
11408         
11409         Roo.each(buttons.cn, function(c){
11410             if (_this.size) {
11411                 c.cls += ' btn-' + _this.size;
11412             }
11413
11414             if (_this.disabled) {
11415                 c.disabled = true;
11416             }
11417         });
11418         
11419         var box = {
11420             tag: 'div',
11421             cn: [
11422                 {
11423                     tag: 'input',
11424                     type : 'hidden',
11425                     cls: 'form-hidden-field'
11426                 },
11427                 {
11428                     tag: 'ul',
11429                     cls: 'select2-choices',
11430                     cn:[
11431                         {
11432                             tag: 'li',
11433                             cls: 'select2-search-field',
11434                             cn: [
11435
11436                                 buttons
11437                             ]
11438                         }
11439                     ]
11440                 }
11441             ]
11442         }
11443         
11444         var combobox = {
11445             cls: 'select2-container input-group select2-container-multi',
11446             cn: [
11447                 box
11448 //                {
11449 //                    tag: 'ul',
11450 //                    cls: 'typeahead typeahead-long dropdown-menu',
11451 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11452 //                }
11453             ]
11454         };
11455         
11456         if(this.hasFeedback && !this.allowBlank){
11457             
11458             var feedback = {
11459                 tag: 'span',
11460                 cls: 'glyphicon form-control-feedback'
11461             };
11462
11463             combobox.cn.push(feedback);
11464         }
11465         
11466         if (align ==='left' && this.fieldLabel.length) {
11467             
11468                 Roo.log("left and has label");
11469                 cfg.cn = [
11470                     
11471                     {
11472                         tag: 'label',
11473                         'for' :  id,
11474                         cls : 'control-label col-sm-' + this.labelWidth,
11475                         html : this.fieldLabel
11476                         
11477                     },
11478                     {
11479                         cls : "col-sm-" + (12 - this.labelWidth), 
11480                         cn: [
11481                             combobox
11482                         ]
11483                     }
11484                     
11485                 ];
11486         } else if ( this.fieldLabel.length) {
11487                 Roo.log(" label");
11488                  cfg.cn = [
11489                    
11490                     {
11491                         tag: 'label',
11492                         //cls : 'input-group-addon',
11493                         html : this.fieldLabel
11494                         
11495                     },
11496                     
11497                     combobox
11498                     
11499                 ];
11500
11501         } else {
11502             
11503                 Roo.log(" no label && no align");
11504                 cfg = combobox
11505                      
11506                 
11507         }
11508          
11509         var settings=this;
11510         ['xs','sm','md','lg'].map(function(size){
11511             if (settings[size]) {
11512                 cfg.cls += ' col-' + size + '-' + settings[size];
11513             }
11514         });
11515         
11516         return cfg;
11517         
11518     },
11519     
11520     // private
11521     initEvents: function()
11522     {
11523         
11524         if (!this.store) {
11525             throw "can not find store for combo";
11526         }
11527         
11528         this.store = Roo.factory(this.store, Roo.data);
11529         
11530         /*
11531          * Touch Devices
11532          */
11533         
11534         if(Roo.isTouch && this.mobileTouchView){
11535             this.initTouchView();
11536             return;
11537         }
11538         
11539         if(this.tickable){
11540             this.initTickableEvents();
11541             return;
11542         }
11543         
11544         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11545         
11546         if(this.hiddenName){
11547             
11548             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11549             
11550             this.hiddenField.dom.value =
11551                 this.hiddenValue !== undefined ? this.hiddenValue :
11552                 this.value !== undefined ? this.value : '';
11553
11554             // prevent input submission
11555             this.el.dom.removeAttribute('name');
11556             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11557              
11558              
11559         }
11560         //if(Roo.isGecko){
11561         //    this.el.dom.setAttribute('autocomplete', 'off');
11562         //}
11563         
11564         var cls = 'x-combo-list';
11565         
11566         //this.list = new Roo.Layer({
11567         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11568         //});
11569         
11570         var _this = this;
11571         
11572         (function(){
11573             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11574             _this.list.setWidth(lw);
11575         }).defer(100);
11576         
11577         this.list.on('mouseover', this.onViewOver, this);
11578         this.list.on('mousemove', this.onViewMove, this);
11579         
11580         this.list.on('scroll', this.onViewScroll, this);
11581         
11582         /*
11583         this.list.swallowEvent('mousewheel');
11584         this.assetHeight = 0;
11585
11586         if(this.title){
11587             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11588             this.assetHeight += this.header.getHeight();
11589         }
11590
11591         this.innerList = this.list.createChild({cls:cls+'-inner'});
11592         this.innerList.on('mouseover', this.onViewOver, this);
11593         this.innerList.on('mousemove', this.onViewMove, this);
11594         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11595         
11596         if(this.allowBlank && !this.pageSize && !this.disableClear){
11597             this.footer = this.list.createChild({cls:cls+'-ft'});
11598             this.pageTb = new Roo.Toolbar(this.footer);
11599            
11600         }
11601         if(this.pageSize){
11602             this.footer = this.list.createChild({cls:cls+'-ft'});
11603             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11604                     {pageSize: this.pageSize});
11605             
11606         }
11607         
11608         if (this.pageTb && this.allowBlank && !this.disableClear) {
11609             var _this = this;
11610             this.pageTb.add(new Roo.Toolbar.Fill(), {
11611                 cls: 'x-btn-icon x-btn-clear',
11612                 text: '&#160;',
11613                 handler: function()
11614                 {
11615                     _this.collapse();
11616                     _this.clearValue();
11617                     _this.onSelect(false, -1);
11618                 }
11619             });
11620         }
11621         if (this.footer) {
11622             this.assetHeight += this.footer.getHeight();
11623         }
11624         */
11625             
11626         if(!this.tpl){
11627             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11628         }
11629
11630         this.view = new Roo.View(this.list, this.tpl, {
11631             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11632         });
11633         //this.view.wrapEl.setDisplayed(false);
11634         this.view.on('click', this.onViewClick, this);
11635         
11636         
11637         
11638         this.store.on('beforeload', this.onBeforeLoad, this);
11639         this.store.on('load', this.onLoad, this);
11640         this.store.on('loadexception', this.onLoadException, this);
11641         /*
11642         if(this.resizable){
11643             this.resizer = new Roo.Resizable(this.list,  {
11644                pinned:true, handles:'se'
11645             });
11646             this.resizer.on('resize', function(r, w, h){
11647                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11648                 this.listWidth = w;
11649                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11650                 this.restrictHeight();
11651             }, this);
11652             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11653         }
11654         */
11655         if(!this.editable){
11656             this.editable = true;
11657             this.setEditable(false);
11658         }
11659         
11660         /*
11661         
11662         if (typeof(this.events.add.listeners) != 'undefined') {
11663             
11664             this.addicon = this.wrap.createChild(
11665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11666        
11667             this.addicon.on('click', function(e) {
11668                 this.fireEvent('add', this);
11669             }, this);
11670         }
11671         if (typeof(this.events.edit.listeners) != 'undefined') {
11672             
11673             this.editicon = this.wrap.createChild(
11674                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11675             if (this.addicon) {
11676                 this.editicon.setStyle('margin-left', '40px');
11677             }
11678             this.editicon.on('click', function(e) {
11679                 
11680                 // we fire even  if inothing is selected..
11681                 this.fireEvent('edit', this, this.lastData );
11682                 
11683             }, this);
11684         }
11685         */
11686         
11687         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11688             "up" : function(e){
11689                 this.inKeyMode = true;
11690                 this.selectPrev();
11691             },
11692
11693             "down" : function(e){
11694                 if(!this.isExpanded()){
11695                     this.onTriggerClick();
11696                 }else{
11697                     this.inKeyMode = true;
11698                     this.selectNext();
11699                 }
11700             },
11701
11702             "enter" : function(e){
11703 //                this.onViewClick();
11704                 //return true;
11705                 this.collapse();
11706                 
11707                 if(this.fireEvent("specialkey", this, e)){
11708                     this.onViewClick(false);
11709                 }
11710                 
11711                 return true;
11712             },
11713
11714             "esc" : function(e){
11715                 this.collapse();
11716             },
11717
11718             "tab" : function(e){
11719                 this.collapse();
11720                 
11721                 if(this.fireEvent("specialkey", this, e)){
11722                     this.onViewClick(false);
11723                 }
11724                 
11725                 return true;
11726             },
11727
11728             scope : this,
11729
11730             doRelay : function(foo, bar, hname){
11731                 if(hname == 'down' || this.scope.isExpanded()){
11732                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11733                 }
11734                 return true;
11735             },
11736
11737             forceKeyDown: true
11738         });
11739         
11740         
11741         this.queryDelay = Math.max(this.queryDelay || 10,
11742                 this.mode == 'local' ? 10 : 250);
11743         
11744         
11745         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11746         
11747         if(this.typeAhead){
11748             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11749         }
11750         if(this.editable !== false){
11751             this.inputEl().on("keyup", this.onKeyUp, this);
11752         }
11753         if(this.forceSelection){
11754             this.inputEl().on('blur', this.doForce, this);
11755         }
11756         
11757         if(this.multiple){
11758             this.choices = this.el.select('ul.select2-choices', true).first();
11759             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11760         }
11761     },
11762     
11763     initTickableEvents: function()
11764     {   
11765         this.createList();
11766         
11767         if(this.hiddenName){
11768             
11769             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11770             
11771             this.hiddenField.dom.value =
11772                 this.hiddenValue !== undefined ? this.hiddenValue :
11773                 this.value !== undefined ? this.value : '';
11774
11775             // prevent input submission
11776             this.el.dom.removeAttribute('name');
11777             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11778              
11779              
11780         }
11781         
11782 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11783         
11784         this.choices = this.el.select('ul.select2-choices', true).first();
11785         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11786         if(this.triggerList){
11787             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11788         }
11789          
11790         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11791         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11792         
11793         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11794         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11795         
11796         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11797         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11798         
11799         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11800         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11801         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11802         
11803         this.okBtn.hide();
11804         this.cancelBtn.hide();
11805         
11806         var _this = this;
11807         
11808         (function(){
11809             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11810             _this.list.setWidth(lw);
11811         }).defer(100);
11812         
11813         this.list.on('mouseover', this.onViewOver, this);
11814         this.list.on('mousemove', this.onViewMove, this);
11815         
11816         this.list.on('scroll', this.onViewScroll, this);
11817         
11818         if(!this.tpl){
11819             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>';
11820         }
11821
11822         this.view = new Roo.View(this.list, this.tpl, {
11823             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11824         });
11825         
11826         //this.view.wrapEl.setDisplayed(false);
11827         this.view.on('click', this.onViewClick, this);
11828         
11829         
11830         
11831         this.store.on('beforeload', this.onBeforeLoad, this);
11832         this.store.on('load', this.onLoad, this);
11833         this.store.on('loadexception', this.onLoadException, this);
11834         
11835         if(this.editable){
11836             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11837                 "up" : function(e){
11838                     this.inKeyMode = true;
11839                     this.selectPrev();
11840                 },
11841
11842                 "down" : function(e){
11843                     this.inKeyMode = true;
11844                     this.selectNext();
11845                 },
11846
11847                 "enter" : function(e){
11848                     if(this.fireEvent("specialkey", this, e)){
11849                         this.onViewClick(false);
11850                     }
11851                     
11852                     return true;
11853                 },
11854
11855                 "esc" : function(e){
11856                     this.onTickableFooterButtonClick(e, false, false);
11857                 },
11858
11859                 "tab" : function(e){
11860                     this.fireEvent("specialkey", this, e);
11861                     
11862                     this.onTickableFooterButtonClick(e, false, false);
11863                     
11864                     return true;
11865                 },
11866
11867                 scope : this,
11868
11869                 doRelay : function(e, fn, key){
11870                     if(this.scope.isExpanded()){
11871                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11872                     }
11873                     return true;
11874                 },
11875
11876                 forceKeyDown: true
11877             });
11878         }
11879         
11880         this.queryDelay = Math.max(this.queryDelay || 10,
11881                 this.mode == 'local' ? 10 : 250);
11882         
11883         
11884         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11885         
11886         if(this.typeAhead){
11887             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11888         }
11889         
11890         if(this.editable !== false){
11891             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11892         }
11893         
11894     },
11895
11896     onDestroy : function(){
11897         if(this.view){
11898             this.view.setStore(null);
11899             this.view.el.removeAllListeners();
11900             this.view.el.remove();
11901             this.view.purgeListeners();
11902         }
11903         if(this.list){
11904             this.list.dom.innerHTML  = '';
11905         }
11906         
11907         if(this.store){
11908             this.store.un('beforeload', this.onBeforeLoad, this);
11909             this.store.un('load', this.onLoad, this);
11910             this.store.un('loadexception', this.onLoadException, this);
11911         }
11912         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11913     },
11914
11915     // private
11916     fireKey : function(e){
11917         if(e.isNavKeyPress() && !this.list.isVisible()){
11918             this.fireEvent("specialkey", this, e);
11919         }
11920     },
11921
11922     // private
11923     onResize: function(w, h){
11924 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11925 //        
11926 //        if(typeof w != 'number'){
11927 //            // we do not handle it!?!?
11928 //            return;
11929 //        }
11930 //        var tw = this.trigger.getWidth();
11931 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11932 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11933 //        var x = w - tw;
11934 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11935 //            
11936 //        //this.trigger.setStyle('left', x+'px');
11937 //        
11938 //        if(this.list && this.listWidth === undefined){
11939 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11940 //            this.list.setWidth(lw);
11941 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11942 //        }
11943         
11944     
11945         
11946     },
11947
11948     /**
11949      * Allow or prevent the user from directly editing the field text.  If false is passed,
11950      * the user will only be able to select from the items defined in the dropdown list.  This method
11951      * is the runtime equivalent of setting the 'editable' config option at config time.
11952      * @param {Boolean} value True to allow the user to directly edit the field text
11953      */
11954     setEditable : function(value){
11955         if(value == this.editable){
11956             return;
11957         }
11958         this.editable = value;
11959         if(!value){
11960             this.inputEl().dom.setAttribute('readOnly', true);
11961             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11962             this.inputEl().addClass('x-combo-noedit');
11963         }else{
11964             this.inputEl().dom.setAttribute('readOnly', false);
11965             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11966             this.inputEl().removeClass('x-combo-noedit');
11967         }
11968     },
11969
11970     // private
11971     
11972     onBeforeLoad : function(combo,opts){
11973         if(!this.hasFocus){
11974             return;
11975         }
11976          if (!opts.add) {
11977             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11978          }
11979         this.restrictHeight();
11980         this.selectedIndex = -1;
11981     },
11982
11983     // private
11984     onLoad : function(){
11985         
11986         this.hasQuery = false;
11987         
11988         if(!this.hasFocus){
11989             return;
11990         }
11991         
11992         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11993             this.loading.hide();
11994         }
11995              
11996         if(this.store.getCount() > 0){
11997             this.expand();
11998             this.restrictHeight();
11999             if(this.lastQuery == this.allQuery){
12000                 if(this.editable && !this.tickable){
12001                     this.inputEl().dom.select();
12002                 }
12003                 
12004                 if(
12005                     !this.selectByValue(this.value, true) &&
12006                     this.autoFocus && 
12007                     (
12008                         !this.store.lastOptions ||
12009                         typeof(this.store.lastOptions.add) == 'undefined' || 
12010                         this.store.lastOptions.add != true
12011                     )
12012                 ){
12013                     this.select(0, true);
12014                 }
12015             }else{
12016                 if(this.autoFocus){
12017                     this.selectNext();
12018                 }
12019                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12020                     this.taTask.delay(this.typeAheadDelay);
12021                 }
12022             }
12023         }else{
12024             this.onEmptyResults();
12025         }
12026         
12027         //this.el.focus();
12028     },
12029     // private
12030     onLoadException : function()
12031     {
12032         this.hasQuery = false;
12033         
12034         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12035             this.loading.hide();
12036         }
12037         
12038         if(this.tickable && this.editable){
12039             return;
12040         }
12041         
12042         this.collapse();
12043         
12044         Roo.log(this.store.reader.jsonData);
12045         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12046             // fixme
12047             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12048         }
12049         
12050         
12051     },
12052     // private
12053     onTypeAhead : function(){
12054         if(this.store.getCount() > 0){
12055             var r = this.store.getAt(0);
12056             var newValue = r.data[this.displayField];
12057             var len = newValue.length;
12058             var selStart = this.getRawValue().length;
12059             
12060             if(selStart != len){
12061                 this.setRawValue(newValue);
12062                 this.selectText(selStart, newValue.length);
12063             }
12064         }
12065     },
12066
12067     // private
12068     onSelect : function(record, index){
12069         
12070         if(this.fireEvent('beforeselect', this, record, index) !== false){
12071         
12072             this.setFromData(index > -1 ? record.data : false);
12073             
12074             this.collapse();
12075             this.fireEvent('select', this, record, index);
12076         }
12077     },
12078
12079     /**
12080      * Returns the currently selected field value or empty string if no value is set.
12081      * @return {String} value The selected value
12082      */
12083     getValue : function(){
12084         
12085         if(this.multiple){
12086             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12087         }
12088         
12089         if(this.valueField){
12090             return typeof this.value != 'undefined' ? this.value : '';
12091         }else{
12092             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12093         }
12094     },
12095
12096     /**
12097      * Clears any text/value currently set in the field
12098      */
12099     clearValue : function(){
12100         if(this.hiddenField){
12101             this.hiddenField.dom.value = '';
12102         }
12103         this.value = '';
12104         this.setRawValue('');
12105         this.lastSelectionText = '';
12106         this.lastData = false;
12107         
12108         var close = this.closeTriggerEl();
12109         
12110         if(close){
12111             close.hide();
12112         }
12113         
12114     },
12115
12116     /**
12117      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12118      * will be displayed in the field.  If the value does not match the data value of an existing item,
12119      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12120      * Otherwise the field will be blank (although the value will still be set).
12121      * @param {String} value The value to match
12122      */
12123     setValue : function(v){
12124         if(this.multiple){
12125             this.syncValue();
12126             return;
12127         }
12128         
12129         var text = v;
12130         if(this.valueField){
12131             var r = this.findRecord(this.valueField, v);
12132             if(r){
12133                 text = r.data[this.displayField];
12134             }else if(this.valueNotFoundText !== undefined){
12135                 text = this.valueNotFoundText;
12136             }
12137         }
12138         this.lastSelectionText = text;
12139         if(this.hiddenField){
12140             this.hiddenField.dom.value = v;
12141         }
12142         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12143         this.value = v;
12144         
12145         var close = this.closeTriggerEl();
12146         
12147         if(close){
12148             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12149         }
12150     },
12151     /**
12152      * @property {Object} the last set data for the element
12153      */
12154     
12155     lastData : false,
12156     /**
12157      * Sets the value of the field based on a object which is related to the record format for the store.
12158      * @param {Object} value the value to set as. or false on reset?
12159      */
12160     setFromData : function(o){
12161         
12162         if(this.multiple){
12163             this.addItem(o);
12164             return;
12165         }
12166             
12167         var dv = ''; // display value
12168         var vv = ''; // value value..
12169         this.lastData = o;
12170         if (this.displayField) {
12171             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12172         } else {
12173             // this is an error condition!!!
12174             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12175         }
12176         
12177         if(this.valueField){
12178             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12179         }
12180         
12181         var close = this.closeTriggerEl();
12182         
12183         if(close){
12184             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12185         }
12186         
12187         if(this.hiddenField){
12188             this.hiddenField.dom.value = vv;
12189             
12190             this.lastSelectionText = dv;
12191             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12192             this.value = vv;
12193             return;
12194         }
12195         // no hidden field.. - we store the value in 'value', but still display
12196         // display field!!!!
12197         this.lastSelectionText = dv;
12198         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12199         this.value = vv;
12200         
12201         
12202         
12203     },
12204     // private
12205     reset : function(){
12206         // overridden so that last data is reset..
12207         
12208         if(this.multiple){
12209             this.clearItem();
12210             return;
12211         }
12212         
12213         this.setValue(this.originalValue);
12214         this.clearInvalid();
12215         this.lastData = false;
12216         if (this.view) {
12217             this.view.clearSelections();
12218         }
12219     },
12220     // private
12221     findRecord : function(prop, value){
12222         var record;
12223         if(this.store.getCount() > 0){
12224             this.store.each(function(r){
12225                 if(r.data[prop] == value){
12226                     record = r;
12227                     return false;
12228                 }
12229                 return true;
12230             });
12231         }
12232         return record;
12233     },
12234     
12235     getName: function()
12236     {
12237         // returns hidden if it's set..
12238         if (!this.rendered) {return ''};
12239         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12240         
12241     },
12242     // private
12243     onViewMove : function(e, t){
12244         this.inKeyMode = false;
12245     },
12246
12247     // private
12248     onViewOver : function(e, t){
12249         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12250             return;
12251         }
12252         var item = this.view.findItemFromChild(t);
12253         
12254         if(item){
12255             var index = this.view.indexOf(item);
12256             this.select(index, false);
12257         }
12258     },
12259
12260     // private
12261     onViewClick : function(view, doFocus, el, e)
12262     {
12263         var index = this.view.getSelectedIndexes()[0];
12264         
12265         var r = this.store.getAt(index);
12266         
12267         if(this.tickable){
12268             
12269             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12270                 return;
12271             }
12272             
12273             var rm = false;
12274             var _this = this;
12275             
12276             Roo.each(this.tickItems, function(v,k){
12277                 
12278                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12279                     _this.tickItems.splice(k, 1);
12280                     
12281                     if(typeof(e) == 'undefined' && view == false){
12282                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12283                     }
12284                     
12285                     rm = true;
12286                     return;
12287                 }
12288             });
12289             
12290             if(rm){
12291                 return;
12292             }
12293             
12294             this.tickItems.push(r.data);
12295             
12296             if(typeof(e) == 'undefined' && view == false){
12297                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12298             }
12299                     
12300             return;
12301         }
12302         
12303         if(r){
12304             this.onSelect(r, index);
12305         }
12306         if(doFocus !== false && !this.blockFocus){
12307             this.inputEl().focus();
12308         }
12309     },
12310
12311     // private
12312     restrictHeight : function(){
12313         //this.innerList.dom.style.height = '';
12314         //var inner = this.innerList.dom;
12315         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12316         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12317         //this.list.beginUpdate();
12318         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12319         this.list.alignTo(this.inputEl(), this.listAlign);
12320         this.list.alignTo(this.inputEl(), this.listAlign);
12321         //this.list.endUpdate();
12322     },
12323
12324     // private
12325     onEmptyResults : function(){
12326         
12327         if(this.tickable && this.editable){
12328             this.restrictHeight();
12329             return;
12330         }
12331         
12332         this.collapse();
12333     },
12334
12335     /**
12336      * Returns true if the dropdown list is expanded, else false.
12337      */
12338     isExpanded : function(){
12339         return this.list.isVisible();
12340     },
12341
12342     /**
12343      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12344      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12345      * @param {String} value The data value of the item to select
12346      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12347      * selected item if it is not currently in view (defaults to true)
12348      * @return {Boolean} True if the value matched an item in the list, else false
12349      */
12350     selectByValue : function(v, scrollIntoView){
12351         if(v !== undefined && v !== null){
12352             var r = this.findRecord(this.valueField || this.displayField, v);
12353             if(r){
12354                 this.select(this.store.indexOf(r), scrollIntoView);
12355                 return true;
12356             }
12357         }
12358         return false;
12359     },
12360
12361     /**
12362      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12363      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12364      * @param {Number} index The zero-based index of the list item to select
12365      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12366      * selected item if it is not currently in view (defaults to true)
12367      */
12368     select : function(index, scrollIntoView){
12369         this.selectedIndex = index;
12370         this.view.select(index);
12371         if(scrollIntoView !== false){
12372             var el = this.view.getNode(index);
12373             /*
12374              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12375              */
12376             if(el){
12377                 this.list.scrollChildIntoView(el, false);
12378             }
12379         }
12380     },
12381
12382     // private
12383     selectNext : function(){
12384         var ct = this.store.getCount();
12385         if(ct > 0){
12386             if(this.selectedIndex == -1){
12387                 this.select(0);
12388             }else if(this.selectedIndex < ct-1){
12389                 this.select(this.selectedIndex+1);
12390             }
12391         }
12392     },
12393
12394     // private
12395     selectPrev : function(){
12396         var ct = this.store.getCount();
12397         if(ct > 0){
12398             if(this.selectedIndex == -1){
12399                 this.select(0);
12400             }else if(this.selectedIndex != 0){
12401                 this.select(this.selectedIndex-1);
12402             }
12403         }
12404     },
12405
12406     // private
12407     onKeyUp : function(e){
12408         if(this.editable !== false && !e.isSpecialKey()){
12409             this.lastKey = e.getKey();
12410             this.dqTask.delay(this.queryDelay);
12411         }
12412     },
12413
12414     // private
12415     validateBlur : function(){
12416         return !this.list || !this.list.isVisible();   
12417     },
12418
12419     // private
12420     initQuery : function(){
12421         
12422         var v = this.getRawValue();
12423         
12424         if(this.tickable && this.editable){
12425             v = this.tickableInputEl().getValue();
12426         }
12427         
12428         this.doQuery(v);
12429     },
12430
12431     // private
12432     doForce : function(){
12433         if(this.inputEl().dom.value.length > 0){
12434             this.inputEl().dom.value =
12435                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12436              
12437         }
12438     },
12439
12440     /**
12441      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12442      * query allowing the query action to be canceled if needed.
12443      * @param {String} query The SQL query to execute
12444      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12445      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12446      * saved in the current store (defaults to false)
12447      */
12448     doQuery : function(q, forceAll){
12449         
12450         if(q === undefined || q === null){
12451             q = '';
12452         }
12453         var qe = {
12454             query: q,
12455             forceAll: forceAll,
12456             combo: this,
12457             cancel:false
12458         };
12459         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12460             return false;
12461         }
12462         q = qe.query;
12463         
12464         forceAll = qe.forceAll;
12465         if(forceAll === true || (q.length >= this.minChars)){
12466             
12467             this.hasQuery = true;
12468             
12469             if(this.lastQuery != q || this.alwaysQuery){
12470                 this.lastQuery = q;
12471                 if(this.mode == 'local'){
12472                     this.selectedIndex = -1;
12473                     if(forceAll){
12474                         this.store.clearFilter();
12475                     }else{
12476                         
12477                         if(this.specialFilter){
12478                             this.fireEvent('specialfilter', this);
12479                             this.onLoad();
12480                             return;
12481                         }
12482                         
12483                         this.store.filter(this.displayField, q);
12484                     }
12485                     
12486                     this.store.fireEvent("datachanged", this.store);
12487                     
12488                     this.onLoad();
12489                     
12490                     
12491                 }else{
12492                     
12493                     this.store.baseParams[this.queryParam] = q;
12494                     
12495                     var options = {params : this.getParams(q)};
12496                     
12497                     if(this.loadNext){
12498                         options.add = true;
12499                         options.params.start = this.page * this.pageSize;
12500                     }
12501                     
12502                     this.store.load(options);
12503                     
12504                     /*
12505                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12506                      *  we should expand the list on onLoad
12507                      *  so command out it
12508                      */
12509 //                    this.expand();
12510                 }
12511             }else{
12512                 this.selectedIndex = -1;
12513                 this.onLoad();   
12514             }
12515         }
12516         
12517         this.loadNext = false;
12518     },
12519     
12520     // private
12521     getParams : function(q){
12522         var p = {};
12523         //p[this.queryParam] = q;
12524         
12525         if(this.pageSize){
12526             p.start = 0;
12527             p.limit = this.pageSize;
12528         }
12529         return p;
12530     },
12531
12532     /**
12533      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12534      */
12535     collapse : function(){
12536         if(!this.isExpanded()){
12537             return;
12538         }
12539         
12540         this.list.hide();
12541         
12542         if(this.tickable){
12543             this.hasFocus = false;
12544             this.okBtn.hide();
12545             this.cancelBtn.hide();
12546             this.trigger.show();
12547             
12548             if(this.editable){
12549                 this.tickableInputEl().dom.value = '';
12550                 this.tickableInputEl().blur();
12551             }
12552             
12553         }
12554         
12555         Roo.get(document).un('mousedown', this.collapseIf, this);
12556         Roo.get(document).un('mousewheel', this.collapseIf, this);
12557         if (!this.editable) {
12558             Roo.get(document).un('keydown', this.listKeyPress, this);
12559         }
12560         this.fireEvent('collapse', this);
12561     },
12562
12563     // private
12564     collapseIf : function(e){
12565         var in_combo  = e.within(this.el);
12566         var in_list =  e.within(this.list);
12567         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12568         
12569         if (in_combo || in_list || is_list) {
12570             //e.stopPropagation();
12571             return;
12572         }
12573         
12574         if(this.tickable){
12575             this.onTickableFooterButtonClick(e, false, false);
12576         }
12577
12578         this.collapse();
12579         
12580     },
12581
12582     /**
12583      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12584      */
12585     expand : function(){
12586        
12587         if(this.isExpanded() || !this.hasFocus){
12588             return;
12589         }
12590         
12591         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12592         this.list.setWidth(lw);
12593         
12594         
12595          Roo.log('expand');
12596         
12597         this.list.show();
12598         
12599         this.restrictHeight();
12600         
12601         if(this.tickable){
12602             
12603             this.tickItems = Roo.apply([], this.item);
12604             
12605             this.okBtn.show();
12606             this.cancelBtn.show();
12607             this.trigger.hide();
12608             
12609             if(this.editable){
12610                 this.tickableInputEl().focus();
12611             }
12612             
12613         }
12614         
12615         Roo.get(document).on('mousedown', this.collapseIf, this);
12616         Roo.get(document).on('mousewheel', this.collapseIf, this);
12617         if (!this.editable) {
12618             Roo.get(document).on('keydown', this.listKeyPress, this);
12619         }
12620         
12621         this.fireEvent('expand', this);
12622     },
12623
12624     // private
12625     // Implements the default empty TriggerField.onTriggerClick function
12626     onTriggerClick : function(e)
12627     {
12628         Roo.log('trigger click');
12629         
12630         if(this.disabled || !this.triggerList){
12631             return;
12632         }
12633         
12634         this.page = 0;
12635         this.loadNext = false;
12636         
12637         if(this.isExpanded()){
12638             this.collapse();
12639             if (!this.blockFocus) {
12640                 this.inputEl().focus();
12641             }
12642             
12643         }else {
12644             this.hasFocus = true;
12645             if(this.triggerAction == 'all') {
12646                 this.doQuery(this.allQuery, true);
12647             } else {
12648                 this.doQuery(this.getRawValue());
12649             }
12650             if (!this.blockFocus) {
12651                 this.inputEl().focus();
12652             }
12653         }
12654     },
12655     
12656     onTickableTriggerClick : function(e)
12657     {
12658         if(this.disabled){
12659             return;
12660         }
12661         
12662         this.page = 0;
12663         this.loadNext = false;
12664         this.hasFocus = true;
12665         
12666         if(this.triggerAction == 'all') {
12667             this.doQuery(this.allQuery, true);
12668         } else {
12669             this.doQuery(this.getRawValue());
12670         }
12671     },
12672     
12673     onSearchFieldClick : function(e)
12674     {
12675         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12676             this.onTickableFooterButtonClick(e, false, false);
12677             return;
12678         }
12679         
12680         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12681             return;
12682         }
12683         
12684         this.page = 0;
12685         this.loadNext = false;
12686         this.hasFocus = true;
12687         
12688         if(this.triggerAction == 'all') {
12689             this.doQuery(this.allQuery, true);
12690         } else {
12691             this.doQuery(this.getRawValue());
12692         }
12693     },
12694     
12695     listKeyPress : function(e)
12696     {
12697         //Roo.log('listkeypress');
12698         // scroll to first matching element based on key pres..
12699         if (e.isSpecialKey()) {
12700             return false;
12701         }
12702         var k = String.fromCharCode(e.getKey()).toUpperCase();
12703         //Roo.log(k);
12704         var match  = false;
12705         var csel = this.view.getSelectedNodes();
12706         var cselitem = false;
12707         if (csel.length) {
12708             var ix = this.view.indexOf(csel[0]);
12709             cselitem  = this.store.getAt(ix);
12710             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12711                 cselitem = false;
12712             }
12713             
12714         }
12715         
12716         this.store.each(function(v) { 
12717             if (cselitem) {
12718                 // start at existing selection.
12719                 if (cselitem.id == v.id) {
12720                     cselitem = false;
12721                 }
12722                 return true;
12723             }
12724                 
12725             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12726                 match = this.store.indexOf(v);
12727                 return false;
12728             }
12729             return true;
12730         }, this);
12731         
12732         if (match === false) {
12733             return true; // no more action?
12734         }
12735         // scroll to?
12736         this.view.select(match);
12737         var sn = Roo.get(this.view.getSelectedNodes()[0])
12738         sn.scrollIntoView(sn.dom.parentNode, false);
12739     },
12740     
12741     onViewScroll : function(e, t){
12742         
12743         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){
12744             return;
12745         }
12746         
12747         this.hasQuery = true;
12748         
12749         this.loading = this.list.select('.loading', true).first();
12750         
12751         if(this.loading === null){
12752             this.list.createChild({
12753                 tag: 'div',
12754                 cls: 'loading select2-more-results select2-active',
12755                 html: 'Loading more results...'
12756             })
12757             
12758             this.loading = this.list.select('.loading', true).first();
12759             
12760             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12761             
12762             this.loading.hide();
12763         }
12764         
12765         this.loading.show();
12766         
12767         var _combo = this;
12768         
12769         this.page++;
12770         this.loadNext = true;
12771         
12772         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12773         
12774         return;
12775     },
12776     
12777     addItem : function(o)
12778     {   
12779         var dv = ''; // display value
12780         
12781         if (this.displayField) {
12782             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12783         } else {
12784             // this is an error condition!!!
12785             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12786         }
12787         
12788         if(!dv.length){
12789             return;
12790         }
12791         
12792         var choice = this.choices.createChild({
12793             tag: 'li',
12794             cls: 'select2-search-choice',
12795             cn: [
12796                 {
12797                     tag: 'div',
12798                     html: dv
12799                 },
12800                 {
12801                     tag: 'a',
12802                     href: '#',
12803                     cls: 'select2-search-choice-close',
12804                     tabindex: '-1'
12805                 }
12806             ]
12807             
12808         }, this.searchField);
12809         
12810         var close = choice.select('a.select2-search-choice-close', true).first()
12811         
12812         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12813         
12814         this.item.push(o);
12815         
12816         this.lastData = o;
12817         
12818         this.syncValue();
12819         
12820         this.inputEl().dom.value = '';
12821         
12822         this.validate();
12823     },
12824     
12825     onRemoveItem : function(e, _self, o)
12826     {
12827         e.preventDefault();
12828         
12829         this.lastItem = Roo.apply([], this.item);
12830         
12831         var index = this.item.indexOf(o.data) * 1;
12832         
12833         if( index < 0){
12834             Roo.log('not this item?!');
12835             return;
12836         }
12837         
12838         this.item.splice(index, 1);
12839         o.item.remove();
12840         
12841         this.syncValue();
12842         
12843         this.fireEvent('remove', this, e);
12844         
12845         this.validate();
12846         
12847     },
12848     
12849     syncValue : function()
12850     {
12851         if(!this.item.length){
12852             this.clearValue();
12853             return;
12854         }
12855             
12856         var value = [];
12857         var _this = this;
12858         Roo.each(this.item, function(i){
12859             if(_this.valueField){
12860                 value.push(i[_this.valueField]);
12861                 return;
12862             }
12863
12864             value.push(i);
12865         });
12866
12867         this.value = value.join(',');
12868
12869         if(this.hiddenField){
12870             this.hiddenField.dom.value = this.value;
12871         }
12872         
12873         this.store.fireEvent("datachanged", this.store);
12874     },
12875     
12876     clearItem : function()
12877     {
12878         if(!this.multiple){
12879             return;
12880         }
12881         
12882         this.item = [];
12883         
12884         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12885            c.remove();
12886         });
12887         
12888         this.syncValue();
12889         
12890         this.validate();
12891     },
12892     
12893     inputEl: function ()
12894     {
12895         if(Roo.isTouch && this.mobileTouchView){
12896             return this.el.select('input.form-control',true).first();
12897         }
12898         
12899         if(this.tickable){
12900             return this.searchField;
12901         }
12902         
12903         return this.el.select('input.form-control',true).first();
12904     },
12905     
12906     
12907     onTickableFooterButtonClick : function(e, btn, el)
12908     {
12909         e.preventDefault();
12910         
12911         this.lastItem = Roo.apply([], this.item);
12912         
12913         if(btn && btn.name == 'cancel'){
12914             this.tickItems = Roo.apply([], this.item);
12915             this.collapse();
12916             return;
12917         }
12918         
12919         this.clearItem();
12920         
12921         var _this = this;
12922         
12923         Roo.each(this.tickItems, function(o){
12924             _this.addItem(o);
12925         });
12926         
12927         this.collapse();
12928         
12929     },
12930     
12931     validate : function()
12932     {
12933         var v = this.getRawValue();
12934         
12935         if(this.multiple){
12936             v = this.getValue();
12937         }
12938         
12939         if(this.disabled || this.allowBlank || v.length){
12940             this.markValid();
12941             return true;
12942         }
12943         
12944         this.markInvalid();
12945         return false;
12946     },
12947     
12948     tickableInputEl : function()
12949     {
12950         if(!this.tickable || !this.editable){
12951             return this.inputEl();
12952         }
12953         
12954         return this.inputEl().select('.select2-search-field-input', true).first();
12955     },
12956     
12957     
12958     getAutoCreateTouchView : function()
12959     {
12960         var id = Roo.id();
12961         
12962         var cfg = {
12963             cls: 'form-group' //input-group
12964         };
12965         
12966         var input =  {
12967             tag: 'input',
12968             id : id,
12969             type : this.inputType,
12970             cls : 'form-control x-combo-noedit',
12971             autocomplete: 'new-password',
12972             placeholder : this.placeholder || '',
12973             readonly : true
12974         };
12975         
12976         if (this.name) {
12977             input.name = this.name;
12978         }
12979         
12980         if (this.size) {
12981             input.cls += ' input-' + this.size;
12982         }
12983         
12984         if (this.disabled) {
12985             input.disabled = true;
12986         }
12987         
12988         var inputblock = {
12989             cls : '',
12990             cn : [
12991                 input
12992             ]
12993         };
12994         
12995         if(this.before){
12996             inputblock.cls += ' input-group';
12997             
12998             inputblock.cn.unshift({
12999                 tag :'span',
13000                 cls : 'input-group-addon',
13001                 html : this.before
13002             });
13003         }
13004         
13005         if(this.removable && !this.multiple){
13006             inputblock.cls += ' roo-removable';
13007             
13008             inputblock.cn.push({
13009                 tag: 'button',
13010                 html : 'x',
13011                 cls : 'roo-combo-removable-btn close'
13012             });
13013         }
13014
13015         if(this.hasFeedback && !this.allowBlank){
13016             
13017             inputblock.cls += ' has-feedback';
13018             
13019             inputblock.cn.push({
13020                 tag: 'span',
13021                 cls: 'glyphicon form-control-feedback'
13022             });
13023             
13024         }
13025         
13026         if (this.after) {
13027             
13028             inputblock.cls += (this.before) ? '' : ' input-group';
13029             
13030             inputblock.cn.push({
13031                 tag :'span',
13032                 cls : 'input-group-addon',
13033                 html : this.after
13034             });
13035         }
13036
13037         var box = {
13038             tag: 'div',
13039             cn: [
13040                 {
13041                     tag: 'input',
13042                     type : 'hidden',
13043                     cls: 'form-hidden-field'
13044                 },
13045                 inputblock
13046             ]
13047             
13048         };
13049         
13050         if(this.multiple){
13051             box = {
13052                 tag: 'div',
13053                 cn: [
13054                     {
13055                         tag: 'input',
13056                         type : 'hidden',
13057                         cls: 'form-hidden-field'
13058                     },
13059                     {
13060                         tag: 'ul',
13061                         cls: 'select2-choices',
13062                         cn:[
13063                             {
13064                                 tag: 'li',
13065                                 cls: 'select2-search-field',
13066                                 cn: [
13067
13068                                     inputblock
13069                                 ]
13070                             }
13071                         ]
13072                     }
13073                 ]
13074             }
13075         };
13076         
13077         var combobox = {
13078             cls: 'select2-container input-group',
13079             cn: [
13080                 box
13081             ]
13082         };
13083         
13084         if(this.multiple){
13085             combobox.cls += ' select2-container-multi';
13086         }
13087         
13088         var align = this.labelAlign || this.parentLabelAlign();
13089         
13090         cfg.cn = combobox;
13091         
13092         if(this.fieldLabel.length){
13093             
13094             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13095             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13096             
13097             cfg.cn = [
13098                 {
13099                     tag: 'label',
13100                     cls : 'control-label ' + lw,
13101                     html : this.fieldLabel
13102
13103                 },
13104                 {
13105                     cls : cw, 
13106                     cn: [
13107                         combobox
13108                     ]
13109                 }
13110             ];
13111         }
13112         
13113         var settings = this;
13114         
13115         ['xs','sm','md','lg'].map(function(size){
13116             if (settings[size]) {
13117                 cfg.cls += ' col-' + size + '-' + settings[size];
13118             }
13119         });
13120         
13121         return cfg;
13122     },
13123     
13124     initTouchView : function()
13125     {
13126         this.renderTouchView();
13127         
13128         this.touchViewEl.on('scroll', function(){
13129             this.el.dom.scrollTop = 0;
13130         }, this);
13131         
13132         this.inputEl().on("click", this.showTouchView, this);
13133         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13134         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13135         
13136         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13137         
13138         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13139         this.store.on('load', this.onTouchViewLoad, this);
13140         this.store.on('loadexception', this.onTouchViewLoadException, this);
13141         
13142         if(this.hiddenName){
13143             
13144             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13145             
13146             this.hiddenField.dom.value =
13147                 this.hiddenValue !== undefined ? this.hiddenValue :
13148                 this.value !== undefined ? this.value : '';
13149         
13150             this.el.dom.removeAttribute('name');
13151             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13152         }
13153         
13154         if(this.multiple){
13155             this.choices = this.el.select('ul.select2-choices', true).first();
13156             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13157         }
13158         
13159         if(this.removable && !this.multiple){
13160             var close = this.closeTriggerEl();
13161             if(close){
13162                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13163                 close.on('click', this.removeBtnClick, this, close);
13164             }
13165         }
13166         
13167         return;
13168         
13169         
13170     },
13171     
13172     renderTouchView : function()
13173     {
13174         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13175         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13176         
13177         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13178         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13179         
13180         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13181         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13182         this.touchViewBodyEl.setStyle('overflow', 'auto');
13183         
13184         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13185         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13186         
13187         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13188         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13189         
13190     },
13191     
13192     showTouchView : function()
13193     {
13194         this.touchViewHeaderEl.hide();
13195
13196         if(this.fieldLabel.length){
13197             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13198             this.touchViewHeaderEl.show();
13199         }
13200
13201         this.touchViewEl.show();
13202
13203         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13204         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13205
13206         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13207
13208         if(this.fieldLabel.length){
13209             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13210         }
13211         
13212         this.touchViewBodyEl.setHeight(bodyHeight);
13213
13214         if(this.animate){
13215             var _this = this;
13216             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13217         }else{
13218             this.touchViewEl.addClass('in');
13219         }
13220
13221         this.doTouchViewQuery();
13222         
13223     },
13224     
13225     hideTouchView : function()
13226     {
13227         this.touchViewEl.removeClass('in');
13228
13229         if(this.animate){
13230             var _this = this;
13231             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13232         }else{
13233             this.touchViewEl.setStyle('display', 'none');
13234         }
13235         
13236     },
13237     
13238     setTouchViewValue : function()
13239     {
13240         if(this.multiple){
13241             this.clearItem();
13242         
13243             var _this = this;
13244
13245             Roo.each(this.tickItems, function(o){
13246                 this.addItem(o);
13247             }, this);
13248         }
13249         
13250         this.hideTouchView();
13251     },
13252     
13253     doTouchViewQuery : function()
13254     {
13255         var qe = {
13256             query: '',
13257             forceAll: true,
13258             combo: this,
13259             cancel:false
13260         };
13261         
13262         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13263             return false;
13264         }
13265         
13266         if(!this.alwaysQuery || this.mode == 'local'){
13267             this.onTouchViewLoad();
13268             return;
13269         }
13270         
13271         this.store.load();
13272     },
13273     
13274     onTouchViewBeforeLoad : function(combo,opts)
13275     {
13276         return;
13277     },
13278
13279     // private
13280     onTouchViewLoad : function()
13281     {
13282         if(this.store.getCount() < 1){
13283             this.onTouchViewEmptyResults();
13284             return;
13285         }
13286         
13287         this.clearTouchView();
13288         
13289         var rawValue = this.getRawValue();
13290         
13291         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13292         
13293         this.tickItems = [];
13294         
13295         this.store.data.each(function(d, rowIndex){
13296             var row = this.touchViewListGroup.createChild(template);
13297             
13298             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13299                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13300             }
13301             
13302             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13303                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13304             }
13305             
13306             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13307                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13308                 this.tickItems.push(d.data);
13309             }
13310             
13311             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13312             
13313         }, this);
13314         
13315         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13316         
13317         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13318
13319         if(this.fieldLabel.length){
13320             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13321         }
13322
13323         var listHeight = this.touchViewListGroup.getHeight();
13324         
13325         if(firstChecked && listHeight > bodyHeight){
13326             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13327         }
13328         
13329     },
13330     
13331     onTouchViewLoadException : function()
13332     {
13333         this.hideTouchView();
13334     },
13335     
13336     onTouchViewEmptyResults : function()
13337     {
13338         this.clearTouchView();
13339         
13340         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13341         
13342         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13343         
13344     },
13345     
13346     clearTouchView : function()
13347     {
13348         this.touchViewListGroup.dom.innerHTML = '';
13349     },
13350     
13351     onTouchViewClick : function(e, el, o)
13352     {
13353         e.preventDefault();
13354         
13355         var row = o.row;
13356         var rowIndex = o.rowIndex;
13357         
13358         var r = this.store.getAt(rowIndex);
13359         
13360         if(!this.multiple){
13361             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13362                 c.dom.removeAttribute('checked');
13363             }, this);
13364             
13365             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13366         
13367             this.setFromData(r.data);
13368             
13369             var close = this.closeTriggerEl();
13370         
13371             if(close){
13372                 close.show();
13373             }
13374
13375             this.hideTouchView();
13376             
13377             this.fireEvent('select', this, r, rowIndex);
13378             
13379             return;
13380         }
13381         
13382         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13383             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13384             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13385             return;
13386         }
13387         
13388         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13389         this.addItem(r.data);
13390         this.tickItems.push(r.data);
13391         
13392     }
13393     
13394
13395     /** 
13396     * @cfg {Boolean} grow 
13397     * @hide 
13398     */
13399     /** 
13400     * @cfg {Number} growMin 
13401     * @hide 
13402     */
13403     /** 
13404     * @cfg {Number} growMax 
13405     * @hide 
13406     */
13407     /**
13408      * @hide
13409      * @method autoSize
13410      */
13411 });
13412
13413 Roo.apply(Roo.bootstrap.ComboBox,  {
13414     
13415     header : {
13416         tag: 'div',
13417         cls: 'modal-header',
13418         cn: [
13419             {
13420                 tag: 'h4',
13421                 cls: 'modal-title'
13422             }
13423         ]
13424     },
13425     
13426     body : {
13427         tag: 'div',
13428         cls: 'modal-body',
13429         cn: [
13430             {
13431                 tag: 'ul',
13432                 cls: 'list-group'
13433             }
13434         ]
13435     },
13436     
13437     listItemRadio : {
13438         tag: 'li',
13439         cls: 'list-group-item',
13440         cn: [
13441             {
13442                 tag: 'span',
13443                 cls: 'roo-combobox-list-group-item-value'
13444             },
13445             {
13446                 tag: 'div',
13447                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13448                 cn: [
13449                     {
13450                         tag: 'input',
13451                         type: 'radio'
13452                     },
13453                     {
13454                         tag: 'label'
13455                     }
13456                 ]
13457             }
13458         ]
13459     },
13460     
13461     listItemCheckbox : {
13462         tag: 'li',
13463         cls: 'list-group-item',
13464         cn: [
13465             {
13466                 tag: 'span',
13467                 cls: 'roo-combobox-list-group-item-value'
13468             },
13469             {
13470                 tag: 'div',
13471                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13472                 cn: [
13473                     {
13474                         tag: 'input',
13475                         type: 'checkbox'
13476                     },
13477                     {
13478                         tag: 'label'
13479                     }
13480                 ]
13481             }
13482         ]
13483     },
13484     
13485     emptyResult : {
13486         tag: 'div',
13487         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13488     },
13489     
13490     footer : {
13491         tag: 'div',
13492         cls: 'modal-footer',
13493         cn: [
13494             {
13495                 tag: 'div',
13496                 cls: 'row',
13497                 cn: [
13498                     {
13499                         tag: 'div',
13500                         cls: 'col-xs-6 text-left',
13501                         cn: {
13502                             tag: 'button',
13503                             cls: 'btn btn-danger roo-touch-view-cancel',
13504                             html: 'Cancel'
13505                         }
13506                     },
13507                     {
13508                         tag: 'div',
13509                         cls: 'col-xs-6 text-right',
13510                         cn: {
13511                             tag: 'button',
13512                             cls: 'btn btn-success roo-touch-view-ok',
13513                             html: 'OK'
13514                         }
13515                     }
13516                 ]
13517             }
13518         ]
13519         
13520     }
13521 });
13522
13523 Roo.apply(Roo.bootstrap.ComboBox,  {
13524     
13525     touchViewTemplate : {
13526         tag: 'div',
13527         cls: 'modal fade roo-combobox-touch-view',
13528         cn: [
13529             {
13530                 tag: 'div',
13531                 cls: 'modal-dialog',
13532                 cn: [
13533                     {
13534                         tag: 'div',
13535                         cls: 'modal-content',
13536                         cn: [
13537                             Roo.bootstrap.ComboBox.header,
13538                             Roo.bootstrap.ComboBox.body,
13539                             Roo.bootstrap.ComboBox.footer
13540                         ]
13541                     }
13542                 ]
13543             }
13544         ]
13545     }
13546 });/*
13547  * Based on:
13548  * Ext JS Library 1.1.1
13549  * Copyright(c) 2006-2007, Ext JS, LLC.
13550  *
13551  * Originally Released Under LGPL - original licence link has changed is not relivant.
13552  *
13553  * Fork - LGPL
13554  * <script type="text/javascript">
13555  */
13556
13557 /**
13558  * @class Roo.View
13559  * @extends Roo.util.Observable
13560  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13561  * This class also supports single and multi selection modes. <br>
13562  * Create a data model bound view:
13563  <pre><code>
13564  var store = new Roo.data.Store(...);
13565
13566  var view = new Roo.View({
13567     el : "my-element",
13568     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13569  
13570     singleSelect: true,
13571     selectedClass: "ydataview-selected",
13572     store: store
13573  });
13574
13575  // listen for node click?
13576  view.on("click", function(vw, index, node, e){
13577  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13578  });
13579
13580  // load XML data
13581  dataModel.load("foobar.xml");
13582  </code></pre>
13583  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13584  * <br><br>
13585  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13586  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13587  * 
13588  * Note: old style constructor is still suported (container, template, config)
13589  * 
13590  * @constructor
13591  * Create a new View
13592  * @param {Object} config The config object
13593  * 
13594  */
13595 Roo.View = function(config, depreciated_tpl, depreciated_config){
13596     
13597     this.parent = false;
13598     
13599     if (typeof(depreciated_tpl) == 'undefined') {
13600         // new way.. - universal constructor.
13601         Roo.apply(this, config);
13602         this.el  = Roo.get(this.el);
13603     } else {
13604         // old format..
13605         this.el  = Roo.get(config);
13606         this.tpl = depreciated_tpl;
13607         Roo.apply(this, depreciated_config);
13608     }
13609     this.wrapEl  = this.el.wrap().wrap();
13610     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13611     
13612     
13613     if(typeof(this.tpl) == "string"){
13614         this.tpl = new Roo.Template(this.tpl);
13615     } else {
13616         // support xtype ctors..
13617         this.tpl = new Roo.factory(this.tpl, Roo);
13618     }
13619     
13620     
13621     this.tpl.compile();
13622     
13623     /** @private */
13624     this.addEvents({
13625         /**
13626          * @event beforeclick
13627          * Fires before a click is processed. Returns false to cancel the default action.
13628          * @param {Roo.View} this
13629          * @param {Number} index The index of the target node
13630          * @param {HTMLElement} node The target node
13631          * @param {Roo.EventObject} e The raw event object
13632          */
13633             "beforeclick" : true,
13634         /**
13635          * @event click
13636          * Fires when a template node is clicked.
13637          * @param {Roo.View} this
13638          * @param {Number} index The index of the target node
13639          * @param {HTMLElement} node The target node
13640          * @param {Roo.EventObject} e The raw event object
13641          */
13642             "click" : true,
13643         /**
13644          * @event dblclick
13645          * Fires when a template node is double clicked.
13646          * @param {Roo.View} this
13647          * @param {Number} index The index of the target node
13648          * @param {HTMLElement} node The target node
13649          * @param {Roo.EventObject} e The raw event object
13650          */
13651             "dblclick" : true,
13652         /**
13653          * @event contextmenu
13654          * Fires when a template node is right clicked.
13655          * @param {Roo.View} this
13656          * @param {Number} index The index of the target node
13657          * @param {HTMLElement} node The target node
13658          * @param {Roo.EventObject} e The raw event object
13659          */
13660             "contextmenu" : true,
13661         /**
13662          * @event selectionchange
13663          * Fires when the selected nodes change.
13664          * @param {Roo.View} this
13665          * @param {Array} selections Array of the selected nodes
13666          */
13667             "selectionchange" : true,
13668     
13669         /**
13670          * @event beforeselect
13671          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13672          * @param {Roo.View} this
13673          * @param {HTMLElement} node The node to be selected
13674          * @param {Array} selections Array of currently selected nodes
13675          */
13676             "beforeselect" : true,
13677         /**
13678          * @event preparedata
13679          * Fires on every row to render, to allow you to change the data.
13680          * @param {Roo.View} this
13681          * @param {Object} data to be rendered (change this)
13682          */
13683           "preparedata" : true
13684           
13685           
13686         });
13687
13688
13689
13690     this.el.on({
13691         "click": this.onClick,
13692         "dblclick": this.onDblClick,
13693         "contextmenu": this.onContextMenu,
13694         scope:this
13695     });
13696
13697     this.selections = [];
13698     this.nodes = [];
13699     this.cmp = new Roo.CompositeElementLite([]);
13700     if(this.store){
13701         this.store = Roo.factory(this.store, Roo.data);
13702         this.setStore(this.store, true);
13703     }
13704     
13705     if ( this.footer && this.footer.xtype) {
13706            
13707          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13708         
13709         this.footer.dataSource = this.store
13710         this.footer.container = fctr;
13711         this.footer = Roo.factory(this.footer, Roo);
13712         fctr.insertFirst(this.el);
13713         
13714         // this is a bit insane - as the paging toolbar seems to detach the el..
13715 //        dom.parentNode.parentNode.parentNode
13716          // they get detached?
13717     }
13718     
13719     
13720     Roo.View.superclass.constructor.call(this);
13721     
13722     
13723 };
13724
13725 Roo.extend(Roo.View, Roo.util.Observable, {
13726     
13727      /**
13728      * @cfg {Roo.data.Store} store Data store to load data from.
13729      */
13730     store : false,
13731     
13732     /**
13733      * @cfg {String|Roo.Element} el The container element.
13734      */
13735     el : '',
13736     
13737     /**
13738      * @cfg {String|Roo.Template} tpl The template used by this View 
13739      */
13740     tpl : false,
13741     /**
13742      * @cfg {String} dataName the named area of the template to use as the data area
13743      *                          Works with domtemplates roo-name="name"
13744      */
13745     dataName: false,
13746     /**
13747      * @cfg {String} selectedClass The css class to add to selected nodes
13748      */
13749     selectedClass : "x-view-selected",
13750      /**
13751      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13752      */
13753     emptyText : "",
13754     
13755     /**
13756      * @cfg {String} text to display on mask (default Loading)
13757      */
13758     mask : false,
13759     /**
13760      * @cfg {Boolean} multiSelect Allow multiple selection
13761      */
13762     multiSelect : false,
13763     /**
13764      * @cfg {Boolean} singleSelect Allow single selection
13765      */
13766     singleSelect:  false,
13767     
13768     /**
13769      * @cfg {Boolean} toggleSelect - selecting 
13770      */
13771     toggleSelect : false,
13772     
13773     /**
13774      * @cfg {Boolean} tickable - selecting 
13775      */
13776     tickable : false,
13777     
13778     /**
13779      * Returns the element this view is bound to.
13780      * @return {Roo.Element}
13781      */
13782     getEl : function(){
13783         return this.wrapEl;
13784     },
13785     
13786     
13787
13788     /**
13789      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13790      */
13791     refresh : function(){
13792         //Roo.log('refresh');
13793         var t = this.tpl;
13794         
13795         // if we are using something like 'domtemplate', then
13796         // the what gets used is:
13797         // t.applySubtemplate(NAME, data, wrapping data..)
13798         // the outer template then get' applied with
13799         //     the store 'extra data'
13800         // and the body get's added to the
13801         //      roo-name="data" node?
13802         //      <span class='roo-tpl-{name}'></span> ?????
13803         
13804         
13805         
13806         this.clearSelections();
13807         this.el.update("");
13808         var html = [];
13809         var records = this.store.getRange();
13810         if(records.length < 1) {
13811             
13812             // is this valid??  = should it render a template??
13813             
13814             this.el.update(this.emptyText);
13815             return;
13816         }
13817         var el = this.el;
13818         if (this.dataName) {
13819             this.el.update(t.apply(this.store.meta)); //????
13820             el = this.el.child('.roo-tpl-' + this.dataName);
13821         }
13822         
13823         for(var i = 0, len = records.length; i < len; i++){
13824             var data = this.prepareData(records[i].data, i, records[i]);
13825             this.fireEvent("preparedata", this, data, i, records[i]);
13826             
13827             var d = Roo.apply({}, data);
13828             
13829             if(this.tickable){
13830                 Roo.apply(d, {'roo-id' : Roo.id()});
13831                 
13832                 var _this = this;
13833             
13834                 Roo.each(this.parent.item, function(item){
13835                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13836                         return;
13837                     }
13838                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13839                 });
13840             }
13841             
13842             html[html.length] = Roo.util.Format.trim(
13843                 this.dataName ?
13844                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13845                     t.apply(d)
13846             );
13847         }
13848         
13849         
13850         
13851         el.update(html.join(""));
13852         this.nodes = el.dom.childNodes;
13853         this.updateIndexes(0);
13854     },
13855     
13856
13857     /**
13858      * Function to override to reformat the data that is sent to
13859      * the template for each node.
13860      * DEPRICATED - use the preparedata event handler.
13861      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13862      * a JSON object for an UpdateManager bound view).
13863      */
13864     prepareData : function(data, index, record)
13865     {
13866         this.fireEvent("preparedata", this, data, index, record);
13867         return data;
13868     },
13869
13870     onUpdate : function(ds, record){
13871         // Roo.log('on update');   
13872         this.clearSelections();
13873         var index = this.store.indexOf(record);
13874         var n = this.nodes[index];
13875         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13876         n.parentNode.removeChild(n);
13877         this.updateIndexes(index, index);
13878     },
13879
13880     
13881     
13882 // --------- FIXME     
13883     onAdd : function(ds, records, index)
13884     {
13885         //Roo.log(['on Add', ds, records, index] );        
13886         this.clearSelections();
13887         if(this.nodes.length == 0){
13888             this.refresh();
13889             return;
13890         }
13891         var n = this.nodes[index];
13892         for(var i = 0, len = records.length; i < len; i++){
13893             var d = this.prepareData(records[i].data, i, records[i]);
13894             if(n){
13895                 this.tpl.insertBefore(n, d);
13896             }else{
13897                 
13898                 this.tpl.append(this.el, d);
13899             }
13900         }
13901         this.updateIndexes(index);
13902     },
13903
13904     onRemove : function(ds, record, index){
13905        // Roo.log('onRemove');
13906         this.clearSelections();
13907         var el = this.dataName  ?
13908             this.el.child('.roo-tpl-' + this.dataName) :
13909             this.el; 
13910         
13911         el.dom.removeChild(this.nodes[index]);
13912         this.updateIndexes(index);
13913     },
13914
13915     /**
13916      * Refresh an individual node.
13917      * @param {Number} index
13918      */
13919     refreshNode : function(index){
13920         this.onUpdate(this.store, this.store.getAt(index));
13921     },
13922
13923     updateIndexes : function(startIndex, endIndex){
13924         var ns = this.nodes;
13925         startIndex = startIndex || 0;
13926         endIndex = endIndex || ns.length - 1;
13927         for(var i = startIndex; i <= endIndex; i++){
13928             ns[i].nodeIndex = i;
13929         }
13930     },
13931
13932     /**
13933      * Changes the data store this view uses and refresh the view.
13934      * @param {Store} store
13935      */
13936     setStore : function(store, initial){
13937         if(!initial && this.store){
13938             this.store.un("datachanged", this.refresh);
13939             this.store.un("add", this.onAdd);
13940             this.store.un("remove", this.onRemove);
13941             this.store.un("update", this.onUpdate);
13942             this.store.un("clear", this.refresh);
13943             this.store.un("beforeload", this.onBeforeLoad);
13944             this.store.un("load", this.onLoad);
13945             this.store.un("loadexception", this.onLoad);
13946         }
13947         if(store){
13948           
13949             store.on("datachanged", this.refresh, this);
13950             store.on("add", this.onAdd, this);
13951             store.on("remove", this.onRemove, this);
13952             store.on("update", this.onUpdate, this);
13953             store.on("clear", this.refresh, this);
13954             store.on("beforeload", this.onBeforeLoad, this);
13955             store.on("load", this.onLoad, this);
13956             store.on("loadexception", this.onLoad, this);
13957         }
13958         
13959         if(store){
13960             this.refresh();
13961         }
13962     },
13963     /**
13964      * onbeforeLoad - masks the loading area.
13965      *
13966      */
13967     onBeforeLoad : function(store,opts)
13968     {
13969          //Roo.log('onBeforeLoad');   
13970         if (!opts.add) {
13971             this.el.update("");
13972         }
13973         this.el.mask(this.mask ? this.mask : "Loading" ); 
13974     },
13975     onLoad : function ()
13976     {
13977         this.el.unmask();
13978     },
13979     
13980
13981     /**
13982      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13983      * @param {HTMLElement} node
13984      * @return {HTMLElement} The template node
13985      */
13986     findItemFromChild : function(node){
13987         var el = this.dataName  ?
13988             this.el.child('.roo-tpl-' + this.dataName,true) :
13989             this.el.dom; 
13990         
13991         if(!node || node.parentNode == el){
13992                     return node;
13993             }
13994             var p = node.parentNode;
13995             while(p && p != el){
13996             if(p.parentNode == el){
13997                 return p;
13998             }
13999             p = p.parentNode;
14000         }
14001             return null;
14002     },
14003
14004     /** @ignore */
14005     onClick : function(e){
14006         var item = this.findItemFromChild(e.getTarget());
14007         if(item){
14008             var index = this.indexOf(item);
14009             if(this.onItemClick(item, index, e) !== false){
14010                 this.fireEvent("click", this, index, item, e);
14011             }
14012         }else{
14013             this.clearSelections();
14014         }
14015     },
14016
14017     /** @ignore */
14018     onContextMenu : function(e){
14019         var item = this.findItemFromChild(e.getTarget());
14020         if(item){
14021             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14022         }
14023     },
14024
14025     /** @ignore */
14026     onDblClick : function(e){
14027         var item = this.findItemFromChild(e.getTarget());
14028         if(item){
14029             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14030         }
14031     },
14032
14033     onItemClick : function(item, index, e)
14034     {
14035         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14036             return false;
14037         }
14038         if (this.toggleSelect) {
14039             var m = this.isSelected(item) ? 'unselect' : 'select';
14040             //Roo.log(m);
14041             var _t = this;
14042             _t[m](item, true, false);
14043             return true;
14044         }
14045         if(this.multiSelect || this.singleSelect){
14046             if(this.multiSelect && e.shiftKey && this.lastSelection){
14047                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14048             }else{
14049                 this.select(item, this.multiSelect && e.ctrlKey);
14050                 this.lastSelection = item;
14051             }
14052             
14053             if(!this.tickable){
14054                 e.preventDefault();
14055             }
14056             
14057         }
14058         return true;
14059     },
14060
14061     /**
14062      * Get the number of selected nodes.
14063      * @return {Number}
14064      */
14065     getSelectionCount : function(){
14066         return this.selections.length;
14067     },
14068
14069     /**
14070      * Get the currently selected nodes.
14071      * @return {Array} An array of HTMLElements
14072      */
14073     getSelectedNodes : function(){
14074         return this.selections;
14075     },
14076
14077     /**
14078      * Get the indexes of the selected nodes.
14079      * @return {Array}
14080      */
14081     getSelectedIndexes : function(){
14082         var indexes = [], s = this.selections;
14083         for(var i = 0, len = s.length; i < len; i++){
14084             indexes.push(s[i].nodeIndex);
14085         }
14086         return indexes;
14087     },
14088
14089     /**
14090      * Clear all selections
14091      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14092      */
14093     clearSelections : function(suppressEvent){
14094         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14095             this.cmp.elements = this.selections;
14096             this.cmp.removeClass(this.selectedClass);
14097             this.selections = [];
14098             if(!suppressEvent){
14099                 this.fireEvent("selectionchange", this, this.selections);
14100             }
14101         }
14102     },
14103
14104     /**
14105      * Returns true if the passed node is selected
14106      * @param {HTMLElement/Number} node The node or node index
14107      * @return {Boolean}
14108      */
14109     isSelected : function(node){
14110         var s = this.selections;
14111         if(s.length < 1){
14112             return false;
14113         }
14114         node = this.getNode(node);
14115         return s.indexOf(node) !== -1;
14116     },
14117
14118     /**
14119      * Selects nodes.
14120      * @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
14121      * @param {Boolean} keepExisting (optional) true to keep existing selections
14122      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14123      */
14124     select : function(nodeInfo, keepExisting, suppressEvent){
14125         if(nodeInfo instanceof Array){
14126             if(!keepExisting){
14127                 this.clearSelections(true);
14128             }
14129             for(var i = 0, len = nodeInfo.length; i < len; i++){
14130                 this.select(nodeInfo[i], true, true);
14131             }
14132             return;
14133         } 
14134         var node = this.getNode(nodeInfo);
14135         if(!node || this.isSelected(node)){
14136             return; // already selected.
14137         }
14138         if(!keepExisting){
14139             this.clearSelections(true);
14140         }
14141         
14142         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14143             Roo.fly(node).addClass(this.selectedClass);
14144             this.selections.push(node);
14145             if(!suppressEvent){
14146                 this.fireEvent("selectionchange", this, this.selections);
14147             }
14148         }
14149         
14150         
14151     },
14152       /**
14153      * Unselects nodes.
14154      * @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
14155      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14156      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14157      */
14158     unselect : function(nodeInfo, keepExisting, suppressEvent)
14159     {
14160         if(nodeInfo instanceof Array){
14161             Roo.each(this.selections, function(s) {
14162                 this.unselect(s, nodeInfo);
14163             }, this);
14164             return;
14165         }
14166         var node = this.getNode(nodeInfo);
14167         if(!node || !this.isSelected(node)){
14168             //Roo.log("not selected");
14169             return; // not selected.
14170         }
14171         // fireevent???
14172         var ns = [];
14173         Roo.each(this.selections, function(s) {
14174             if (s == node ) {
14175                 Roo.fly(node).removeClass(this.selectedClass);
14176
14177                 return;
14178             }
14179             ns.push(s);
14180         },this);
14181         
14182         this.selections= ns;
14183         this.fireEvent("selectionchange", this, this.selections);
14184     },
14185
14186     /**
14187      * Gets a template node.
14188      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14189      * @return {HTMLElement} The node or null if it wasn't found
14190      */
14191     getNode : function(nodeInfo){
14192         if(typeof nodeInfo == "string"){
14193             return document.getElementById(nodeInfo);
14194         }else if(typeof nodeInfo == "number"){
14195             return this.nodes[nodeInfo];
14196         }
14197         return nodeInfo;
14198     },
14199
14200     /**
14201      * Gets a range template nodes.
14202      * @param {Number} startIndex
14203      * @param {Number} endIndex
14204      * @return {Array} An array of nodes
14205      */
14206     getNodes : function(start, end){
14207         var ns = this.nodes;
14208         start = start || 0;
14209         end = typeof end == "undefined" ? ns.length - 1 : end;
14210         var nodes = [];
14211         if(start <= end){
14212             for(var i = start; i <= end; i++){
14213                 nodes.push(ns[i]);
14214             }
14215         } else{
14216             for(var i = start; i >= end; i--){
14217                 nodes.push(ns[i]);
14218             }
14219         }
14220         return nodes;
14221     },
14222
14223     /**
14224      * Finds the index of the passed node
14225      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14226      * @return {Number} The index of the node or -1
14227      */
14228     indexOf : function(node){
14229         node = this.getNode(node);
14230         if(typeof node.nodeIndex == "number"){
14231             return node.nodeIndex;
14232         }
14233         var ns = this.nodes;
14234         for(var i = 0, len = ns.length; i < len; i++){
14235             if(ns[i] == node){
14236                 return i;
14237             }
14238         }
14239         return -1;
14240     }
14241 });
14242 /*
14243  * - LGPL
14244  *
14245  * based on jquery fullcalendar
14246  * 
14247  */
14248
14249 Roo.bootstrap = Roo.bootstrap || {};
14250 /**
14251  * @class Roo.bootstrap.Calendar
14252  * @extends Roo.bootstrap.Component
14253  * Bootstrap Calendar class
14254  * @cfg {Boolean} loadMask (true|false) default false
14255  * @cfg {Object} header generate the user specific header of the calendar, default false
14256
14257  * @constructor
14258  * Create a new Container
14259  * @param {Object} config The config object
14260  */
14261
14262
14263
14264 Roo.bootstrap.Calendar = function(config){
14265     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14266      this.addEvents({
14267         /**
14268              * @event select
14269              * Fires when a date is selected
14270              * @param {DatePicker} this
14271              * @param {Date} date The selected date
14272              */
14273         'select': true,
14274         /**
14275              * @event monthchange
14276              * Fires when the displayed month changes 
14277              * @param {DatePicker} this
14278              * @param {Date} date The selected month
14279              */
14280         'monthchange': true,
14281         /**
14282              * @event evententer
14283              * Fires when mouse over an event
14284              * @param {Calendar} this
14285              * @param {event} Event
14286              */
14287         'evententer': true,
14288         /**
14289              * @event eventleave
14290              * Fires when the mouse leaves an
14291              * @param {Calendar} this
14292              * @param {event}
14293              */
14294         'eventleave': true,
14295         /**
14296              * @event eventclick
14297              * Fires when the mouse click an
14298              * @param {Calendar} this
14299              * @param {event}
14300              */
14301         'eventclick': true
14302         
14303     });
14304
14305 };
14306
14307 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14308     
14309      /**
14310      * @cfg {Number} startDay
14311      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14312      */
14313     startDay : 0,
14314     
14315     loadMask : false,
14316     
14317     header : false,
14318       
14319     getAutoCreate : function(){
14320         
14321         
14322         var fc_button = function(name, corner, style, content ) {
14323             return Roo.apply({},{
14324                 tag : 'span',
14325                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14326                          (corner.length ?
14327                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14328                             ''
14329                         ),
14330                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14331                 unselectable: 'on'
14332             });
14333         };
14334         
14335         var header = {};
14336         
14337         if(!this.header){
14338             header = {
14339                 tag : 'table',
14340                 cls : 'fc-header',
14341                 style : 'width:100%',
14342                 cn : [
14343                     {
14344                         tag: 'tr',
14345                         cn : [
14346                             {
14347                                 tag : 'td',
14348                                 cls : 'fc-header-left',
14349                                 cn : [
14350                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14351                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14352                                     { tag: 'span', cls: 'fc-header-space' },
14353                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14354
14355
14356                                 ]
14357                             },
14358
14359                             {
14360                                 tag : 'td',
14361                                 cls : 'fc-header-center',
14362                                 cn : [
14363                                     {
14364                                         tag: 'span',
14365                                         cls: 'fc-header-title',
14366                                         cn : {
14367                                             tag: 'H2',
14368                                             html : 'month / year'
14369                                         }
14370                                     }
14371
14372                                 ]
14373                             },
14374                             {
14375                                 tag : 'td',
14376                                 cls : 'fc-header-right',
14377                                 cn : [
14378                               /*      fc_button('month', 'left', '', 'month' ),
14379                                     fc_button('week', '', '', 'week' ),
14380                                     fc_button('day', 'right', '', 'day' )
14381                                 */    
14382
14383                                 ]
14384                             }
14385
14386                         ]
14387                     }
14388                 ]
14389             };
14390         }
14391         
14392         header = this.header;
14393         
14394        
14395         var cal_heads = function() {
14396             var ret = [];
14397             // fixme - handle this.
14398             
14399             for (var i =0; i < Date.dayNames.length; i++) {
14400                 var d = Date.dayNames[i];
14401                 ret.push({
14402                     tag: 'th',
14403                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14404                     html : d.substring(0,3)
14405                 });
14406                 
14407             }
14408             ret[0].cls += ' fc-first';
14409             ret[6].cls += ' fc-last';
14410             return ret;
14411         };
14412         var cal_cell = function(n) {
14413             return  {
14414                 tag: 'td',
14415                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14416                 cn : [
14417                     {
14418                         cn : [
14419                             {
14420                                 cls: 'fc-day-number',
14421                                 html: 'D'
14422                             },
14423                             {
14424                                 cls: 'fc-day-content',
14425                              
14426                                 cn : [
14427                                      {
14428                                         style: 'position: relative;' // height: 17px;
14429                                     }
14430                                 ]
14431                             }
14432                             
14433                             
14434                         ]
14435                     }
14436                 ]
14437                 
14438             }
14439         };
14440         var cal_rows = function() {
14441             
14442             var ret = [];
14443             for (var r = 0; r < 6; r++) {
14444                 var row= {
14445                     tag : 'tr',
14446                     cls : 'fc-week',
14447                     cn : []
14448                 };
14449                 
14450                 for (var i =0; i < Date.dayNames.length; i++) {
14451                     var d = Date.dayNames[i];
14452                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14453
14454                 }
14455                 row.cn[0].cls+=' fc-first';
14456                 row.cn[0].cn[0].style = 'min-height:90px';
14457                 row.cn[6].cls+=' fc-last';
14458                 ret.push(row);
14459                 
14460             }
14461             ret[0].cls += ' fc-first';
14462             ret[4].cls += ' fc-prev-last';
14463             ret[5].cls += ' fc-last';
14464             return ret;
14465             
14466         };
14467         
14468         var cal_table = {
14469             tag: 'table',
14470             cls: 'fc-border-separate',
14471             style : 'width:100%',
14472             cellspacing  : 0,
14473             cn : [
14474                 { 
14475                     tag: 'thead',
14476                     cn : [
14477                         { 
14478                             tag: 'tr',
14479                             cls : 'fc-first fc-last',
14480                             cn : cal_heads()
14481                         }
14482                     ]
14483                 },
14484                 { 
14485                     tag: 'tbody',
14486                     cn : cal_rows()
14487                 }
14488                   
14489             ]
14490         };
14491          
14492          var cfg = {
14493             cls : 'fc fc-ltr',
14494             cn : [
14495                 header,
14496                 {
14497                     cls : 'fc-content',
14498                     style : "position: relative;",
14499                     cn : [
14500                         {
14501                             cls : 'fc-view fc-view-month fc-grid',
14502                             style : 'position: relative',
14503                             unselectable : 'on',
14504                             cn : [
14505                                 {
14506                                     cls : 'fc-event-container',
14507                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14508                                 },
14509                                 cal_table
14510                             ]
14511                         }
14512                     ]
14513     
14514                 }
14515            ] 
14516             
14517         };
14518         
14519          
14520         
14521         return cfg;
14522     },
14523     
14524     
14525     initEvents : function()
14526     {
14527         if(!this.store){
14528             throw "can not find store for calendar";
14529         }
14530         
14531         var mark = {
14532             tag: "div",
14533             cls:"x-dlg-mask",
14534             style: "text-align:center",
14535             cn: [
14536                 {
14537                     tag: "div",
14538                     style: "background-color:white;width:50%;margin:250 auto",
14539                     cn: [
14540                         {
14541                             tag: "img",
14542                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14543                         },
14544                         {
14545                             tag: "span",
14546                             html: "Loading"
14547                         }
14548                         
14549                     ]
14550                 }
14551             ]
14552         }
14553         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14554         
14555         var size = this.el.select('.fc-content', true).first().getSize();
14556         this.maskEl.setSize(size.width, size.height);
14557         this.maskEl.enableDisplayMode("block");
14558         if(!this.loadMask){
14559             this.maskEl.hide();
14560         }
14561         
14562         this.store = Roo.factory(this.store, Roo.data);
14563         this.store.on('load', this.onLoad, this);
14564         this.store.on('beforeload', this.onBeforeLoad, this);
14565         
14566         this.resize();
14567         
14568         this.cells = this.el.select('.fc-day',true);
14569         //Roo.log(this.cells);
14570         this.textNodes = this.el.query('.fc-day-number');
14571         this.cells.addClassOnOver('fc-state-hover');
14572         
14573         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14574         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14575         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14576         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14577         
14578         this.on('monthchange', this.onMonthChange, this);
14579         
14580         this.update(new Date().clearTime());
14581     },
14582     
14583     resize : function() {
14584         var sz  = this.el.getSize();
14585         
14586         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14587         this.el.select('.fc-day-content div',true).setHeight(34);
14588     },
14589     
14590     
14591     // private
14592     showPrevMonth : function(e){
14593         this.update(this.activeDate.add("mo", -1));
14594     },
14595     showToday : function(e){
14596         this.update(new Date().clearTime());
14597     },
14598     // private
14599     showNextMonth : function(e){
14600         this.update(this.activeDate.add("mo", 1));
14601     },
14602
14603     // private
14604     showPrevYear : function(){
14605         this.update(this.activeDate.add("y", -1));
14606     },
14607
14608     // private
14609     showNextYear : function(){
14610         this.update(this.activeDate.add("y", 1));
14611     },
14612
14613     
14614    // private
14615     update : function(date)
14616     {
14617         var vd = this.activeDate;
14618         this.activeDate = date;
14619 //        if(vd && this.el){
14620 //            var t = date.getTime();
14621 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14622 //                Roo.log('using add remove');
14623 //                
14624 //                this.fireEvent('monthchange', this, date);
14625 //                
14626 //                this.cells.removeClass("fc-state-highlight");
14627 //                this.cells.each(function(c){
14628 //                   if(c.dateValue == t){
14629 //                       c.addClass("fc-state-highlight");
14630 //                       setTimeout(function(){
14631 //                            try{c.dom.firstChild.focus();}catch(e){}
14632 //                       }, 50);
14633 //                       return false;
14634 //                   }
14635 //                   return true;
14636 //                });
14637 //                return;
14638 //            }
14639 //        }
14640         
14641         var days = date.getDaysInMonth();
14642         
14643         var firstOfMonth = date.getFirstDateOfMonth();
14644         var startingPos = firstOfMonth.getDay()-this.startDay;
14645         
14646         if(startingPos < this.startDay){
14647             startingPos += 7;
14648         }
14649         
14650         var pm = date.add(Date.MONTH, -1);
14651         var prevStart = pm.getDaysInMonth()-startingPos;
14652 //        
14653         this.cells = this.el.select('.fc-day',true);
14654         this.textNodes = this.el.query('.fc-day-number');
14655         this.cells.addClassOnOver('fc-state-hover');
14656         
14657         var cells = this.cells.elements;
14658         var textEls = this.textNodes;
14659         
14660         Roo.each(cells, function(cell){
14661             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14662         });
14663         
14664         days += startingPos;
14665
14666         // convert everything to numbers so it's fast
14667         var day = 86400000;
14668         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14669         //Roo.log(d);
14670         //Roo.log(pm);
14671         //Roo.log(prevStart);
14672         
14673         var today = new Date().clearTime().getTime();
14674         var sel = date.clearTime().getTime();
14675         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14676         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14677         var ddMatch = this.disabledDatesRE;
14678         var ddText = this.disabledDatesText;
14679         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14680         var ddaysText = this.disabledDaysText;
14681         var format = this.format;
14682         
14683         var setCellClass = function(cal, cell){
14684             cell.row = 0;
14685             cell.events = [];
14686             cell.more = [];
14687             //Roo.log('set Cell Class');
14688             cell.title = "";
14689             var t = d.getTime();
14690             
14691             //Roo.log(d);
14692             
14693             cell.dateValue = t;
14694             if(t == today){
14695                 cell.className += " fc-today";
14696                 cell.className += " fc-state-highlight";
14697                 cell.title = cal.todayText;
14698             }
14699             if(t == sel){
14700                 // disable highlight in other month..
14701                 //cell.className += " fc-state-highlight";
14702                 
14703             }
14704             // disabling
14705             if(t < min) {
14706                 cell.className = " fc-state-disabled";
14707                 cell.title = cal.minText;
14708                 return;
14709             }
14710             if(t > max) {
14711                 cell.className = " fc-state-disabled";
14712                 cell.title = cal.maxText;
14713                 return;
14714             }
14715             if(ddays){
14716                 if(ddays.indexOf(d.getDay()) != -1){
14717                     cell.title = ddaysText;
14718                     cell.className = " fc-state-disabled";
14719                 }
14720             }
14721             if(ddMatch && format){
14722                 var fvalue = d.dateFormat(format);
14723                 if(ddMatch.test(fvalue)){
14724                     cell.title = ddText.replace("%0", fvalue);
14725                     cell.className = " fc-state-disabled";
14726                 }
14727             }
14728             
14729             if (!cell.initialClassName) {
14730                 cell.initialClassName = cell.dom.className;
14731             }
14732             
14733             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14734         };
14735
14736         var i = 0;
14737         
14738         for(; i < startingPos; i++) {
14739             textEls[i].innerHTML = (++prevStart);
14740             d.setDate(d.getDate()+1);
14741             
14742             cells[i].className = "fc-past fc-other-month";
14743             setCellClass(this, cells[i]);
14744         }
14745         
14746         var intDay = 0;
14747         
14748         for(; i < days; i++){
14749             intDay = i - startingPos + 1;
14750             textEls[i].innerHTML = (intDay);
14751             d.setDate(d.getDate()+1);
14752             
14753             cells[i].className = ''; // "x-date-active";
14754             setCellClass(this, cells[i]);
14755         }
14756         var extraDays = 0;
14757         
14758         for(; i < 42; i++) {
14759             textEls[i].innerHTML = (++extraDays);
14760             d.setDate(d.getDate()+1);
14761             
14762             cells[i].className = "fc-future fc-other-month";
14763             setCellClass(this, cells[i]);
14764         }
14765         
14766         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14767         
14768         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14769         
14770         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14771         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14772         
14773         if(totalRows != 6){
14774             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14775             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14776         }
14777         
14778         this.fireEvent('monthchange', this, date);
14779         
14780         
14781         /*
14782         if(!this.internalRender){
14783             var main = this.el.dom.firstChild;
14784             var w = main.offsetWidth;
14785             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14786             Roo.fly(main).setWidth(w);
14787             this.internalRender = true;
14788             // opera does not respect the auto grow header center column
14789             // then, after it gets a width opera refuses to recalculate
14790             // without a second pass
14791             if(Roo.isOpera && !this.secondPass){
14792                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14793                 this.secondPass = true;
14794                 this.update.defer(10, this, [date]);
14795             }
14796         }
14797         */
14798         
14799     },
14800     
14801     findCell : function(dt) {
14802         dt = dt.clearTime().getTime();
14803         var ret = false;
14804         this.cells.each(function(c){
14805             //Roo.log("check " +c.dateValue + '?=' + dt);
14806             if(c.dateValue == dt){
14807                 ret = c;
14808                 return false;
14809             }
14810             return true;
14811         });
14812         
14813         return ret;
14814     },
14815     
14816     findCells : function(ev) {
14817         var s = ev.start.clone().clearTime().getTime();
14818        // Roo.log(s);
14819         var e= ev.end.clone().clearTime().getTime();
14820        // Roo.log(e);
14821         var ret = [];
14822         this.cells.each(function(c){
14823              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14824             
14825             if(c.dateValue > e){
14826                 return ;
14827             }
14828             if(c.dateValue < s){
14829                 return ;
14830             }
14831             ret.push(c);
14832         });
14833         
14834         return ret;    
14835     },
14836     
14837 //    findBestRow: function(cells)
14838 //    {
14839 //        var ret = 0;
14840 //        
14841 //        for (var i =0 ; i < cells.length;i++) {
14842 //            ret  = Math.max(cells[i].rows || 0,ret);
14843 //        }
14844 //        return ret;
14845 //        
14846 //    },
14847     
14848     
14849     addItem : function(ev)
14850     {
14851         // look for vertical location slot in
14852         var cells = this.findCells(ev);
14853         
14854 //        ev.row = this.findBestRow(cells);
14855         
14856         // work out the location.
14857         
14858         var crow = false;
14859         var rows = [];
14860         for(var i =0; i < cells.length; i++) {
14861             
14862             cells[i].row = cells[0].row;
14863             
14864             if(i == 0){
14865                 cells[i].row = cells[i].row + 1;
14866             }
14867             
14868             if (!crow) {
14869                 crow = {
14870                     start : cells[i],
14871                     end :  cells[i]
14872                 };
14873                 continue;
14874             }
14875             if (crow.start.getY() == cells[i].getY()) {
14876                 // on same row.
14877                 crow.end = cells[i];
14878                 continue;
14879             }
14880             // different row.
14881             rows.push(crow);
14882             crow = {
14883                 start: cells[i],
14884                 end : cells[i]
14885             };
14886             
14887         }
14888         
14889         rows.push(crow);
14890         ev.els = [];
14891         ev.rows = rows;
14892         ev.cells = cells;
14893         
14894         cells[0].events.push(ev);
14895         
14896         this.calevents.push(ev);
14897     },
14898     
14899     clearEvents: function() {
14900         
14901         if(!this.calevents){
14902             return;
14903         }
14904         
14905         Roo.each(this.cells.elements, function(c){
14906             c.row = 0;
14907             c.events = [];
14908             c.more = [];
14909         });
14910         
14911         Roo.each(this.calevents, function(e) {
14912             Roo.each(e.els, function(el) {
14913                 el.un('mouseenter' ,this.onEventEnter, this);
14914                 el.un('mouseleave' ,this.onEventLeave, this);
14915                 el.remove();
14916             },this);
14917         },this);
14918         
14919         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14920             e.remove();
14921         });
14922         
14923     },
14924     
14925     renderEvents: function()
14926     {   
14927         var _this = this;
14928         
14929         this.cells.each(function(c) {
14930             
14931             if(c.row < 5){
14932                 return;
14933             }
14934             
14935             var ev = c.events;
14936             
14937             var r = 4;
14938             if(c.row != c.events.length){
14939                 r = 4 - (4 - (c.row - c.events.length));
14940             }
14941             
14942             c.events = ev.slice(0, r);
14943             c.more = ev.slice(r);
14944             
14945             if(c.more.length && c.more.length == 1){
14946                 c.events.push(c.more.pop());
14947             }
14948             
14949             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14950             
14951         });
14952             
14953         this.cells.each(function(c) {
14954             
14955             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14956             
14957             
14958             for (var e = 0; e < c.events.length; e++){
14959                 var ev = c.events[e];
14960                 var rows = ev.rows;
14961                 
14962                 for(var i = 0; i < rows.length; i++) {
14963                 
14964                     // how many rows should it span..
14965
14966                     var  cfg = {
14967                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14968                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14969
14970                         unselectable : "on",
14971                         cn : [
14972                             {
14973                                 cls: 'fc-event-inner',
14974                                 cn : [
14975     //                                {
14976     //                                  tag:'span',
14977     //                                  cls: 'fc-event-time',
14978     //                                  html : cells.length > 1 ? '' : ev.time
14979     //                                },
14980                                     {
14981                                       tag:'span',
14982                                       cls: 'fc-event-title',
14983                                       html : String.format('{0}', ev.title)
14984                                     }
14985
14986
14987                                 ]
14988                             },
14989                             {
14990                                 cls: 'ui-resizable-handle ui-resizable-e',
14991                                 html : '&nbsp;&nbsp;&nbsp'
14992                             }
14993
14994                         ]
14995                     };
14996
14997                     if (i == 0) {
14998                         cfg.cls += ' fc-event-start';
14999                     }
15000                     if ((i+1) == rows.length) {
15001                         cfg.cls += ' fc-event-end';
15002                     }
15003
15004                     var ctr = _this.el.select('.fc-event-container',true).first();
15005                     var cg = ctr.createChild(cfg);
15006
15007                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15008                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15009
15010                     var r = (c.more.length) ? 1 : 0;
15011                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15012                     cg.setWidth(ebox.right - sbox.x -2);
15013
15014                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15015                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15016                     cg.on('click', _this.onEventClick, _this, ev);
15017
15018                     ev.els.push(cg);
15019                     
15020                 }
15021                 
15022             }
15023             
15024             
15025             if(c.more.length){
15026                 var  cfg = {
15027                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15028                     style : 'position: absolute',
15029                     unselectable : "on",
15030                     cn : [
15031                         {
15032                             cls: 'fc-event-inner',
15033                             cn : [
15034                                 {
15035                                   tag:'span',
15036                                   cls: 'fc-event-title',
15037                                   html : 'More'
15038                                 }
15039
15040
15041                             ]
15042                         },
15043                         {
15044                             cls: 'ui-resizable-handle ui-resizable-e',
15045                             html : '&nbsp;&nbsp;&nbsp'
15046                         }
15047
15048                     ]
15049                 };
15050
15051                 var ctr = _this.el.select('.fc-event-container',true).first();
15052                 var cg = ctr.createChild(cfg);
15053
15054                 var sbox = c.select('.fc-day-content',true).first().getBox();
15055                 var ebox = c.select('.fc-day-content',true).first().getBox();
15056                 //Roo.log(cg);
15057                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15058                 cg.setWidth(ebox.right - sbox.x -2);
15059
15060                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15061                 
15062             }
15063             
15064         });
15065         
15066         
15067         
15068     },
15069     
15070     onEventEnter: function (e, el,event,d) {
15071         this.fireEvent('evententer', this, el, event);
15072     },
15073     
15074     onEventLeave: function (e, el,event,d) {
15075         this.fireEvent('eventleave', this, el, event);
15076     },
15077     
15078     onEventClick: function (e, el,event,d) {
15079         this.fireEvent('eventclick', this, el, event);
15080     },
15081     
15082     onMonthChange: function () {
15083         this.store.load();
15084     },
15085     
15086     onMoreEventClick: function(e, el, more)
15087     {
15088         var _this = this;
15089         
15090         this.calpopover.placement = 'right';
15091         this.calpopover.setTitle('More');
15092         
15093         this.calpopover.setContent('');
15094         
15095         var ctr = this.calpopover.el.select('.popover-content', true).first();
15096         
15097         Roo.each(more, function(m){
15098             var cfg = {
15099                 cls : 'fc-event-hori fc-event-draggable',
15100                 html : m.title
15101             }
15102             var cg = ctr.createChild(cfg);
15103             
15104             cg.on('click', _this.onEventClick, _this, m);
15105         });
15106         
15107         this.calpopover.show(el);
15108         
15109         
15110     },
15111     
15112     onLoad: function () 
15113     {   
15114         this.calevents = [];
15115         var cal = this;
15116         
15117         if(this.store.getCount() > 0){
15118             this.store.data.each(function(d){
15119                cal.addItem({
15120                     id : d.data.id,
15121                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15122                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15123                     time : d.data.start_time,
15124                     title : d.data.title,
15125                     description : d.data.description,
15126                     venue : d.data.venue
15127                 });
15128             });
15129         }
15130         
15131         this.renderEvents();
15132         
15133         if(this.calevents.length && this.loadMask){
15134             this.maskEl.hide();
15135         }
15136     },
15137     
15138     onBeforeLoad: function()
15139     {
15140         this.clearEvents();
15141         if(this.loadMask){
15142             this.maskEl.show();
15143         }
15144     }
15145 });
15146
15147  
15148  /*
15149  * - LGPL
15150  *
15151  * element
15152  * 
15153  */
15154
15155 /**
15156  * @class Roo.bootstrap.Popover
15157  * @extends Roo.bootstrap.Component
15158  * Bootstrap Popover class
15159  * @cfg {String} html contents of the popover   (or false to use children..)
15160  * @cfg {String} title of popover (or false to hide)
15161  * @cfg {String} placement how it is placed
15162  * @cfg {String} trigger click || hover (or false to trigger manually)
15163  * @cfg {String} over what (parent or false to trigger manually.)
15164  * @cfg {Number} delay - delay before showing
15165  
15166  * @constructor
15167  * Create a new Popover
15168  * @param {Object} config The config object
15169  */
15170
15171 Roo.bootstrap.Popover = function(config){
15172     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15173 };
15174
15175 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15176     
15177     title: 'Fill in a title',
15178     html: false,
15179     
15180     placement : 'right',
15181     trigger : 'hover', // hover
15182     
15183     delay : 0,
15184     
15185     over: 'parent',
15186     
15187     can_build_overlaid : false,
15188     
15189     getChildContainer : function()
15190     {
15191         return this.el.select('.popover-content',true).first();
15192     },
15193     
15194     getAutoCreate : function(){
15195          Roo.log('make popover?');
15196         var cfg = {
15197            cls : 'popover roo-dynamic',
15198            style: 'display:block',
15199            cn : [
15200                 {
15201                     cls : 'arrow'
15202                 },
15203                 {
15204                     cls : 'popover-inner',
15205                     cn : [
15206                         {
15207                             tag: 'h3',
15208                             cls: 'popover-title',
15209                             html : this.title
15210                         },
15211                         {
15212                             cls : 'popover-content',
15213                             html : this.html
15214                         }
15215                     ]
15216                     
15217                 }
15218            ]
15219         };
15220         
15221         return cfg;
15222     },
15223     setTitle: function(str)
15224     {
15225         this.title = str;
15226         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15227     },
15228     setContent: function(str)
15229     {
15230         this.html = str;
15231         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15232     },
15233     // as it get's added to the bottom of the page.
15234     onRender : function(ct, position)
15235     {
15236         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15237         if(!this.el){
15238             var cfg = Roo.apply({},  this.getAutoCreate());
15239             cfg.id = Roo.id();
15240             
15241             if (this.cls) {
15242                 cfg.cls += ' ' + this.cls;
15243             }
15244             if (this.style) {
15245                 cfg.style = this.style;
15246             }
15247             Roo.log("adding to ")
15248             this.el = Roo.get(document.body).createChild(cfg, position);
15249             Roo.log(this.el);
15250         }
15251         this.initEvents();
15252     },
15253     
15254     initEvents : function()
15255     {
15256         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15257         this.el.enableDisplayMode('block');
15258         this.el.hide();
15259         if (this.over === false) {
15260             return; 
15261         }
15262         if (this.triggers === false) {
15263             return;
15264         }
15265         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15266         var triggers = this.trigger ? this.trigger.split(' ') : [];
15267         Roo.each(triggers, function(trigger) {
15268         
15269             if (trigger == 'click') {
15270                 on_el.on('click', this.toggle, this);
15271             } else if (trigger != 'manual') {
15272                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15273                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15274       
15275                 on_el.on(eventIn  ,this.enter, this);
15276                 on_el.on(eventOut, this.leave, this);
15277             }
15278         }, this);
15279         
15280     },
15281     
15282     
15283     // private
15284     timeout : null,
15285     hoverState : null,
15286     
15287     toggle : function () {
15288         this.hoverState == 'in' ? this.leave() : this.enter();
15289     },
15290     
15291     enter : function () {
15292        
15293     
15294         clearTimeout(this.timeout);
15295     
15296         this.hoverState = 'in';
15297     
15298         if (!this.delay || !this.delay.show) {
15299             this.show();
15300             return;
15301         }
15302         var _t = this;
15303         this.timeout = setTimeout(function () {
15304             if (_t.hoverState == 'in') {
15305                 _t.show();
15306             }
15307         }, this.delay.show)
15308     },
15309     leave : function() {
15310         clearTimeout(this.timeout);
15311     
15312         this.hoverState = 'out';
15313     
15314         if (!this.delay || !this.delay.hide) {
15315             this.hide();
15316             return;
15317         }
15318         var _t = this;
15319         this.timeout = setTimeout(function () {
15320             if (_t.hoverState == 'out') {
15321                 _t.hide();
15322             }
15323         }, this.delay.hide)
15324     },
15325     
15326     show : function (on_el)
15327     {
15328         if (!on_el) {
15329             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15330         }
15331         // set content.
15332         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15333         if (this.html !== false) {
15334             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15335         }
15336         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15337         if (!this.title.length) {
15338             this.el.select('.popover-title',true).hide();
15339         }
15340         
15341         var placement = typeof this.placement == 'function' ?
15342             this.placement.call(this, this.el, on_el) :
15343             this.placement;
15344             
15345         var autoToken = /\s?auto?\s?/i;
15346         var autoPlace = autoToken.test(placement);
15347         if (autoPlace) {
15348             placement = placement.replace(autoToken, '') || 'top';
15349         }
15350         
15351         //this.el.detach()
15352         //this.el.setXY([0,0]);
15353         this.el.show();
15354         this.el.dom.style.display='block';
15355         this.el.addClass(placement);
15356         
15357         //this.el.appendTo(on_el);
15358         
15359         var p = this.getPosition();
15360         var box = this.el.getBox();
15361         
15362         if (autoPlace) {
15363             // fixme..
15364         }
15365         var align = Roo.bootstrap.Popover.alignment[placement];
15366         this.el.alignTo(on_el, align[0],align[1]);
15367         //var arrow = this.el.select('.arrow',true).first();
15368         //arrow.set(align[2], 
15369         
15370         this.el.addClass('in');
15371         
15372         
15373         if (this.el.hasClass('fade')) {
15374             // fade it?
15375         }
15376         
15377     },
15378     hide : function()
15379     {
15380         this.el.setXY([0,0]);
15381         this.el.removeClass('in');
15382         this.el.hide();
15383         this.hoverState = null;
15384         
15385     }
15386     
15387 });
15388
15389 Roo.bootstrap.Popover.alignment = {
15390     'left' : ['r-l', [-10,0], 'right'],
15391     'right' : ['l-r', [10,0], 'left'],
15392     'bottom' : ['t-b', [0,10], 'top'],
15393     'top' : [ 'b-t', [0,-10], 'bottom']
15394 };
15395
15396  /*
15397  * - LGPL
15398  *
15399  * Progress
15400  * 
15401  */
15402
15403 /**
15404  * @class Roo.bootstrap.Progress
15405  * @extends Roo.bootstrap.Component
15406  * Bootstrap Progress class
15407  * @cfg {Boolean} striped striped of the progress bar
15408  * @cfg {Boolean} active animated of the progress bar
15409  * 
15410  * 
15411  * @constructor
15412  * Create a new Progress
15413  * @param {Object} config The config object
15414  */
15415
15416 Roo.bootstrap.Progress = function(config){
15417     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15418 };
15419
15420 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15421     
15422     striped : false,
15423     active: false,
15424     
15425     getAutoCreate : function(){
15426         var cfg = {
15427             tag: 'div',
15428             cls: 'progress'
15429         };
15430         
15431         
15432         if(this.striped){
15433             cfg.cls += ' progress-striped';
15434         }
15435       
15436         if(this.active){
15437             cfg.cls += ' active';
15438         }
15439         
15440         
15441         return cfg;
15442     }
15443    
15444 });
15445
15446  
15447
15448  /*
15449  * - LGPL
15450  *
15451  * ProgressBar
15452  * 
15453  */
15454
15455 /**
15456  * @class Roo.bootstrap.ProgressBar
15457  * @extends Roo.bootstrap.Component
15458  * Bootstrap ProgressBar class
15459  * @cfg {Number} aria_valuenow aria-value now
15460  * @cfg {Number} aria_valuemin aria-value min
15461  * @cfg {Number} aria_valuemax aria-value max
15462  * @cfg {String} label label for the progress bar
15463  * @cfg {String} panel (success | info | warning | danger )
15464  * @cfg {String} role role of the progress bar
15465  * @cfg {String} sr_only text
15466  * 
15467  * 
15468  * @constructor
15469  * Create a new ProgressBar
15470  * @param {Object} config The config object
15471  */
15472
15473 Roo.bootstrap.ProgressBar = function(config){
15474     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15475 };
15476
15477 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15478     
15479     aria_valuenow : 0,
15480     aria_valuemin : 0,
15481     aria_valuemax : 100,
15482     label : false,
15483     panel : false,
15484     role : false,
15485     sr_only: false,
15486     
15487     getAutoCreate : function()
15488     {
15489         
15490         var cfg = {
15491             tag: 'div',
15492             cls: 'progress-bar',
15493             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15494         };
15495         
15496         if(this.sr_only){
15497             cfg.cn = {
15498                 tag: 'span',
15499                 cls: 'sr-only',
15500                 html: this.sr_only
15501             }
15502         }
15503         
15504         if(this.role){
15505             cfg.role = this.role;
15506         }
15507         
15508         if(this.aria_valuenow){
15509             cfg['aria-valuenow'] = this.aria_valuenow;
15510         }
15511         
15512         if(this.aria_valuemin){
15513             cfg['aria-valuemin'] = this.aria_valuemin;
15514         }
15515         
15516         if(this.aria_valuemax){
15517             cfg['aria-valuemax'] = this.aria_valuemax;
15518         }
15519         
15520         if(this.label && !this.sr_only){
15521             cfg.html = this.label;
15522         }
15523         
15524         if(this.panel){
15525             cfg.cls += ' progress-bar-' + this.panel;
15526         }
15527         
15528         return cfg;
15529     },
15530     
15531     update : function(aria_valuenow)
15532     {
15533         this.aria_valuenow = aria_valuenow;
15534         
15535         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15536     }
15537    
15538 });
15539
15540  
15541
15542  /*
15543  * - LGPL
15544  *
15545  * column
15546  * 
15547  */
15548
15549 /**
15550  * @class Roo.bootstrap.TabGroup
15551  * @extends Roo.bootstrap.Column
15552  * Bootstrap Column class
15553  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15554  * @cfg {Boolean} carousel true to make the group behave like a carousel
15555  * @cfg {Number} bullets show the panel pointer.. default 0
15556  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15557  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15558  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15559  * 
15560  * @constructor
15561  * Create a new TabGroup
15562  * @param {Object} config The config object
15563  */
15564
15565 Roo.bootstrap.TabGroup = function(config){
15566     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15567     if (!this.navId) {
15568         this.navId = Roo.id();
15569     }
15570     this.tabs = [];
15571     Roo.bootstrap.TabGroup.register(this);
15572     
15573 };
15574
15575 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15576     
15577     carousel : false,
15578     transition : false,
15579     bullets : 0,
15580     timer : 0,
15581     autoslide : false,
15582     slideFn : false,
15583     slideOnTouch : false,
15584     
15585     getAutoCreate : function()
15586     {
15587         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15588         
15589         cfg.cls += ' tab-content';
15590         
15591         Roo.log('get auto create...............');
15592         
15593         if (this.carousel) {
15594             cfg.cls += ' carousel slide';
15595             
15596             cfg.cn = [{
15597                cls : 'carousel-inner'
15598             }];
15599         
15600             if(this.bullets > 0 && !Roo.isTouch){
15601                 
15602                 var bullets = {
15603                     cls : 'carousel-bullets',
15604                     cn : []
15605                 };
15606                 
15607                 if(this.bullets_cls){
15608                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15609                 }
15610                 
15611                 for (var i = 0; i < this.bullets; i++){
15612                     bullets.cn.push({
15613                         cls : 'bullet bullet-' + i
15614                     });
15615                 }
15616                 
15617                 bullets.cn.push({
15618                     cls : 'clear'
15619                 });
15620                 
15621                 cfg.cn[0].cn = bullets;
15622             }
15623         }
15624         
15625         return cfg;
15626     },
15627     
15628     initEvents:  function()
15629     {
15630         Roo.log('-------- init events on tab group ---------');
15631         
15632         if(this.bullets > 0 && !Roo.isTouch){
15633             this.initBullet();
15634         }
15635         
15636         Roo.log(this);
15637         
15638         if(Roo.isTouch && this.slideOnTouch){
15639             this.el.on("touchstart", this.onTouchStart, this);
15640         }
15641         
15642         if(this.autoslide){
15643             var _this = this;
15644             
15645             this.slideFn = window.setInterval(function() {
15646                 _this.showPanelNext();
15647             }, this.timer);
15648         }
15649         
15650     },
15651     
15652     onTouchStart : function(e, el, o)
15653     {
15654         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15655             return;
15656         }
15657         
15658         this.showPanelNext();
15659     },
15660     
15661     getChildContainer : function()
15662     {
15663         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15664     },
15665     
15666     /**
15667     * register a Navigation item
15668     * @param {Roo.bootstrap.NavItem} the navitem to add
15669     */
15670     register : function(item)
15671     {
15672         this.tabs.push( item);
15673         item.navId = this.navId; // not really needed..
15674     
15675     },
15676     
15677     getActivePanel : function()
15678     {
15679         var r = false;
15680         Roo.each(this.tabs, function(t) {
15681             if (t.active) {
15682                 r = t;
15683                 return false;
15684             }
15685             return null;
15686         });
15687         return r;
15688         
15689     },
15690     getPanelByName : function(n)
15691     {
15692         var r = false;
15693         Roo.each(this.tabs, function(t) {
15694             if (t.tabId == n) {
15695                 r = t;
15696                 return false;
15697             }
15698             return null;
15699         });
15700         return r;
15701     },
15702     indexOfPanel : function(p)
15703     {
15704         var r = false;
15705         Roo.each(this.tabs, function(t,i) {
15706             if (t.tabId == p.tabId) {
15707                 r = i;
15708                 return false;
15709             }
15710             return null;
15711         });
15712         return r;
15713     },
15714     /**
15715      * show a specific panel
15716      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15717      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15718      */
15719     showPanel : function (pan)
15720     {
15721         if(this.transition){
15722             Roo.log("waiting for the transitionend");
15723             return;
15724         }
15725         
15726         if (typeof(pan) == 'number') {
15727             pan = this.tabs[pan];
15728         }
15729         if (typeof(pan) == 'string') {
15730             pan = this.getPanelByName(pan);
15731         }
15732         if (pan.tabId == this.getActivePanel().tabId) {
15733             return true;
15734         }
15735         var cur = this.getActivePanel();
15736         
15737         if (false === cur.fireEvent('beforedeactivate')) {
15738             return false;
15739         }
15740         
15741         if(this.bullets > 0 && !Roo.isTouch){
15742             this.setActiveBullet(this.indexOfPanel(pan));
15743         }
15744         
15745         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15746             
15747             this.transition = true;
15748             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15749             var lr = dir == 'next' ? 'left' : 'right';
15750             pan.el.addClass(dir); // or prev
15751             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15752             cur.el.addClass(lr); // or right
15753             pan.el.addClass(lr);
15754             
15755             var _this = this;
15756             cur.el.on('transitionend', function() {
15757                 Roo.log("trans end?");
15758                 
15759                 pan.el.removeClass([lr,dir]);
15760                 pan.setActive(true);
15761                 
15762                 cur.el.removeClass([lr]);
15763                 cur.setActive(false);
15764                 
15765                 _this.transition = false;
15766                 
15767             }, this, { single:  true } );
15768             
15769             return true;
15770         }
15771         
15772         cur.setActive(false);
15773         pan.setActive(true);
15774         
15775         return true;
15776         
15777     },
15778     showPanelNext : function()
15779     {
15780         var i = this.indexOfPanel(this.getActivePanel());
15781         
15782         if (i >= this.tabs.length - 1 && !this.autoslide) {
15783             return;
15784         }
15785         
15786         if (i >= this.tabs.length - 1 && this.autoslide) {
15787             i = -1;
15788         }
15789         
15790         this.showPanel(this.tabs[i+1]);
15791     },
15792     
15793     showPanelPrev : function()
15794     {
15795         var i = this.indexOfPanel(this.getActivePanel());
15796         
15797         if (i  < 1 && !this.autoslide) {
15798             return;
15799         }
15800         
15801         if (i < 1 && this.autoslide) {
15802             i = this.tabs.length;
15803         }
15804         
15805         this.showPanel(this.tabs[i-1]);
15806     },
15807     
15808     initBullet : function()
15809     {
15810         if(Roo.isTouch){
15811             return;
15812         }
15813         
15814         var _this = this;
15815         
15816         for (var i = 0; i < this.bullets; i++){
15817             var bullet = this.el.select('.bullet-' + i, true).first();
15818
15819             if(!bullet){
15820                 continue;
15821             }
15822
15823             bullet.on('click', (function(e, el, o, ii, t){
15824
15825                 e.preventDefault();
15826
15827                 _this.showPanel(ii);
15828
15829                 if(_this.autoslide && _this.slideFn){
15830                     clearInterval(_this.slideFn);
15831                     _this.slideFn = window.setInterval(function() {
15832                         _this.showPanelNext();
15833                     }, _this.timer);
15834                 }
15835
15836             }).createDelegate(this, [i, bullet], true));
15837         }
15838     },
15839     
15840     setActiveBullet : function(i)
15841     {
15842         if(Roo.isTouch){
15843             return;
15844         }
15845         
15846         Roo.each(this.el.select('.bullet', true).elements, function(el){
15847             el.removeClass('selected');
15848         });
15849
15850         var bullet = this.el.select('.bullet-' + i, true).first();
15851         
15852         if(!bullet){
15853             return;
15854         }
15855         
15856         bullet.addClass('selected');
15857     }
15858     
15859     
15860   
15861 });
15862
15863  
15864
15865  
15866  
15867 Roo.apply(Roo.bootstrap.TabGroup, {
15868     
15869     groups: {},
15870      /**
15871     * register a Navigation Group
15872     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15873     */
15874     register : function(navgrp)
15875     {
15876         this.groups[navgrp.navId] = navgrp;
15877         
15878     },
15879     /**
15880     * fetch a Navigation Group based on the navigation ID
15881     * if one does not exist , it will get created.
15882     * @param {string} the navgroup to add
15883     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15884     */
15885     get: function(navId) {
15886         if (typeof(this.groups[navId]) == 'undefined') {
15887             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15888         }
15889         return this.groups[navId] ;
15890     }
15891     
15892     
15893     
15894 });
15895
15896  /*
15897  * - LGPL
15898  *
15899  * TabPanel
15900  * 
15901  */
15902
15903 /**
15904  * @class Roo.bootstrap.TabPanel
15905  * @extends Roo.bootstrap.Component
15906  * Bootstrap TabPanel class
15907  * @cfg {Boolean} active panel active
15908  * @cfg {String} html panel content
15909  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15910  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15911  * 
15912  * 
15913  * @constructor
15914  * Create a new TabPanel
15915  * @param {Object} config The config object
15916  */
15917
15918 Roo.bootstrap.TabPanel = function(config){
15919     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15920     this.addEvents({
15921         /**
15922              * @event changed
15923              * Fires when the active status changes
15924              * @param {Roo.bootstrap.TabPanel} this
15925              * @param {Boolean} state the new state
15926             
15927          */
15928         'changed': true,
15929         /**
15930              * @event beforedeactivate
15931              * Fires before a tab is de-activated - can be used to do validation on a form.
15932              * @param {Roo.bootstrap.TabPanel} this
15933              * @return {Boolean} false if there is an error
15934             
15935          */
15936         'beforedeactivate': true
15937      });
15938     
15939     this.tabId = this.tabId || Roo.id();
15940   
15941 };
15942
15943 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15944     
15945     active: false,
15946     html: false,
15947     tabId: false,
15948     navId : false,
15949     
15950     getAutoCreate : function(){
15951         var cfg = {
15952             tag: 'div',
15953             // item is needed for carousel - not sure if it has any effect otherwise
15954             cls: 'tab-pane item',
15955             html: this.html || ''
15956         };
15957         
15958         if(this.active){
15959             cfg.cls += ' active';
15960         }
15961         
15962         if(this.tabId){
15963             cfg.tabId = this.tabId;
15964         }
15965         
15966         
15967         return cfg;
15968     },
15969     
15970     initEvents:  function()
15971     {
15972         Roo.log('-------- init events on tab panel ---------');
15973         
15974         var p = this.parent();
15975         this.navId = this.navId || p.navId;
15976         
15977         if (typeof(this.navId) != 'undefined') {
15978             // not really needed.. but just in case.. parent should be a NavGroup.
15979             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15980             Roo.log(['register', tg, this]);
15981             tg.register(this);
15982             
15983             var i = tg.tabs.length - 1;
15984             
15985             if(this.active && tg.bullets > 0 && i < tg.bullets){
15986                 tg.setActiveBullet(i);
15987             }
15988         }
15989         
15990     },
15991     
15992     
15993     onRender : function(ct, position)
15994     {
15995        // Roo.log("Call onRender: " + this.xtype);
15996         
15997         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15998         
15999         
16000         
16001         
16002         
16003     },
16004     
16005     setActive: function(state)
16006     {
16007         Roo.log("panel - set active " + this.tabId + "=" + state);
16008         
16009         this.active = state;
16010         if (!state) {
16011             this.el.removeClass('active');
16012             
16013         } else  if (!this.el.hasClass('active')) {
16014             this.el.addClass('active');
16015         }
16016         
16017         this.fireEvent('changed', this, state);
16018     }
16019     
16020     
16021 });
16022  
16023
16024  
16025
16026  /*
16027  * - LGPL
16028  *
16029  * DateField
16030  * 
16031  */
16032
16033 /**
16034  * @class Roo.bootstrap.DateField
16035  * @extends Roo.bootstrap.Input
16036  * Bootstrap DateField class
16037  * @cfg {Number} weekStart default 0
16038  * @cfg {String} viewMode default empty, (months|years)
16039  * @cfg {String} minViewMode default empty, (months|years)
16040  * @cfg {Number} startDate default -Infinity
16041  * @cfg {Number} endDate default Infinity
16042  * @cfg {Boolean} todayHighlight default false
16043  * @cfg {Boolean} todayBtn default false
16044  * @cfg {Boolean} calendarWeeks default false
16045  * @cfg {Object} daysOfWeekDisabled default empty
16046  * @cfg {Boolean} singleMode default false (true | false)
16047  * 
16048  * @cfg {Boolean} keyboardNavigation default true
16049  * @cfg {String} language default en
16050  * 
16051  * @constructor
16052  * Create a new DateField
16053  * @param {Object} config The config object
16054  */
16055
16056 Roo.bootstrap.DateField = function(config){
16057     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16058      this.addEvents({
16059             /**
16060              * @event show
16061              * Fires when this field show.
16062              * @param {Roo.bootstrap.DateField} this
16063              * @param {Mixed} date The date value
16064              */
16065             show : true,
16066             /**
16067              * @event show
16068              * Fires when this field hide.
16069              * @param {Roo.bootstrap.DateField} this
16070              * @param {Mixed} date The date value
16071              */
16072             hide : true,
16073             /**
16074              * @event select
16075              * Fires when select a date.
16076              * @param {Roo.bootstrap.DateField} this
16077              * @param {Mixed} date The date value
16078              */
16079             select : true
16080         });
16081 };
16082
16083 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16084     
16085     /**
16086      * @cfg {String} format
16087      * The default date format string which can be overriden for localization support.  The format must be
16088      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16089      */
16090     format : "m/d/y",
16091     /**
16092      * @cfg {String} altFormats
16093      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16094      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16095      */
16096     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16097     
16098     weekStart : 0,
16099     
16100     viewMode : '',
16101     
16102     minViewMode : '',
16103     
16104     todayHighlight : false,
16105     
16106     todayBtn: false,
16107     
16108     language: 'en',
16109     
16110     keyboardNavigation: true,
16111     
16112     calendarWeeks: false,
16113     
16114     startDate: -Infinity,
16115     
16116     endDate: Infinity,
16117     
16118     daysOfWeekDisabled: [],
16119     
16120     _events: [],
16121     
16122     singleMode : false,
16123     
16124     UTCDate: function()
16125     {
16126         return new Date(Date.UTC.apply(Date, arguments));
16127     },
16128     
16129     UTCToday: function()
16130     {
16131         var today = new Date();
16132         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16133     },
16134     
16135     getDate: function() {
16136             var d = this.getUTCDate();
16137             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16138     },
16139     
16140     getUTCDate: function() {
16141             return this.date;
16142     },
16143     
16144     setDate: function(d) {
16145             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16146     },
16147     
16148     setUTCDate: function(d) {
16149             this.date = d;
16150             this.setValue(this.formatDate(this.date));
16151     },
16152         
16153     onRender: function(ct, position)
16154     {
16155         
16156         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16157         
16158         this.language = this.language || 'en';
16159         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16160         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16161         
16162         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16163         this.format = this.format || 'm/d/y';
16164         this.isInline = false;
16165         this.isInput = true;
16166         this.component = this.el.select('.add-on', true).first() || false;
16167         this.component = (this.component && this.component.length === 0) ? false : this.component;
16168         this.hasInput = this.component && this.inputEL().length;
16169         
16170         if (typeof(this.minViewMode === 'string')) {
16171             switch (this.minViewMode) {
16172                 case 'months':
16173                     this.minViewMode = 1;
16174                     break;
16175                 case 'years':
16176                     this.minViewMode = 2;
16177                     break;
16178                 default:
16179                     this.minViewMode = 0;
16180                     break;
16181             }
16182         }
16183         
16184         if (typeof(this.viewMode === 'string')) {
16185             switch (this.viewMode) {
16186                 case 'months':
16187                     this.viewMode = 1;
16188                     break;
16189                 case 'years':
16190                     this.viewMode = 2;
16191                     break;
16192                 default:
16193                     this.viewMode = 0;
16194                     break;
16195             }
16196         }
16197                 
16198         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16199         
16200 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16201         
16202         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16203         
16204         this.picker().on('mousedown', this.onMousedown, this);
16205         this.picker().on('click', this.onClick, this);
16206         
16207         this.picker().addClass('datepicker-dropdown');
16208         
16209         this.startViewMode = this.viewMode;
16210         
16211         if(this.singleMode){
16212             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16213                 v.setVisibilityMode(Roo.Element.DISPLAY)
16214                 v.hide();
16215             });
16216             
16217             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16218                 v.setStyle('width', '189px');
16219             });
16220         }
16221         
16222         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16223             if(!this.calendarWeeks){
16224                 v.remove();
16225                 return;
16226             }
16227             
16228             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16229             v.attr('colspan', function(i, val){
16230                 return parseInt(val) + 1;
16231             });
16232         })
16233                         
16234         
16235         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16236         
16237         this.setStartDate(this.startDate);
16238         this.setEndDate(this.endDate);
16239         
16240         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16241         
16242         this.fillDow();
16243         this.fillMonths();
16244         this.update();
16245         this.showMode();
16246         
16247         if(this.isInline) {
16248             this.show();
16249         }
16250     },
16251     
16252     picker : function()
16253     {
16254         return this.pickerEl;
16255 //        return this.el.select('.datepicker', true).first();
16256     },
16257     
16258     fillDow: function()
16259     {
16260         var dowCnt = this.weekStart;
16261         
16262         var dow = {
16263             tag: 'tr',
16264             cn: [
16265                 
16266             ]
16267         };
16268         
16269         if(this.calendarWeeks){
16270             dow.cn.push({
16271                 tag: 'th',
16272                 cls: 'cw',
16273                 html: '&nbsp;'
16274             })
16275         }
16276         
16277         while (dowCnt < this.weekStart + 7) {
16278             dow.cn.push({
16279                 tag: 'th',
16280                 cls: 'dow',
16281                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16282             });
16283         }
16284         
16285         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16286     },
16287     
16288     fillMonths: function()
16289     {    
16290         var i = 0;
16291         var months = this.picker().select('>.datepicker-months td', true).first();
16292         
16293         months.dom.innerHTML = '';
16294         
16295         while (i < 12) {
16296             var month = {
16297                 tag: 'span',
16298                 cls: 'month',
16299                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16300             }
16301             
16302             months.createChild(month);
16303         }
16304         
16305     },
16306     
16307     update: function()
16308     {
16309         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;
16310         
16311         if (this.date < this.startDate) {
16312             this.viewDate = new Date(this.startDate);
16313         } else if (this.date > this.endDate) {
16314             this.viewDate = new Date(this.endDate);
16315         } else {
16316             this.viewDate = new Date(this.date);
16317         }
16318         
16319         this.fill();
16320     },
16321     
16322     fill: function() 
16323     {
16324         var d = new Date(this.viewDate),
16325                 year = d.getUTCFullYear(),
16326                 month = d.getUTCMonth(),
16327                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16328                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16329                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16330                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16331                 currentDate = this.date && this.date.valueOf(),
16332                 today = this.UTCToday();
16333         
16334         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16335         
16336 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16337         
16338 //        this.picker.select('>tfoot th.today').
16339 //                                              .text(dates[this.language].today)
16340 //                                              .toggle(this.todayBtn !== false);
16341     
16342         this.updateNavArrows();
16343         this.fillMonths();
16344                                                 
16345         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16346         
16347         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16348          
16349         prevMonth.setUTCDate(day);
16350         
16351         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16352         
16353         var nextMonth = new Date(prevMonth);
16354         
16355         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16356         
16357         nextMonth = nextMonth.valueOf();
16358         
16359         var fillMonths = false;
16360         
16361         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16362         
16363         while(prevMonth.valueOf() < nextMonth) {
16364             var clsName = '';
16365             
16366             if (prevMonth.getUTCDay() === this.weekStart) {
16367                 if(fillMonths){
16368                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16369                 }
16370                     
16371                 fillMonths = {
16372                     tag: 'tr',
16373                     cn: []
16374                 };
16375                 
16376                 if(this.calendarWeeks){
16377                     // ISO 8601: First week contains first thursday.
16378                     // ISO also states week starts on Monday, but we can be more abstract here.
16379                     var
16380                     // Start of current week: based on weekstart/current date
16381                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16382                     // Thursday of this week
16383                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16384                     // First Thursday of year, year from thursday
16385                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16386                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16387                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16388                     
16389                     fillMonths.cn.push({
16390                         tag: 'td',
16391                         cls: 'cw',
16392                         html: calWeek
16393                     });
16394                 }
16395             }
16396             
16397             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16398                 clsName += ' old';
16399             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16400                 clsName += ' new';
16401             }
16402             if (this.todayHighlight &&
16403                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16404                 prevMonth.getUTCMonth() == today.getMonth() &&
16405                 prevMonth.getUTCDate() == today.getDate()) {
16406                 clsName += ' today';
16407             }
16408             
16409             if (currentDate && prevMonth.valueOf() === currentDate) {
16410                 clsName += ' active';
16411             }
16412             
16413             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16414                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16415                     clsName += ' disabled';
16416             }
16417             
16418             fillMonths.cn.push({
16419                 tag: 'td',
16420                 cls: 'day ' + clsName,
16421                 html: prevMonth.getDate()
16422             })
16423             
16424             prevMonth.setDate(prevMonth.getDate()+1);
16425         }
16426           
16427         var currentYear = this.date && this.date.getUTCFullYear();
16428         var currentMonth = this.date && this.date.getUTCMonth();
16429         
16430         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16431         
16432         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16433             v.removeClass('active');
16434             
16435             if(currentYear === year && k === currentMonth){
16436                 v.addClass('active');
16437             }
16438             
16439             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16440                 v.addClass('disabled');
16441             }
16442             
16443         });
16444         
16445         
16446         year = parseInt(year/10, 10) * 10;
16447         
16448         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16449         
16450         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16451         
16452         year -= 1;
16453         for (var i = -1; i < 11; i++) {
16454             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16455                 tag: 'span',
16456                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16457                 html: year
16458             })
16459             
16460             year += 1;
16461         }
16462     },
16463     
16464     showMode: function(dir) 
16465     {
16466         if (dir) {
16467             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16468         }
16469         
16470         Roo.each(this.picker().select('>div',true).elements, function(v){
16471             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16472             v.hide();
16473         });
16474         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16475     },
16476     
16477     place: function()
16478     {
16479         if(this.isInline) return;
16480         
16481         this.picker().removeClass(['bottom', 'top']);
16482         
16483         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16484             /*
16485              * place to the top of element!
16486              *
16487              */
16488             
16489             this.picker().addClass('top');
16490             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16491             
16492             return;
16493         }
16494         
16495         this.picker().addClass('bottom');
16496         
16497         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16498     },
16499     
16500     parseDate : function(value)
16501     {
16502         if(!value || value instanceof Date){
16503             return value;
16504         }
16505         var v = Date.parseDate(value, this.format);
16506         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16507             v = Date.parseDate(value, 'Y-m-d');
16508         }
16509         if(!v && this.altFormats){
16510             if(!this.altFormatsArray){
16511                 this.altFormatsArray = this.altFormats.split("|");
16512             }
16513             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16514                 v = Date.parseDate(value, this.altFormatsArray[i]);
16515             }
16516         }
16517         return v;
16518     },
16519     
16520     formatDate : function(date, fmt)
16521     {   
16522         return (!date || !(date instanceof Date)) ?
16523         date : date.dateFormat(fmt || this.format);
16524     },
16525     
16526     onFocus : function()
16527     {
16528         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16529         this.show();
16530     },
16531     
16532     onBlur : function()
16533     {
16534         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16535         
16536         var d = this.inputEl().getValue();
16537         
16538         this.setValue(d);
16539                 
16540         this.hide();
16541     },
16542     
16543     show : function()
16544     {
16545         this.picker().show();
16546         this.update();
16547         this.place();
16548         
16549         this.fireEvent('show', this, this.date);
16550     },
16551     
16552     hide : function()
16553     {
16554         if(this.isInline) return;
16555         this.picker().hide();
16556         this.viewMode = this.startViewMode;
16557         this.showMode();
16558         
16559         this.fireEvent('hide', this, this.date);
16560         
16561     },
16562     
16563     onMousedown: function(e)
16564     {
16565         e.stopPropagation();
16566         e.preventDefault();
16567     },
16568     
16569     keyup: function(e)
16570     {
16571         Roo.bootstrap.DateField.superclass.keyup.call(this);
16572         this.update();
16573     },
16574
16575     setValue: function(v)
16576     {
16577         
16578         // v can be a string or a date..
16579         
16580         
16581         var d = new Date(this.parseDate(v) ).clearTime();
16582         
16583         if(isNaN(d.getTime())){
16584             this.date = this.viewDate = '';
16585             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16586             return;
16587         }
16588         
16589         v = this.formatDate(d);
16590         
16591         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16592         
16593         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16594      
16595         this.update();
16596
16597         this.fireEvent('select', this, this.date);
16598         
16599     },
16600     
16601     getValue: function()
16602     {
16603         return this.formatDate(this.date);
16604     },
16605     
16606     fireKey: function(e)
16607     {
16608         if (!this.picker().isVisible()){
16609             if (e.keyCode == 27) // allow escape to hide and re-show picker
16610                 this.show();
16611             return;
16612         }
16613         
16614         var dateChanged = false,
16615         dir, day, month,
16616         newDate, newViewDate;
16617         
16618         switch(e.keyCode){
16619             case 27: // escape
16620                 this.hide();
16621                 e.preventDefault();
16622                 break;
16623             case 37: // left
16624             case 39: // right
16625                 if (!this.keyboardNavigation) break;
16626                 dir = e.keyCode == 37 ? -1 : 1;
16627                 
16628                 if (e.ctrlKey){
16629                     newDate = this.moveYear(this.date, dir);
16630                     newViewDate = this.moveYear(this.viewDate, dir);
16631                 } else if (e.shiftKey){
16632                     newDate = this.moveMonth(this.date, dir);
16633                     newViewDate = this.moveMonth(this.viewDate, dir);
16634                 } else {
16635                     newDate = new Date(this.date);
16636                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16637                     newViewDate = new Date(this.viewDate);
16638                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16639                 }
16640                 if (this.dateWithinRange(newDate)){
16641                     this.date = newDate;
16642                     this.viewDate = newViewDate;
16643                     this.setValue(this.formatDate(this.date));
16644 //                    this.update();
16645                     e.preventDefault();
16646                     dateChanged = true;
16647                 }
16648                 break;
16649             case 38: // up
16650             case 40: // down
16651                 if (!this.keyboardNavigation) break;
16652                 dir = e.keyCode == 38 ? -1 : 1;
16653                 if (e.ctrlKey){
16654                     newDate = this.moveYear(this.date, dir);
16655                     newViewDate = this.moveYear(this.viewDate, dir);
16656                 } else if (e.shiftKey){
16657                     newDate = this.moveMonth(this.date, dir);
16658                     newViewDate = this.moveMonth(this.viewDate, dir);
16659                 } else {
16660                     newDate = new Date(this.date);
16661                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16662                     newViewDate = new Date(this.viewDate);
16663                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16664                 }
16665                 if (this.dateWithinRange(newDate)){
16666                     this.date = newDate;
16667                     this.viewDate = newViewDate;
16668                     this.setValue(this.formatDate(this.date));
16669 //                    this.update();
16670                     e.preventDefault();
16671                     dateChanged = true;
16672                 }
16673                 break;
16674             case 13: // enter
16675                 this.setValue(this.formatDate(this.date));
16676                 this.hide();
16677                 e.preventDefault();
16678                 break;
16679             case 9: // tab
16680                 this.setValue(this.formatDate(this.date));
16681                 this.hide();
16682                 break;
16683             case 16: // shift
16684             case 17: // ctrl
16685             case 18: // alt
16686                 break;
16687             default :
16688                 this.hide();
16689                 
16690         }
16691     },
16692     
16693     
16694     onClick: function(e) 
16695     {
16696         e.stopPropagation();
16697         e.preventDefault();
16698         
16699         var target = e.getTarget();
16700         
16701         if(target.nodeName.toLowerCase() === 'i'){
16702             target = Roo.get(target).dom.parentNode;
16703         }
16704         
16705         var nodeName = target.nodeName;
16706         var className = target.className;
16707         var html = target.innerHTML;
16708         //Roo.log(nodeName);
16709         
16710         switch(nodeName.toLowerCase()) {
16711             case 'th':
16712                 switch(className) {
16713                     case 'switch':
16714                         this.showMode(1);
16715                         break;
16716                     case 'prev':
16717                     case 'next':
16718                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16719                         switch(this.viewMode){
16720                                 case 0:
16721                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16722                                         break;
16723                                 case 1:
16724                                 case 2:
16725                                         this.viewDate = this.moveYear(this.viewDate, dir);
16726                                         break;
16727                         }
16728                         this.fill();
16729                         break;
16730                     case 'today':
16731                         var date = new Date();
16732                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16733 //                        this.fill()
16734                         this.setValue(this.formatDate(this.date));
16735                         
16736                         this.hide();
16737                         break;
16738                 }
16739                 break;
16740             case 'span':
16741                 if (className.indexOf('disabled') < 0) {
16742                     this.viewDate.setUTCDate(1);
16743                     if (className.indexOf('month') > -1) {
16744                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16745                     } else {
16746                         var year = parseInt(html, 10) || 0;
16747                         this.viewDate.setUTCFullYear(year);
16748                         
16749                     }
16750                     
16751                     if(this.singleMode){
16752                         this.setValue(this.formatDate(this.viewDate));
16753                         this.hide();
16754                         return;
16755                     }
16756                     
16757                     this.showMode(-1);
16758                     this.fill();
16759                 }
16760                 break;
16761                 
16762             case 'td':
16763                 //Roo.log(className);
16764                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16765                     var day = parseInt(html, 10) || 1;
16766                     var year = this.viewDate.getUTCFullYear(),
16767                         month = this.viewDate.getUTCMonth();
16768
16769                     if (className.indexOf('old') > -1) {
16770                         if(month === 0 ){
16771                             month = 11;
16772                             year -= 1;
16773                         }else{
16774                             month -= 1;
16775                         }
16776                     } else if (className.indexOf('new') > -1) {
16777                         if (month == 11) {
16778                             month = 0;
16779                             year += 1;
16780                         } else {
16781                             month += 1;
16782                         }
16783                     }
16784                     //Roo.log([year,month,day]);
16785                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16786                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16787 //                    this.fill();
16788                     //Roo.log(this.formatDate(this.date));
16789                     this.setValue(this.formatDate(this.date));
16790                     this.hide();
16791                 }
16792                 break;
16793         }
16794     },
16795     
16796     setStartDate: function(startDate)
16797     {
16798         this.startDate = startDate || -Infinity;
16799         if (this.startDate !== -Infinity) {
16800             this.startDate = this.parseDate(this.startDate);
16801         }
16802         this.update();
16803         this.updateNavArrows();
16804     },
16805
16806     setEndDate: function(endDate)
16807     {
16808         this.endDate = endDate || Infinity;
16809         if (this.endDate !== Infinity) {
16810             this.endDate = this.parseDate(this.endDate);
16811         }
16812         this.update();
16813         this.updateNavArrows();
16814     },
16815     
16816     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16817     {
16818         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16819         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16820             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16821         }
16822         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16823             return parseInt(d, 10);
16824         });
16825         this.update();
16826         this.updateNavArrows();
16827     },
16828     
16829     updateNavArrows: function() 
16830     {
16831         if(this.singleMode){
16832             return;
16833         }
16834         
16835         var d = new Date(this.viewDate),
16836         year = d.getUTCFullYear(),
16837         month = d.getUTCMonth();
16838         
16839         Roo.each(this.picker().select('.prev', true).elements, function(v){
16840             v.show();
16841             switch (this.viewMode) {
16842                 case 0:
16843
16844                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16845                         v.hide();
16846                     }
16847                     break;
16848                 case 1:
16849                 case 2:
16850                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16851                         v.hide();
16852                     }
16853                     break;
16854             }
16855         });
16856         
16857         Roo.each(this.picker().select('.next', true).elements, function(v){
16858             v.show();
16859             switch (this.viewMode) {
16860                 case 0:
16861
16862                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16863                         v.hide();
16864                     }
16865                     break;
16866                 case 1:
16867                 case 2:
16868                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16869                         v.hide();
16870                     }
16871                     break;
16872             }
16873         })
16874     },
16875     
16876     moveMonth: function(date, dir)
16877     {
16878         if (!dir) return date;
16879         var new_date = new Date(date.valueOf()),
16880         day = new_date.getUTCDate(),
16881         month = new_date.getUTCMonth(),
16882         mag = Math.abs(dir),
16883         new_month, test;
16884         dir = dir > 0 ? 1 : -1;
16885         if (mag == 1){
16886             test = dir == -1
16887             // If going back one month, make sure month is not current month
16888             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16889             ? function(){
16890                 return new_date.getUTCMonth() == month;
16891             }
16892             // If going forward one month, make sure month is as expected
16893             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16894             : function(){
16895                 return new_date.getUTCMonth() != new_month;
16896             };
16897             new_month = month + dir;
16898             new_date.setUTCMonth(new_month);
16899             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16900             if (new_month < 0 || new_month > 11)
16901                 new_month = (new_month + 12) % 12;
16902         } else {
16903             // For magnitudes >1, move one month at a time...
16904             for (var i=0; i<mag; i++)
16905                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16906                 new_date = this.moveMonth(new_date, dir);
16907             // ...then reset the day, keeping it in the new month
16908             new_month = new_date.getUTCMonth();
16909             new_date.setUTCDate(day);
16910             test = function(){
16911                 return new_month != new_date.getUTCMonth();
16912             };
16913         }
16914         // Common date-resetting loop -- if date is beyond end of month, make it
16915         // end of month
16916         while (test()){
16917             new_date.setUTCDate(--day);
16918             new_date.setUTCMonth(new_month);
16919         }
16920         return new_date;
16921     },
16922
16923     moveYear: function(date, dir)
16924     {
16925         return this.moveMonth(date, dir*12);
16926     },
16927
16928     dateWithinRange: function(date)
16929     {
16930         return date >= this.startDate && date <= this.endDate;
16931     },
16932
16933     
16934     remove: function() 
16935     {
16936         this.picker().remove();
16937     }
16938    
16939 });
16940
16941 Roo.apply(Roo.bootstrap.DateField,  {
16942     
16943     head : {
16944         tag: 'thead',
16945         cn: [
16946         {
16947             tag: 'tr',
16948             cn: [
16949             {
16950                 tag: 'th',
16951                 cls: 'prev',
16952                 html: '<i class="fa fa-arrow-left"/>'
16953             },
16954             {
16955                 tag: 'th',
16956                 cls: 'switch',
16957                 colspan: '5'
16958             },
16959             {
16960                 tag: 'th',
16961                 cls: 'next',
16962                 html: '<i class="fa fa-arrow-right"/>'
16963             }
16964
16965             ]
16966         }
16967         ]
16968     },
16969     
16970     content : {
16971         tag: 'tbody',
16972         cn: [
16973         {
16974             tag: 'tr',
16975             cn: [
16976             {
16977                 tag: 'td',
16978                 colspan: '7'
16979             }
16980             ]
16981         }
16982         ]
16983     },
16984     
16985     footer : {
16986         tag: 'tfoot',
16987         cn: [
16988         {
16989             tag: 'tr',
16990             cn: [
16991             {
16992                 tag: 'th',
16993                 colspan: '7',
16994                 cls: 'today'
16995             }
16996                     
16997             ]
16998         }
16999         ]
17000     },
17001     
17002     dates:{
17003         en: {
17004             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17005             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17006             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17007             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17008             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17009             today: "Today"
17010         }
17011     },
17012     
17013     modes: [
17014     {
17015         clsName: 'days',
17016         navFnc: 'Month',
17017         navStep: 1
17018     },
17019     {
17020         clsName: 'months',
17021         navFnc: 'FullYear',
17022         navStep: 1
17023     },
17024     {
17025         clsName: 'years',
17026         navFnc: 'FullYear',
17027         navStep: 10
17028     }]
17029 });
17030
17031 Roo.apply(Roo.bootstrap.DateField,  {
17032   
17033     template : {
17034         tag: 'div',
17035         cls: 'datepicker dropdown-menu roo-dynamic',
17036         cn: [
17037         {
17038             tag: 'div',
17039             cls: 'datepicker-days',
17040             cn: [
17041             {
17042                 tag: 'table',
17043                 cls: 'table-condensed',
17044                 cn:[
17045                 Roo.bootstrap.DateField.head,
17046                 {
17047                     tag: 'tbody'
17048                 },
17049                 Roo.bootstrap.DateField.footer
17050                 ]
17051             }
17052             ]
17053         },
17054         {
17055             tag: 'div',
17056             cls: 'datepicker-months',
17057             cn: [
17058             {
17059                 tag: 'table',
17060                 cls: 'table-condensed',
17061                 cn:[
17062                 Roo.bootstrap.DateField.head,
17063                 Roo.bootstrap.DateField.content,
17064                 Roo.bootstrap.DateField.footer
17065                 ]
17066             }
17067             ]
17068         },
17069         {
17070             tag: 'div',
17071             cls: 'datepicker-years',
17072             cn: [
17073             {
17074                 tag: 'table',
17075                 cls: 'table-condensed',
17076                 cn:[
17077                 Roo.bootstrap.DateField.head,
17078                 Roo.bootstrap.DateField.content,
17079                 Roo.bootstrap.DateField.footer
17080                 ]
17081             }
17082             ]
17083         }
17084         ]
17085     }
17086 });
17087
17088  
17089
17090  /*
17091  * - LGPL
17092  *
17093  * TimeField
17094  * 
17095  */
17096
17097 /**
17098  * @class Roo.bootstrap.TimeField
17099  * @extends Roo.bootstrap.Input
17100  * Bootstrap DateField class
17101  * 
17102  * 
17103  * @constructor
17104  * Create a new TimeField
17105  * @param {Object} config The config object
17106  */
17107
17108 Roo.bootstrap.TimeField = function(config){
17109     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17110     this.addEvents({
17111             /**
17112              * @event show
17113              * Fires when this field show.
17114              * @param {Roo.bootstrap.DateField} thisthis
17115              * @param {Mixed} date The date value
17116              */
17117             show : true,
17118             /**
17119              * @event show
17120              * Fires when this field hide.
17121              * @param {Roo.bootstrap.DateField} this
17122              * @param {Mixed} date The date value
17123              */
17124             hide : true,
17125             /**
17126              * @event select
17127              * Fires when select a date.
17128              * @param {Roo.bootstrap.DateField} this
17129              * @param {Mixed} date The date value
17130              */
17131             select : true
17132         });
17133 };
17134
17135 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17136     
17137     /**
17138      * @cfg {String} format
17139      * The default time format string which can be overriden for localization support.  The format must be
17140      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17141      */
17142     format : "H:i",
17143        
17144     onRender: function(ct, position)
17145     {
17146         
17147         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17148                 
17149         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17150         
17151         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17152         
17153         this.pop = this.picker().select('>.datepicker-time',true).first();
17154         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17155         
17156         this.picker().on('mousedown', this.onMousedown, this);
17157         this.picker().on('click', this.onClick, this);
17158         
17159         this.picker().addClass('datepicker-dropdown');
17160     
17161         this.fillTime();
17162         this.update();
17163             
17164         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17165         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17166         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17167         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17168         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17169         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17170
17171     },
17172     
17173     fireKey: function(e){
17174         if (!this.picker().isVisible()){
17175             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17176                 this.show();
17177             }
17178             return;
17179         }
17180
17181         e.preventDefault();
17182         
17183         switch(e.keyCode){
17184             case 27: // escape
17185                 this.hide();
17186                 break;
17187             case 37: // left
17188             case 39: // right
17189                 this.onTogglePeriod();
17190                 break;
17191             case 38: // up
17192                 this.onIncrementMinutes();
17193                 break;
17194             case 40: // down
17195                 this.onDecrementMinutes();
17196                 break;
17197             case 13: // enter
17198             case 9: // tab
17199                 this.setTime();
17200                 break;
17201         }
17202     },
17203     
17204     onClick: function(e) {
17205         e.stopPropagation();
17206         e.preventDefault();
17207     },
17208     
17209     picker : function()
17210     {
17211         return this.el.select('.datepicker', true).first();
17212     },
17213     
17214     fillTime: function()
17215     {    
17216         var time = this.pop.select('tbody', true).first();
17217         
17218         time.dom.innerHTML = '';
17219         
17220         time.createChild({
17221             tag: 'tr',
17222             cn: [
17223                 {
17224                     tag: 'td',
17225                     cn: [
17226                         {
17227                             tag: 'a',
17228                             href: '#',
17229                             cls: 'btn',
17230                             cn: [
17231                                 {
17232                                     tag: 'span',
17233                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17234                                 }
17235                             ]
17236                         } 
17237                     ]
17238                 },
17239                 {
17240                     tag: 'td',
17241                     cls: 'separator'
17242                 },
17243                 {
17244                     tag: 'td',
17245                     cn: [
17246                         {
17247                             tag: 'a',
17248                             href: '#',
17249                             cls: 'btn',
17250                             cn: [
17251                                 {
17252                                     tag: 'span',
17253                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17254                                 }
17255                             ]
17256                         }
17257                     ]
17258                 },
17259                 {
17260                     tag: 'td',
17261                     cls: 'separator'
17262                 }
17263             ]
17264         });
17265         
17266         time.createChild({
17267             tag: 'tr',
17268             cn: [
17269                 {
17270                     tag: 'td',
17271                     cn: [
17272                         {
17273                             tag: 'span',
17274                             cls: 'timepicker-hour',
17275                             html: '00'
17276                         }  
17277                     ]
17278                 },
17279                 {
17280                     tag: 'td',
17281                     cls: 'separator',
17282                     html: ':'
17283                 },
17284                 {
17285                     tag: 'td',
17286                     cn: [
17287                         {
17288                             tag: 'span',
17289                             cls: 'timepicker-minute',
17290                             html: '00'
17291                         }  
17292                     ]
17293                 },
17294                 {
17295                     tag: 'td',
17296                     cls: 'separator'
17297                 },
17298                 {
17299                     tag: 'td',
17300                     cn: [
17301                         {
17302                             tag: 'button',
17303                             type: 'button',
17304                             cls: 'btn btn-primary period',
17305                             html: 'AM'
17306                             
17307                         }
17308                     ]
17309                 }
17310             ]
17311         });
17312         
17313         time.createChild({
17314             tag: 'tr',
17315             cn: [
17316                 {
17317                     tag: 'td',
17318                     cn: [
17319                         {
17320                             tag: 'a',
17321                             href: '#',
17322                             cls: 'btn',
17323                             cn: [
17324                                 {
17325                                     tag: 'span',
17326                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17327                                 }
17328                             ]
17329                         }
17330                     ]
17331                 },
17332                 {
17333                     tag: 'td',
17334                     cls: 'separator'
17335                 },
17336                 {
17337                     tag: 'td',
17338                     cn: [
17339                         {
17340                             tag: 'a',
17341                             href: '#',
17342                             cls: 'btn',
17343                             cn: [
17344                                 {
17345                                     tag: 'span',
17346                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17347                                 }
17348                             ]
17349                         }
17350                     ]
17351                 },
17352                 {
17353                     tag: 'td',
17354                     cls: 'separator'
17355                 }
17356             ]
17357         });
17358         
17359     },
17360     
17361     update: function()
17362     {
17363         
17364         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17365         
17366         this.fill();
17367     },
17368     
17369     fill: function() 
17370     {
17371         var hours = this.time.getHours();
17372         var minutes = this.time.getMinutes();
17373         var period = 'AM';
17374         
17375         if(hours > 11){
17376             period = 'PM';
17377         }
17378         
17379         if(hours == 0){
17380             hours = 12;
17381         }
17382         
17383         
17384         if(hours > 12){
17385             hours = hours - 12;
17386         }
17387         
17388         if(hours < 10){
17389             hours = '0' + hours;
17390         }
17391         
17392         if(minutes < 10){
17393             minutes = '0' + minutes;
17394         }
17395         
17396         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17397         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17398         this.pop.select('button', true).first().dom.innerHTML = period;
17399         
17400     },
17401     
17402     place: function()
17403     {   
17404         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17405         
17406         var cls = ['bottom'];
17407         
17408         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17409             cls.pop();
17410             cls.push('top');
17411         }
17412         
17413         cls.push('right');
17414         
17415         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17416             cls.pop();
17417             cls.push('left');
17418         }
17419         
17420         this.picker().addClass(cls.join('-'));
17421         
17422         var _this = this;
17423         
17424         Roo.each(cls, function(c){
17425             if(c == 'bottom'){
17426                 _this.picker().setTop(_this.inputEl().getHeight());
17427                 return;
17428             }
17429             if(c == 'top'){
17430                 _this.picker().setTop(0 - _this.picker().getHeight());
17431                 return;
17432             }
17433             
17434             if(c == 'left'){
17435                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17436                 return;
17437             }
17438             if(c == 'right'){
17439                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17440                 return;
17441             }
17442         });
17443         
17444     },
17445   
17446     onFocus : function()
17447     {
17448         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17449         this.show();
17450     },
17451     
17452     onBlur : function()
17453     {
17454         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17455         this.hide();
17456     },
17457     
17458     show : function()
17459     {
17460         this.picker().show();
17461         this.pop.show();
17462         this.update();
17463         this.place();
17464         
17465         this.fireEvent('show', this, this.date);
17466     },
17467     
17468     hide : function()
17469     {
17470         this.picker().hide();
17471         this.pop.hide();
17472         
17473         this.fireEvent('hide', this, this.date);
17474     },
17475     
17476     setTime : function()
17477     {
17478         this.hide();
17479         this.setValue(this.time.format(this.format));
17480         
17481         this.fireEvent('select', this, this.date);
17482         
17483         
17484     },
17485     
17486     onMousedown: function(e){
17487         e.stopPropagation();
17488         e.preventDefault();
17489     },
17490     
17491     onIncrementHours: function()
17492     {
17493         Roo.log('onIncrementHours');
17494         this.time = this.time.add(Date.HOUR, 1);
17495         this.update();
17496         
17497     },
17498     
17499     onDecrementHours: function()
17500     {
17501         Roo.log('onDecrementHours');
17502         this.time = this.time.add(Date.HOUR, -1);
17503         this.update();
17504     },
17505     
17506     onIncrementMinutes: function()
17507     {
17508         Roo.log('onIncrementMinutes');
17509         this.time = this.time.add(Date.MINUTE, 1);
17510         this.update();
17511     },
17512     
17513     onDecrementMinutes: function()
17514     {
17515         Roo.log('onDecrementMinutes');
17516         this.time = this.time.add(Date.MINUTE, -1);
17517         this.update();
17518     },
17519     
17520     onTogglePeriod: function()
17521     {
17522         Roo.log('onTogglePeriod');
17523         this.time = this.time.add(Date.HOUR, 12);
17524         this.update();
17525     }
17526     
17527    
17528 });
17529
17530 Roo.apply(Roo.bootstrap.TimeField,  {
17531     
17532     content : {
17533         tag: 'tbody',
17534         cn: [
17535             {
17536                 tag: 'tr',
17537                 cn: [
17538                 {
17539                     tag: 'td',
17540                     colspan: '7'
17541                 }
17542                 ]
17543             }
17544         ]
17545     },
17546     
17547     footer : {
17548         tag: 'tfoot',
17549         cn: [
17550             {
17551                 tag: 'tr',
17552                 cn: [
17553                 {
17554                     tag: 'th',
17555                     colspan: '7',
17556                     cls: '',
17557                     cn: [
17558                         {
17559                             tag: 'button',
17560                             cls: 'btn btn-info ok',
17561                             html: 'OK'
17562                         }
17563                     ]
17564                 }
17565
17566                 ]
17567             }
17568         ]
17569     }
17570 });
17571
17572 Roo.apply(Roo.bootstrap.TimeField,  {
17573   
17574     template : {
17575         tag: 'div',
17576         cls: 'datepicker dropdown-menu',
17577         cn: [
17578             {
17579                 tag: 'div',
17580                 cls: 'datepicker-time',
17581                 cn: [
17582                 {
17583                     tag: 'table',
17584                     cls: 'table-condensed',
17585                     cn:[
17586                     Roo.bootstrap.TimeField.content,
17587                     Roo.bootstrap.TimeField.footer
17588                     ]
17589                 }
17590                 ]
17591             }
17592         ]
17593     }
17594 });
17595
17596  
17597
17598  /*
17599  * - LGPL
17600  *
17601  * MonthField
17602  * 
17603  */
17604
17605 /**
17606  * @class Roo.bootstrap.MonthField
17607  * @extends Roo.bootstrap.Input
17608  * Bootstrap MonthField class
17609  * 
17610  * @cfg {String} language default en
17611  * 
17612  * @constructor
17613  * Create a new MonthField
17614  * @param {Object} config The config object
17615  */
17616
17617 Roo.bootstrap.MonthField = function(config){
17618     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17619     
17620     this.addEvents({
17621         /**
17622          * @event show
17623          * Fires when this field show.
17624          * @param {Roo.bootstrap.MonthField} this
17625          * @param {Mixed} date The date value
17626          */
17627         show : true,
17628         /**
17629          * @event show
17630          * Fires when this field hide.
17631          * @param {Roo.bootstrap.MonthField} this
17632          * @param {Mixed} date The date value
17633          */
17634         hide : true,
17635         /**
17636          * @event select
17637          * Fires when select a date.
17638          * @param {Roo.bootstrap.MonthField} this
17639          * @param {String} oldvalue The old value
17640          * @param {String} newvalue The new value
17641          */
17642         select : true
17643     });
17644 };
17645
17646 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17647     
17648     onRender: function(ct, position)
17649     {
17650         
17651         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17652         
17653         this.language = this.language || 'en';
17654         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17655         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17656         
17657         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17658         this.isInline = false;
17659         this.isInput = true;
17660         this.component = this.el.select('.add-on', true).first() || false;
17661         this.component = (this.component && this.component.length === 0) ? false : this.component;
17662         this.hasInput = this.component && this.inputEL().length;
17663         
17664         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17665         
17666         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17667         
17668         this.picker().on('mousedown', this.onMousedown, this);
17669         this.picker().on('click', this.onClick, this);
17670         
17671         this.picker().addClass('datepicker-dropdown');
17672         
17673         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17674             v.setStyle('width', '189px');
17675         });
17676         
17677         this.fillMonths();
17678         
17679         this.update();
17680         
17681         if(this.isInline) {
17682             this.show();
17683         }
17684         
17685     },
17686     
17687     setValue: function(v, suppressEvent)
17688     {   
17689         var o = this.getValue();
17690         
17691         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17692         
17693         this.update();
17694
17695         if(suppressEvent !== true){
17696             this.fireEvent('select', this, o, v);
17697         }
17698         
17699     },
17700     
17701     getValue: function()
17702     {
17703         return this.value;
17704     },
17705     
17706     onClick: function(e) 
17707     {
17708         e.stopPropagation();
17709         e.preventDefault();
17710         
17711         var target = e.getTarget();
17712         
17713         if(target.nodeName.toLowerCase() === 'i'){
17714             target = Roo.get(target).dom.parentNode;
17715         }
17716         
17717         var nodeName = target.nodeName;
17718         var className = target.className;
17719         var html = target.innerHTML;
17720         
17721         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17722             return;
17723         }
17724         
17725         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17726         
17727         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17728         
17729         this.hide();
17730                         
17731     },
17732     
17733     picker : function()
17734     {
17735         return this.pickerEl;
17736     },
17737     
17738     fillMonths: function()
17739     {    
17740         var i = 0;
17741         var months = this.picker().select('>.datepicker-months td', true).first();
17742         
17743         months.dom.innerHTML = '';
17744         
17745         while (i < 12) {
17746             var month = {
17747                 tag: 'span',
17748                 cls: 'month',
17749                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17750             }
17751             
17752             months.createChild(month);
17753         }
17754         
17755     },
17756     
17757     update: function()
17758     {
17759         var _this = this;
17760         
17761         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17762             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17763         }
17764         
17765         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17766             e.removeClass('active');
17767             
17768             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17769                 e.addClass('active');
17770             }
17771         })
17772     },
17773     
17774     place: function()
17775     {
17776         if(this.isInline) return;
17777         
17778         this.picker().removeClass(['bottom', 'top']);
17779         
17780         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17781             /*
17782              * place to the top of element!
17783              *
17784              */
17785             
17786             this.picker().addClass('top');
17787             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17788             
17789             return;
17790         }
17791         
17792         this.picker().addClass('bottom');
17793         
17794         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17795     },
17796     
17797     onFocus : function()
17798     {
17799         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17800         this.show();
17801     },
17802     
17803     onBlur : function()
17804     {
17805         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17806         
17807         var d = this.inputEl().getValue();
17808         
17809         this.setValue(d);
17810                 
17811         this.hide();
17812     },
17813     
17814     show : function()
17815     {
17816         this.picker().show();
17817         this.picker().select('>.datepicker-months', true).first().show();
17818         this.update();
17819         this.place();
17820         
17821         this.fireEvent('show', this, this.date);
17822     },
17823     
17824     hide : function()
17825     {
17826         if(this.isInline) return;
17827         this.picker().hide();
17828         this.fireEvent('hide', this, this.date);
17829         
17830     },
17831     
17832     onMousedown: function(e)
17833     {
17834         e.stopPropagation();
17835         e.preventDefault();
17836     },
17837     
17838     keyup: function(e)
17839     {
17840         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17841         this.update();
17842     },
17843
17844     fireKey: function(e)
17845     {
17846         if (!this.picker().isVisible()){
17847             if (e.keyCode == 27) // allow escape to hide and re-show picker
17848                 this.show();
17849             return;
17850         }
17851         
17852         var dir;
17853         
17854         switch(e.keyCode){
17855             case 27: // escape
17856                 this.hide();
17857                 e.preventDefault();
17858                 break;
17859             case 37: // left
17860             case 39: // right
17861                 dir = e.keyCode == 37 ? -1 : 1;
17862                 
17863                 this.vIndex = this.vIndex + dir;
17864                 
17865                 if(this.vIndex < 0){
17866                     this.vIndex = 0;
17867                 }
17868                 
17869                 if(this.vIndex > 11){
17870                     this.vIndex = 11;
17871                 }
17872                 
17873                 if(isNaN(this.vIndex)){
17874                     this.vIndex = 0;
17875                 }
17876                 
17877                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17878                 
17879                 break;
17880             case 38: // up
17881             case 40: // down
17882                 
17883                 dir = e.keyCode == 38 ? -1 : 1;
17884                 
17885                 this.vIndex = this.vIndex + dir * 4;
17886                 
17887                 if(this.vIndex < 0){
17888                     this.vIndex = 0;
17889                 }
17890                 
17891                 if(this.vIndex > 11){
17892                     this.vIndex = 11;
17893                 }
17894                 
17895                 if(isNaN(this.vIndex)){
17896                     this.vIndex = 0;
17897                 }
17898                 
17899                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17900                 break;
17901                 
17902             case 13: // enter
17903                 
17904                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17905                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17906                 }
17907                 
17908                 this.hide();
17909                 e.preventDefault();
17910                 break;
17911             case 9: // tab
17912                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17913                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17914                 }
17915                 this.hide();
17916                 break;
17917             case 16: // shift
17918             case 17: // ctrl
17919             case 18: // alt
17920                 break;
17921             default :
17922                 this.hide();
17923                 
17924         }
17925     },
17926     
17927     remove: function() 
17928     {
17929         this.picker().remove();
17930     }
17931    
17932 });
17933
17934 Roo.apply(Roo.bootstrap.MonthField,  {
17935     
17936     content : {
17937         tag: 'tbody',
17938         cn: [
17939         {
17940             tag: 'tr',
17941             cn: [
17942             {
17943                 tag: 'td',
17944                 colspan: '7'
17945             }
17946             ]
17947         }
17948         ]
17949     },
17950     
17951     dates:{
17952         en: {
17953             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17954             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17955         }
17956     }
17957 });
17958
17959 Roo.apply(Roo.bootstrap.MonthField,  {
17960   
17961     template : {
17962         tag: 'div',
17963         cls: 'datepicker dropdown-menu roo-dynamic',
17964         cn: [
17965             {
17966                 tag: 'div',
17967                 cls: 'datepicker-months',
17968                 cn: [
17969                 {
17970                     tag: 'table',
17971                     cls: 'table-condensed',
17972                     cn:[
17973                         Roo.bootstrap.DateField.content
17974                     ]
17975                 }
17976                 ]
17977             }
17978         ]
17979     }
17980 });
17981
17982  
17983
17984  
17985  /*
17986  * - LGPL
17987  *
17988  * CheckBox
17989  * 
17990  */
17991
17992 /**
17993  * @class Roo.bootstrap.CheckBox
17994  * @extends Roo.bootstrap.Input
17995  * Bootstrap CheckBox class
17996  * 
17997  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17998  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17999  * @cfg {String} boxLabel The text that appears beside the checkbox
18000  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18001  * @cfg {Boolean} checked initnal the element
18002  * @cfg {Boolean} inline inline the element (default false)
18003  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18004  * 
18005  * @constructor
18006  * Create a new CheckBox
18007  * @param {Object} config The config object
18008  */
18009
18010 Roo.bootstrap.CheckBox = function(config){
18011     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18012    
18013     this.addEvents({
18014         /**
18015         * @event check
18016         * Fires when the element is checked or unchecked.
18017         * @param {Roo.bootstrap.CheckBox} this This input
18018         * @param {Boolean} checked The new checked value
18019         */
18020        check : true
18021     });
18022     
18023 };
18024
18025 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18026   
18027     inputType: 'checkbox',
18028     inputValue: 1,
18029     valueOff: 0,
18030     boxLabel: false,
18031     checked: false,
18032     weight : false,
18033     inline: false,
18034     
18035     getAutoCreate : function()
18036     {
18037         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18038         
18039         var id = Roo.id();
18040         
18041         var cfg = {};
18042         
18043         cfg.cls = 'form-group ' + this.inputType; //input-group
18044         
18045         if(this.inline){
18046             cfg.cls += ' ' + this.inputType + '-inline';
18047         }
18048         
18049         var input =  {
18050             tag: 'input',
18051             id : id,
18052             type : this.inputType,
18053             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18054             cls : 'roo-' + this.inputType, //'form-box',
18055             placeholder : this.placeholder || ''
18056             
18057         };
18058         
18059         if (this.weight) { // Validity check?
18060             cfg.cls += " " + this.inputType + "-" + this.weight;
18061         }
18062         
18063         if (this.disabled) {
18064             input.disabled=true;
18065         }
18066         
18067         if(this.checked){
18068             input.checked = this.checked;
18069         }
18070         
18071         if (this.name) {
18072             input.name = this.name;
18073         }
18074         
18075         if (this.size) {
18076             input.cls += ' input-' + this.size;
18077         }
18078         
18079         var settings=this;
18080         
18081         ['xs','sm','md','lg'].map(function(size){
18082             if (settings[size]) {
18083                 cfg.cls += ' col-' + size + '-' + settings[size];
18084             }
18085         });
18086         
18087         var inputblock = input;
18088          
18089         if (this.before || this.after) {
18090             
18091             inputblock = {
18092                 cls : 'input-group',
18093                 cn :  [] 
18094             };
18095             
18096             if (this.before) {
18097                 inputblock.cn.push({
18098                     tag :'span',
18099                     cls : 'input-group-addon',
18100                     html : this.before
18101                 });
18102             }
18103             
18104             inputblock.cn.push(input);
18105             
18106             if (this.after) {
18107                 inputblock.cn.push({
18108                     tag :'span',
18109                     cls : 'input-group-addon',
18110                     html : this.after
18111                 });
18112             }
18113             
18114         }
18115         
18116         if (align ==='left' && this.fieldLabel.length) {
18117                 Roo.log("left and has label");
18118                 cfg.cn = [
18119                     
18120                     {
18121                         tag: 'label',
18122                         'for' :  id,
18123                         cls : 'control-label col-md-' + this.labelWidth,
18124                         html : this.fieldLabel
18125                         
18126                     },
18127                     {
18128                         cls : "col-md-" + (12 - this.labelWidth), 
18129                         cn: [
18130                             inputblock
18131                         ]
18132                     }
18133                     
18134                 ];
18135         } else if ( this.fieldLabel.length) {
18136                 Roo.log(" label");
18137                 cfg.cn = [
18138                    
18139                     {
18140                         tag: this.boxLabel ? 'span' : 'label',
18141                         'for': id,
18142                         cls: 'control-label box-input-label',
18143                         //cls : 'input-group-addon',
18144                         html : this.fieldLabel
18145                         
18146                     },
18147                     
18148                     inputblock
18149                     
18150                 ];
18151
18152         } else {
18153             
18154                 Roo.log(" no label && no align");
18155                 cfg.cn = [  inputblock ] ;
18156                 
18157                 
18158         }
18159         if(this.boxLabel){
18160              var boxLabelCfg = {
18161                 tag: 'label',
18162                 //'for': id, // box label is handled by onclick - so no for...
18163                 cls: 'box-label',
18164                 html: this.boxLabel
18165             }
18166             
18167             if(this.tooltip){
18168                 boxLabelCfg.tooltip = this.tooltip;
18169             }
18170              
18171             cfg.cn.push(boxLabelCfg);
18172         }
18173         
18174         
18175        
18176         return cfg;
18177         
18178     },
18179     
18180     /**
18181      * return the real input element.
18182      */
18183     inputEl: function ()
18184     {
18185         return this.el.select('input.roo-' + this.inputType,true).first();
18186     },
18187     
18188     labelEl: function()
18189     {
18190         return this.el.select('label.control-label',true).first();
18191     },
18192     /* depricated... */
18193     
18194     label: function()
18195     {
18196         return this.labelEl();
18197     },
18198     
18199     initEvents : function()
18200     {
18201 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18202         
18203         this.inputEl().on('click', this.onClick,  this);
18204         
18205         if (this.boxLabel) { 
18206             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18207         }
18208         
18209         this.startValue = this.getValue();
18210         
18211         if(this.groupId){
18212             Roo.bootstrap.CheckBox.register(this);
18213         }
18214     },
18215     
18216     onClick : function()
18217     {   
18218         this.setChecked(!this.checked);
18219     },
18220     
18221     setChecked : function(state,suppressEvent)
18222     {
18223         this.startValue = this.getValue();
18224         
18225         if(this.inputType == 'radio'){
18226             
18227             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18228                 e.dom.checked = false;
18229             });
18230             
18231             this.inputEl().dom.checked = true;
18232             
18233             this.inputEl().dom.value = this.inputValue;
18234             
18235             if(suppressEvent !== true){
18236                 this.fireEvent('check', this, true);
18237             }
18238             
18239             this.validate();
18240             
18241             return;
18242         }
18243         
18244         this.checked = state;
18245         
18246         this.inputEl().dom.checked = state;
18247         
18248         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18249         
18250         if(suppressEvent !== true){
18251             this.fireEvent('check', this, state);
18252         }
18253         
18254         this.validate();
18255     },
18256     
18257     getValue : function()
18258     {
18259         if(this.inputType == 'radio'){
18260             return this.getGroupValue();
18261         }
18262         
18263         return this.inputEl().getValue();
18264         
18265     },
18266     
18267     getGroupValue : function()
18268     {
18269         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18270             return '';
18271         }
18272         
18273         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18274     },
18275     
18276     setValue : function(v,suppressEvent)
18277     {
18278         if(this.inputType == 'radio'){
18279             this.setGroupValue(v, suppressEvent);
18280             return;
18281         }
18282         
18283         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18284         
18285         this.validate();
18286     },
18287     
18288     setGroupValue : function(v, suppressEvent)
18289     {
18290         this.startValue = this.getValue();
18291         
18292         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18293             e.dom.checked = false;
18294             
18295             if(e.dom.value == v){
18296                 e.dom.checked = true;
18297             }
18298         });
18299         
18300         if(suppressEvent !== true){
18301             this.fireEvent('check', this, true);
18302         }
18303
18304         this.validate();
18305         
18306         return;
18307     },
18308     
18309     validate : function()
18310     {
18311         if(
18312                 this.disabled || 
18313                 (this.inputType == 'radio' && this.validateRadio()) ||
18314                 (this.inputType == 'checkbox' && this.validateCheckbox())
18315         ){
18316             this.markValid();
18317             return true;
18318         }
18319         
18320         this.markInvalid();
18321         return false;
18322     },
18323     
18324     validateRadio : function()
18325     {
18326         var valid = false;
18327         
18328         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18329             if(!e.dom.checked){
18330                 return;
18331             }
18332             
18333             valid = true;
18334             
18335             return false;
18336         });
18337         
18338         return valid;
18339     },
18340     
18341     validateCheckbox : function()
18342     {
18343         if(!this.groupId){
18344             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18345         }
18346         
18347         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18348         
18349         if(!group){
18350             return false;
18351         }
18352         
18353         var r = false;
18354         
18355         for(var i in group){
18356             if(r){
18357                 break;
18358             }
18359             
18360             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18361         }
18362         
18363         return r;
18364     },
18365     
18366     /**
18367      * Mark this field as valid
18368      */
18369     markValid : function()
18370     {
18371         if(this.allowBlank){
18372             return;
18373         }
18374         
18375         var _this = this;
18376         
18377         this.fireEvent('valid', this);
18378         
18379         if(this.inputType == 'radio'){
18380             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18381                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18382                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18383             });
18384             
18385             return;
18386         }
18387         
18388         if(!this.groupId){
18389             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18390             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18391             return;
18392         }
18393         
18394         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18395             
18396         if(!group){
18397             return;
18398         }
18399         
18400         for(var i in group){
18401             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18402             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18403         }
18404     },
18405     
18406      /**
18407      * Mark this field as invalid
18408      * @param {String} msg The validation message
18409      */
18410     markInvalid : function(msg)
18411     {
18412         if(this.allowBlank){
18413             return;
18414         }
18415         
18416         var _this = this;
18417         
18418         this.fireEvent('invalid', this, msg);
18419         
18420         if(this.inputType == 'radio'){
18421             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18422                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18423                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18424             });
18425             
18426             return;
18427         }
18428         
18429         if(!this.groupId){
18430             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18431             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18432             return;
18433         }
18434         
18435         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18436             
18437         if(!group){
18438             return;
18439         }
18440         
18441         for(var i in group){
18442             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18443             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18444         }
18445         
18446     }
18447     
18448 });
18449
18450 Roo.apply(Roo.bootstrap.CheckBox, {
18451     
18452     groups: {},
18453     
18454      /**
18455     * register a CheckBox Group
18456     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18457     */
18458     register : function(checkbox)
18459     {
18460         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18461             this.groups[checkbox.groupId] = {};
18462         }
18463         
18464         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18465             return;
18466         }
18467         
18468         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18469         
18470     },
18471     /**
18472     * fetch a CheckBox Group based on the group ID
18473     * @param {string} the group ID
18474     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18475     */
18476     get: function(groupId) {
18477         if (typeof(this.groups[groupId]) == 'undefined') {
18478             return false;
18479         }
18480         
18481         return this.groups[groupId] ;
18482     }
18483     
18484     
18485 });
18486 /*
18487  * - LGPL
18488  *
18489  * Radio
18490  *
18491  *
18492  * not inline
18493  *<div class="radio">
18494   <label>
18495     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18496     Option one is this and that&mdash;be sure to include why it's great
18497   </label>
18498 </div>
18499  *
18500  *
18501  *inline
18502  *<span>
18503  *<label class="radio-inline">fieldLabel</label>
18504  *<label class="radio-inline">
18505   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18506 </label>
18507 <span>
18508  * 
18509  * 
18510  */
18511
18512 /**
18513  * @class Roo.bootstrap.Radio
18514  * @extends Roo.bootstrap.CheckBox
18515  * Bootstrap Radio class
18516
18517  * @constructor
18518  * Create a new Radio
18519  * @param {Object} config The config object
18520  */
18521
18522 Roo.bootstrap.Radio = function(config){
18523     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18524    
18525 };
18526
18527 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18528     
18529     inputType: 'radio',
18530     inputValue: '',
18531     valueOff: '',
18532     
18533     getAutoCreate : function()
18534     {
18535         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18536         align = align || 'left'; // default...
18537         
18538         
18539         
18540         var id = Roo.id();
18541         
18542         var cfg = {
18543                 tag : this.inline ? 'span' : 'div',
18544                 cls : '',
18545                 cn : []
18546         };
18547         
18548         var inline = this.inline ? ' radio-inline' : '';
18549         
18550         var lbl = {
18551                 tag: 'label' ,
18552                 // does not need for, as we wrap the input with it..
18553                 'for' : id,
18554                 cls : 'control-label box-label' + inline,
18555                 cn : []
18556         };
18557         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18558         
18559         var fieldLabel = {
18560             tag: 'label' ,
18561             //cls : 'control-label' + inline,
18562             html : this.fieldLabel,
18563             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18564         };
18565         
18566  
18567         
18568         
18569         var input =  {
18570             tag: 'input',
18571             id : id,
18572             type : this.inputType,
18573             //value : (!this.checked) ? this.valueOff : this.inputValue,
18574             value : this.inputValue,
18575             cls : 'roo-radio',
18576             placeholder : this.placeholder || '' // ?? needed????
18577             
18578         };
18579         if (this.weight) { // Validity check?
18580             input.cls += " radio-" + this.weight;
18581         }
18582         if (this.disabled) {
18583             input.disabled=true;
18584         }
18585         
18586         if(this.checked){
18587             input.checked = this.checked;
18588         }
18589         
18590         if (this.name) {
18591             input.name = this.name;
18592         }
18593         
18594         if (this.size) {
18595             input.cls += ' input-' + this.size;
18596         }
18597         
18598         //?? can span's inline have a width??
18599         
18600         var settings=this;
18601         ['xs','sm','md','lg'].map(function(size){
18602             if (settings[size]) {
18603                 cfg.cls += ' col-' + size + '-' + settings[size];
18604             }
18605         });
18606         
18607         var inputblock = input;
18608         
18609         if (this.before || this.after) {
18610             
18611             inputblock = {
18612                 cls : 'input-group',
18613                 tag : 'span',
18614                 cn :  [] 
18615             };
18616             if (this.before) {
18617                 inputblock.cn.push({
18618                     tag :'span',
18619                     cls : 'input-group-addon',
18620                     html : this.before
18621                 });
18622             }
18623             inputblock.cn.push(input);
18624             if (this.after) {
18625                 inputblock.cn.push({
18626                     tag :'span',
18627                     cls : 'input-group-addon',
18628                     html : this.after
18629                 });
18630             }
18631             
18632         };
18633         
18634         
18635         if (this.fieldLabel && this.fieldLabel.length) {
18636             cfg.cn.push(fieldLabel);
18637         }
18638        
18639         // normal bootstrap puts the input inside the label.
18640         // however with our styled version - it has to go after the input.
18641        
18642         //lbl.cn.push(inputblock);
18643         
18644         var lblwrap =  {
18645             tag: 'span',
18646             cls: 'radio' + inline,
18647             cn: [
18648                 inputblock,
18649                 lbl
18650             ]
18651         };
18652         
18653         cfg.cn.push( lblwrap);
18654         
18655         if(this.boxLabel){
18656             lbl.cn.push({
18657                 tag: 'span',
18658                 html: this.boxLabel
18659             })
18660         }
18661          
18662         
18663         return cfg;
18664         
18665     },
18666     
18667     initEvents : function()
18668     {
18669 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18670         
18671         this.inputEl().on('click', this.onClick,  this);
18672         if (this.boxLabel) {
18673             Roo.log('find label')
18674             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18675         }
18676         
18677     },
18678     
18679     inputEl: function ()
18680     {
18681         return this.el.select('input.roo-radio',true).first();
18682     },
18683     onClick : function()
18684     {   
18685         Roo.log("click");
18686         this.setChecked(true);
18687     },
18688     
18689     setChecked : function(state,suppressEvent)
18690     {
18691         if(state){
18692             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18693                 v.dom.checked = false;
18694             });
18695         }
18696         Roo.log(this.inputEl().dom);
18697         this.checked = state;
18698         this.inputEl().dom.checked = state;
18699         
18700         if(suppressEvent !== true){
18701             this.fireEvent('check', this, state);
18702         }
18703         
18704         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18705         
18706     },
18707     
18708     getGroupValue : function()
18709     {
18710         var value = '';
18711         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18712             if(v.dom.checked == true){
18713                 value = v.dom.value;
18714             }
18715         });
18716         
18717         return value;
18718     },
18719     
18720     /**
18721      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18722      * @return {Mixed} value The field value
18723      */
18724     getValue : function(){
18725         return this.getGroupValue();
18726     }
18727     
18728 });
18729
18730  
18731 //<script type="text/javascript">
18732
18733 /*
18734  * Based  Ext JS Library 1.1.1
18735  * Copyright(c) 2006-2007, Ext JS, LLC.
18736  * LGPL
18737  *
18738  */
18739  
18740 /**
18741  * @class Roo.HtmlEditorCore
18742  * @extends Roo.Component
18743  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18744  *
18745  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18746  */
18747
18748 Roo.HtmlEditorCore = function(config){
18749     
18750     
18751     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18752     
18753     
18754     this.addEvents({
18755         /**
18756          * @event initialize
18757          * Fires when the editor is fully initialized (including the iframe)
18758          * @param {Roo.HtmlEditorCore} this
18759          */
18760         initialize: true,
18761         /**
18762          * @event activate
18763          * Fires when the editor is first receives the focus. Any insertion must wait
18764          * until after this event.
18765          * @param {Roo.HtmlEditorCore} this
18766          */
18767         activate: true,
18768          /**
18769          * @event beforesync
18770          * Fires before the textarea is updated with content from the editor iframe. Return false
18771          * to cancel the sync.
18772          * @param {Roo.HtmlEditorCore} this
18773          * @param {String} html
18774          */
18775         beforesync: true,
18776          /**
18777          * @event beforepush
18778          * Fires before the iframe editor is updated with content from the textarea. Return false
18779          * to cancel the push.
18780          * @param {Roo.HtmlEditorCore} this
18781          * @param {String} html
18782          */
18783         beforepush: true,
18784          /**
18785          * @event sync
18786          * Fires when the textarea is updated with content from the editor iframe.
18787          * @param {Roo.HtmlEditorCore} this
18788          * @param {String} html
18789          */
18790         sync: true,
18791          /**
18792          * @event push
18793          * Fires when the iframe editor is updated with content from the textarea.
18794          * @param {Roo.HtmlEditorCore} this
18795          * @param {String} html
18796          */
18797         push: true,
18798         
18799         /**
18800          * @event editorevent
18801          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18802          * @param {Roo.HtmlEditorCore} this
18803          */
18804         editorevent: true
18805         
18806     });
18807     
18808     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18809     
18810     // defaults : white / black...
18811     this.applyBlacklists();
18812     
18813     
18814     
18815 };
18816
18817
18818 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18819
18820
18821      /**
18822      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18823      */
18824     
18825     owner : false,
18826     
18827      /**
18828      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18829      *                        Roo.resizable.
18830      */
18831     resizable : false,
18832      /**
18833      * @cfg {Number} height (in pixels)
18834      */   
18835     height: 300,
18836    /**
18837      * @cfg {Number} width (in pixels)
18838      */   
18839     width: 500,
18840     
18841     /**
18842      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18843      * 
18844      */
18845     stylesheets: false,
18846     
18847     // id of frame..
18848     frameId: false,
18849     
18850     // private properties
18851     validationEvent : false,
18852     deferHeight: true,
18853     initialized : false,
18854     activated : false,
18855     sourceEditMode : false,
18856     onFocus : Roo.emptyFn,
18857     iframePad:3,
18858     hideMode:'offsets',
18859     
18860     clearUp: true,
18861     
18862     // blacklist + whitelisted elements..
18863     black: false,
18864     white: false,
18865      
18866     
18867
18868     /**
18869      * Protected method that will not generally be called directly. It
18870      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18871      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18872      */
18873     getDocMarkup : function(){
18874         // body styles..
18875         var st = '';
18876         
18877         // inherit styels from page...?? 
18878         if (this.stylesheets === false) {
18879             
18880             Roo.get(document.head).select('style').each(function(node) {
18881                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18882             });
18883             
18884             Roo.get(document.head).select('link').each(function(node) { 
18885                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18886             });
18887             
18888         } else if (!this.stylesheets.length) {
18889                 // simple..
18890                 st = '<style type="text/css">' +
18891                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18892                    '</style>';
18893         } else { 
18894             
18895         }
18896         
18897         st +=  '<style type="text/css">' +
18898             'IMG { cursor: pointer } ' +
18899         '</style>';
18900
18901         
18902         return '<html><head>' + st  +
18903             //<style type="text/css">' +
18904             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18905             //'</style>' +
18906             ' </head><body class="roo-htmleditor-body"></body></html>';
18907     },
18908
18909     // private
18910     onRender : function(ct, position)
18911     {
18912         var _t = this;
18913         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18914         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18915         
18916         
18917         this.el.dom.style.border = '0 none';
18918         this.el.dom.setAttribute('tabIndex', -1);
18919         this.el.addClass('x-hidden hide');
18920         
18921         
18922         
18923         if(Roo.isIE){ // fix IE 1px bogus margin
18924             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18925         }
18926        
18927         
18928         this.frameId = Roo.id();
18929         
18930          
18931         
18932         var iframe = this.owner.wrap.createChild({
18933             tag: 'iframe',
18934             cls: 'form-control', // bootstrap..
18935             id: this.frameId,
18936             name: this.frameId,
18937             frameBorder : 'no',
18938             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18939         }, this.el
18940         );
18941         
18942         
18943         this.iframe = iframe.dom;
18944
18945          this.assignDocWin();
18946         
18947         this.doc.designMode = 'on';
18948        
18949         this.doc.open();
18950         this.doc.write(this.getDocMarkup());
18951         this.doc.close();
18952
18953         
18954         var task = { // must defer to wait for browser to be ready
18955             run : function(){
18956                 //console.log("run task?" + this.doc.readyState);
18957                 this.assignDocWin();
18958                 if(this.doc.body || this.doc.readyState == 'complete'){
18959                     try {
18960                         this.doc.designMode="on";
18961                     } catch (e) {
18962                         return;
18963                     }
18964                     Roo.TaskMgr.stop(task);
18965                     this.initEditor.defer(10, this);
18966                 }
18967             },
18968             interval : 10,
18969             duration: 10000,
18970             scope: this
18971         };
18972         Roo.TaskMgr.start(task);
18973
18974     },
18975
18976     // private
18977     onResize : function(w, h)
18978     {
18979          Roo.log('resize: ' +w + ',' + h );
18980         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18981         if(!this.iframe){
18982             return;
18983         }
18984         if(typeof w == 'number'){
18985             
18986             this.iframe.style.width = w + 'px';
18987         }
18988         if(typeof h == 'number'){
18989             
18990             this.iframe.style.height = h + 'px';
18991             if(this.doc){
18992                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18993             }
18994         }
18995         
18996     },
18997
18998     /**
18999      * Toggles the editor between standard and source edit mode.
19000      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19001      */
19002     toggleSourceEdit : function(sourceEditMode){
19003         
19004         this.sourceEditMode = sourceEditMode === true;
19005         
19006         if(this.sourceEditMode){
19007  
19008             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19009             
19010         }else{
19011             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19012             //this.iframe.className = '';
19013             this.deferFocus();
19014         }
19015         //this.setSize(this.owner.wrap.getSize());
19016         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19017     },
19018
19019     
19020   
19021
19022     /**
19023      * Protected method that will not generally be called directly. If you need/want
19024      * custom HTML cleanup, this is the method you should override.
19025      * @param {String} html The HTML to be cleaned
19026      * return {String} The cleaned HTML
19027      */
19028     cleanHtml : function(html){
19029         html = String(html);
19030         if(html.length > 5){
19031             if(Roo.isSafari){ // strip safari nonsense
19032                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19033             }
19034         }
19035         if(html == '&nbsp;'){
19036             html = '';
19037         }
19038         return html;
19039     },
19040
19041     /**
19042      * HTML Editor -> Textarea
19043      * Protected method that will not generally be called directly. Syncs the contents
19044      * of the editor iframe with the textarea.
19045      */
19046     syncValue : function(){
19047         if(this.initialized){
19048             var bd = (this.doc.body || this.doc.documentElement);
19049             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19050             var html = bd.innerHTML;
19051             if(Roo.isSafari){
19052                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19053                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19054                 if(m && m[1]){
19055                     html = '<div style="'+m[0]+'">' + html + '</div>';
19056                 }
19057             }
19058             html = this.cleanHtml(html);
19059             // fix up the special chars.. normaly like back quotes in word...
19060             // however we do not want to do this with chinese..
19061             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19062                 var cc = b.charCodeAt();
19063                 if (
19064                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19065                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19066                     (cc >= 0xf900 && cc < 0xfb00 )
19067                 ) {
19068                         return b;
19069                 }
19070                 return "&#"+cc+";" 
19071             });
19072             if(this.owner.fireEvent('beforesync', this, html) !== false){
19073                 this.el.dom.value = html;
19074                 this.owner.fireEvent('sync', this, html);
19075             }
19076         }
19077     },
19078
19079     /**
19080      * Protected method that will not generally be called directly. Pushes the value of the textarea
19081      * into the iframe editor.
19082      */
19083     pushValue : function(){
19084         if(this.initialized){
19085             var v = this.el.dom.value.trim();
19086             
19087 //            if(v.length < 1){
19088 //                v = '&#160;';
19089 //            }
19090             
19091             if(this.owner.fireEvent('beforepush', this, v) !== false){
19092                 var d = (this.doc.body || this.doc.documentElement);
19093                 d.innerHTML = v;
19094                 this.cleanUpPaste();
19095                 this.el.dom.value = d.innerHTML;
19096                 this.owner.fireEvent('push', this, v);
19097             }
19098         }
19099     },
19100
19101     // private
19102     deferFocus : function(){
19103         this.focus.defer(10, this);
19104     },
19105
19106     // doc'ed in Field
19107     focus : function(){
19108         if(this.win && !this.sourceEditMode){
19109             this.win.focus();
19110         }else{
19111             this.el.focus();
19112         }
19113     },
19114     
19115     assignDocWin: function()
19116     {
19117         var iframe = this.iframe;
19118         
19119          if(Roo.isIE){
19120             this.doc = iframe.contentWindow.document;
19121             this.win = iframe.contentWindow;
19122         } else {
19123 //            if (!Roo.get(this.frameId)) {
19124 //                return;
19125 //            }
19126 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19127 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19128             
19129             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19130                 return;
19131             }
19132             
19133             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19134             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19135         }
19136     },
19137     
19138     // private
19139     initEditor : function(){
19140         //console.log("INIT EDITOR");
19141         this.assignDocWin();
19142         
19143         
19144         
19145         this.doc.designMode="on";
19146         this.doc.open();
19147         this.doc.write(this.getDocMarkup());
19148         this.doc.close();
19149         
19150         var dbody = (this.doc.body || this.doc.documentElement);
19151         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19152         // this copies styles from the containing element into thsi one..
19153         // not sure why we need all of this..
19154         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19155         
19156         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19157         //ss['background-attachment'] = 'fixed'; // w3c
19158         dbody.bgProperties = 'fixed'; // ie
19159         //Roo.DomHelper.applyStyles(dbody, ss);
19160         Roo.EventManager.on(this.doc, {
19161             //'mousedown': this.onEditorEvent,
19162             'mouseup': this.onEditorEvent,
19163             'dblclick': this.onEditorEvent,
19164             'click': this.onEditorEvent,
19165             'keyup': this.onEditorEvent,
19166             buffer:100,
19167             scope: this
19168         });
19169         if(Roo.isGecko){
19170             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19171         }
19172         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19173             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19174         }
19175         this.initialized = true;
19176
19177         this.owner.fireEvent('initialize', this);
19178         this.pushValue();
19179     },
19180
19181     // private
19182     onDestroy : function(){
19183         
19184         
19185         
19186         if(this.rendered){
19187             
19188             //for (var i =0; i < this.toolbars.length;i++) {
19189             //    // fixme - ask toolbars for heights?
19190             //    this.toolbars[i].onDestroy();
19191            // }
19192             
19193             //this.wrap.dom.innerHTML = '';
19194             //this.wrap.remove();
19195         }
19196     },
19197
19198     // private
19199     onFirstFocus : function(){
19200         
19201         this.assignDocWin();
19202         
19203         
19204         this.activated = true;
19205          
19206     
19207         if(Roo.isGecko){ // prevent silly gecko errors
19208             this.win.focus();
19209             var s = this.win.getSelection();
19210             if(!s.focusNode || s.focusNode.nodeType != 3){
19211                 var r = s.getRangeAt(0);
19212                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19213                 r.collapse(true);
19214                 this.deferFocus();
19215             }
19216             try{
19217                 this.execCmd('useCSS', true);
19218                 this.execCmd('styleWithCSS', false);
19219             }catch(e){}
19220         }
19221         this.owner.fireEvent('activate', this);
19222     },
19223
19224     // private
19225     adjustFont: function(btn){
19226         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19227         //if(Roo.isSafari){ // safari
19228         //    adjust *= 2;
19229        // }
19230         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19231         if(Roo.isSafari){ // safari
19232             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19233             v =  (v < 10) ? 10 : v;
19234             v =  (v > 48) ? 48 : v;
19235             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19236             
19237         }
19238         
19239         
19240         v = Math.max(1, v+adjust);
19241         
19242         this.execCmd('FontSize', v  );
19243     },
19244
19245     onEditorEvent : function(e)
19246     {
19247         this.owner.fireEvent('editorevent', this, e);
19248       //  this.updateToolbar();
19249         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19250     },
19251
19252     insertTag : function(tg)
19253     {
19254         // could be a bit smarter... -> wrap the current selected tRoo..
19255         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19256             
19257             range = this.createRange(this.getSelection());
19258             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19259             wrappingNode.appendChild(range.extractContents());
19260             range.insertNode(wrappingNode);
19261
19262             return;
19263             
19264             
19265             
19266         }
19267         this.execCmd("formatblock",   tg);
19268         
19269     },
19270     
19271     insertText : function(txt)
19272     {
19273         
19274         
19275         var range = this.createRange();
19276         range.deleteContents();
19277                //alert(Sender.getAttribute('label'));
19278                
19279         range.insertNode(this.doc.createTextNode(txt));
19280     } ,
19281     
19282      
19283
19284     /**
19285      * Executes a Midas editor command on the editor document and performs necessary focus and
19286      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19287      * @param {String} cmd The Midas command
19288      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19289      */
19290     relayCmd : function(cmd, value){
19291         this.win.focus();
19292         this.execCmd(cmd, value);
19293         this.owner.fireEvent('editorevent', this);
19294         //this.updateToolbar();
19295         this.owner.deferFocus();
19296     },
19297
19298     /**
19299      * Executes a Midas editor command directly on the editor document.
19300      * For visual commands, you should use {@link #relayCmd} instead.
19301      * <b>This should only be called after the editor is initialized.</b>
19302      * @param {String} cmd The Midas command
19303      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19304      */
19305     execCmd : function(cmd, value){
19306         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19307         this.syncValue();
19308     },
19309  
19310  
19311    
19312     /**
19313      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19314      * to insert tRoo.
19315      * @param {String} text | dom node.. 
19316      */
19317     insertAtCursor : function(text)
19318     {
19319         
19320         
19321         
19322         if(!this.activated){
19323             return;
19324         }
19325         /*
19326         if(Roo.isIE){
19327             this.win.focus();
19328             var r = this.doc.selection.createRange();
19329             if(r){
19330                 r.collapse(true);
19331                 r.pasteHTML(text);
19332                 this.syncValue();
19333                 this.deferFocus();
19334             
19335             }
19336             return;
19337         }
19338         */
19339         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19340             this.win.focus();
19341             
19342             
19343             // from jquery ui (MIT licenced)
19344             var range, node;
19345             var win = this.win;
19346             
19347             if (win.getSelection && win.getSelection().getRangeAt) {
19348                 range = win.getSelection().getRangeAt(0);
19349                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19350                 range.insertNode(node);
19351             } else if (win.document.selection && win.document.selection.createRange) {
19352                 // no firefox support
19353                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19354                 win.document.selection.createRange().pasteHTML(txt);
19355             } else {
19356                 // no firefox support
19357                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19358                 this.execCmd('InsertHTML', txt);
19359             } 
19360             
19361             this.syncValue();
19362             
19363             this.deferFocus();
19364         }
19365     },
19366  // private
19367     mozKeyPress : function(e){
19368         if(e.ctrlKey){
19369             var c = e.getCharCode(), cmd;
19370           
19371             if(c > 0){
19372                 c = String.fromCharCode(c).toLowerCase();
19373                 switch(c){
19374                     case 'b':
19375                         cmd = 'bold';
19376                         break;
19377                     case 'i':
19378                         cmd = 'italic';
19379                         break;
19380                     
19381                     case 'u':
19382                         cmd = 'underline';
19383                         break;
19384                     
19385                     case 'v':
19386                         this.cleanUpPaste.defer(100, this);
19387                         return;
19388                         
19389                 }
19390                 if(cmd){
19391                     this.win.focus();
19392                     this.execCmd(cmd);
19393                     this.deferFocus();
19394                     e.preventDefault();
19395                 }
19396                 
19397             }
19398         }
19399     },
19400
19401     // private
19402     fixKeys : function(){ // load time branching for fastest keydown performance
19403         if(Roo.isIE){
19404             return function(e){
19405                 var k = e.getKey(), r;
19406                 if(k == e.TAB){
19407                     e.stopEvent();
19408                     r = this.doc.selection.createRange();
19409                     if(r){
19410                         r.collapse(true);
19411                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19412                         this.deferFocus();
19413                     }
19414                     return;
19415                 }
19416                 
19417                 if(k == e.ENTER){
19418                     r = this.doc.selection.createRange();
19419                     if(r){
19420                         var target = r.parentElement();
19421                         if(!target || target.tagName.toLowerCase() != 'li'){
19422                             e.stopEvent();
19423                             r.pasteHTML('<br />');
19424                             r.collapse(false);
19425                             r.select();
19426                         }
19427                     }
19428                 }
19429                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19430                     this.cleanUpPaste.defer(100, this);
19431                     return;
19432                 }
19433                 
19434                 
19435             };
19436         }else if(Roo.isOpera){
19437             return function(e){
19438                 var k = e.getKey();
19439                 if(k == e.TAB){
19440                     e.stopEvent();
19441                     this.win.focus();
19442                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19443                     this.deferFocus();
19444                 }
19445                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19446                     this.cleanUpPaste.defer(100, this);
19447                     return;
19448                 }
19449                 
19450             };
19451         }else if(Roo.isSafari){
19452             return function(e){
19453                 var k = e.getKey();
19454                 
19455                 if(k == e.TAB){
19456                     e.stopEvent();
19457                     this.execCmd('InsertText','\t');
19458                     this.deferFocus();
19459                     return;
19460                 }
19461                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19462                     this.cleanUpPaste.defer(100, this);
19463                     return;
19464                 }
19465                 
19466              };
19467         }
19468     }(),
19469     
19470     getAllAncestors: function()
19471     {
19472         var p = this.getSelectedNode();
19473         var a = [];
19474         if (!p) {
19475             a.push(p); // push blank onto stack..
19476             p = this.getParentElement();
19477         }
19478         
19479         
19480         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19481             a.push(p);
19482             p = p.parentNode;
19483         }
19484         a.push(this.doc.body);
19485         return a;
19486     },
19487     lastSel : false,
19488     lastSelNode : false,
19489     
19490     
19491     getSelection : function() 
19492     {
19493         this.assignDocWin();
19494         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19495     },
19496     
19497     getSelectedNode: function() 
19498     {
19499         // this may only work on Gecko!!!
19500         
19501         // should we cache this!!!!
19502         
19503         
19504         
19505          
19506         var range = this.createRange(this.getSelection()).cloneRange();
19507         
19508         if (Roo.isIE) {
19509             var parent = range.parentElement();
19510             while (true) {
19511                 var testRange = range.duplicate();
19512                 testRange.moveToElementText(parent);
19513                 if (testRange.inRange(range)) {
19514                     break;
19515                 }
19516                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19517                     break;
19518                 }
19519                 parent = parent.parentElement;
19520             }
19521             return parent;
19522         }
19523         
19524         // is ancestor a text element.
19525         var ac =  range.commonAncestorContainer;
19526         if (ac.nodeType == 3) {
19527             ac = ac.parentNode;
19528         }
19529         
19530         var ar = ac.childNodes;
19531          
19532         var nodes = [];
19533         var other_nodes = [];
19534         var has_other_nodes = false;
19535         for (var i=0;i<ar.length;i++) {
19536             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19537                 continue;
19538             }
19539             // fullly contained node.
19540             
19541             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19542                 nodes.push(ar[i]);
19543                 continue;
19544             }
19545             
19546             // probably selected..
19547             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19548                 other_nodes.push(ar[i]);
19549                 continue;
19550             }
19551             // outer..
19552             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19553                 continue;
19554             }
19555             
19556             
19557             has_other_nodes = true;
19558         }
19559         if (!nodes.length && other_nodes.length) {
19560             nodes= other_nodes;
19561         }
19562         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19563             return false;
19564         }
19565         
19566         return nodes[0];
19567     },
19568     createRange: function(sel)
19569     {
19570         // this has strange effects when using with 
19571         // top toolbar - not sure if it's a great idea.
19572         //this.editor.contentWindow.focus();
19573         if (typeof sel != "undefined") {
19574             try {
19575                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19576             } catch(e) {
19577                 return this.doc.createRange();
19578             }
19579         } else {
19580             return this.doc.createRange();
19581         }
19582     },
19583     getParentElement: function()
19584     {
19585         
19586         this.assignDocWin();
19587         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19588         
19589         var range = this.createRange(sel);
19590          
19591         try {
19592             var p = range.commonAncestorContainer;
19593             while (p.nodeType == 3) { // text node
19594                 p = p.parentNode;
19595             }
19596             return p;
19597         } catch (e) {
19598             return null;
19599         }
19600     
19601     },
19602     /***
19603      *
19604      * Range intersection.. the hard stuff...
19605      *  '-1' = before
19606      *  '0' = hits..
19607      *  '1' = after.
19608      *         [ -- selected range --- ]
19609      *   [fail]                        [fail]
19610      *
19611      *    basically..
19612      *      if end is before start or  hits it. fail.
19613      *      if start is after end or hits it fail.
19614      *
19615      *   if either hits (but other is outside. - then it's not 
19616      *   
19617      *    
19618      **/
19619     
19620     
19621     // @see http://www.thismuchiknow.co.uk/?p=64.
19622     rangeIntersectsNode : function(range, node)
19623     {
19624         var nodeRange = node.ownerDocument.createRange();
19625         try {
19626             nodeRange.selectNode(node);
19627         } catch (e) {
19628             nodeRange.selectNodeContents(node);
19629         }
19630     
19631         var rangeStartRange = range.cloneRange();
19632         rangeStartRange.collapse(true);
19633     
19634         var rangeEndRange = range.cloneRange();
19635         rangeEndRange.collapse(false);
19636     
19637         var nodeStartRange = nodeRange.cloneRange();
19638         nodeStartRange.collapse(true);
19639     
19640         var nodeEndRange = nodeRange.cloneRange();
19641         nodeEndRange.collapse(false);
19642     
19643         return rangeStartRange.compareBoundaryPoints(
19644                  Range.START_TO_START, nodeEndRange) == -1 &&
19645                rangeEndRange.compareBoundaryPoints(
19646                  Range.START_TO_START, nodeStartRange) == 1;
19647         
19648          
19649     },
19650     rangeCompareNode : function(range, node)
19651     {
19652         var nodeRange = node.ownerDocument.createRange();
19653         try {
19654             nodeRange.selectNode(node);
19655         } catch (e) {
19656             nodeRange.selectNodeContents(node);
19657         }
19658         
19659         
19660         range.collapse(true);
19661     
19662         nodeRange.collapse(true);
19663      
19664         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19665         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19666          
19667         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19668         
19669         var nodeIsBefore   =  ss == 1;
19670         var nodeIsAfter    = ee == -1;
19671         
19672         if (nodeIsBefore && nodeIsAfter)
19673             return 0; // outer
19674         if (!nodeIsBefore && nodeIsAfter)
19675             return 1; //right trailed.
19676         
19677         if (nodeIsBefore && !nodeIsAfter)
19678             return 2;  // left trailed.
19679         // fully contined.
19680         return 3;
19681     },
19682
19683     // private? - in a new class?
19684     cleanUpPaste :  function()
19685     {
19686         // cleans up the whole document..
19687         Roo.log('cleanuppaste');
19688         
19689         this.cleanUpChildren(this.doc.body);
19690         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19691         if (clean != this.doc.body.innerHTML) {
19692             this.doc.body.innerHTML = clean;
19693         }
19694         
19695     },
19696     
19697     cleanWordChars : function(input) {// change the chars to hex code
19698         var he = Roo.HtmlEditorCore;
19699         
19700         var output = input;
19701         Roo.each(he.swapCodes, function(sw) { 
19702             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19703             
19704             output = output.replace(swapper, sw[1]);
19705         });
19706         
19707         return output;
19708     },
19709     
19710     
19711     cleanUpChildren : function (n)
19712     {
19713         if (!n.childNodes.length) {
19714             return;
19715         }
19716         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19717            this.cleanUpChild(n.childNodes[i]);
19718         }
19719     },
19720     
19721     
19722         
19723     
19724     cleanUpChild : function (node)
19725     {
19726         var ed = this;
19727         //console.log(node);
19728         if (node.nodeName == "#text") {
19729             // clean up silly Windows -- stuff?
19730             return; 
19731         }
19732         if (node.nodeName == "#comment") {
19733             node.parentNode.removeChild(node);
19734             // clean up silly Windows -- stuff?
19735             return; 
19736         }
19737         var lcname = node.tagName.toLowerCase();
19738         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19739         // whitelist of tags..
19740         
19741         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19742             // remove node.
19743             node.parentNode.removeChild(node);
19744             return;
19745             
19746         }
19747         
19748         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19749         
19750         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19751         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19752         
19753         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19754         //    remove_keep_children = true;
19755         //}
19756         
19757         if (remove_keep_children) {
19758             this.cleanUpChildren(node);
19759             // inserts everything just before this node...
19760             while (node.childNodes.length) {
19761                 var cn = node.childNodes[0];
19762                 node.removeChild(cn);
19763                 node.parentNode.insertBefore(cn, node);
19764             }
19765             node.parentNode.removeChild(node);
19766             return;
19767         }
19768         
19769         if (!node.attributes || !node.attributes.length) {
19770             this.cleanUpChildren(node);
19771             return;
19772         }
19773         
19774         function cleanAttr(n,v)
19775         {
19776             
19777             if (v.match(/^\./) || v.match(/^\//)) {
19778                 return;
19779             }
19780             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19781                 return;
19782             }
19783             if (v.match(/^#/)) {
19784                 return;
19785             }
19786 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19787             node.removeAttribute(n);
19788             
19789         }
19790         
19791         var cwhite = this.cwhite;
19792         var cblack = this.cblack;
19793             
19794         function cleanStyle(n,v)
19795         {
19796             if (v.match(/expression/)) { //XSS?? should we even bother..
19797                 node.removeAttribute(n);
19798                 return;
19799             }
19800             
19801             var parts = v.split(/;/);
19802             var clean = [];
19803             
19804             Roo.each(parts, function(p) {
19805                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19806                 if (!p.length) {
19807                     return true;
19808                 }
19809                 var l = p.split(':').shift().replace(/\s+/g,'');
19810                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19811                 
19812                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19813 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19814                     //node.removeAttribute(n);
19815                     return true;
19816                 }
19817                 //Roo.log()
19818                 // only allow 'c whitelisted system attributes'
19819                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19820 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19821                     //node.removeAttribute(n);
19822                     return true;
19823                 }
19824                 
19825                 
19826                  
19827                 
19828                 clean.push(p);
19829                 return true;
19830             });
19831             if (clean.length) { 
19832                 node.setAttribute(n, clean.join(';'));
19833             } else {
19834                 node.removeAttribute(n);
19835             }
19836             
19837         }
19838         
19839         
19840         for (var i = node.attributes.length-1; i > -1 ; i--) {
19841             var a = node.attributes[i];
19842             //console.log(a);
19843             
19844             if (a.name.toLowerCase().substr(0,2)=='on')  {
19845                 node.removeAttribute(a.name);
19846                 continue;
19847             }
19848             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19849                 node.removeAttribute(a.name);
19850                 continue;
19851             }
19852             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19853                 cleanAttr(a.name,a.value); // fixme..
19854                 continue;
19855             }
19856             if (a.name == 'style') {
19857                 cleanStyle(a.name,a.value);
19858                 continue;
19859             }
19860             /// clean up MS crap..
19861             // tecnically this should be a list of valid class'es..
19862             
19863             
19864             if (a.name == 'class') {
19865                 if (a.value.match(/^Mso/)) {
19866                     node.className = '';
19867                 }
19868                 
19869                 if (a.value.match(/body/)) {
19870                     node.className = '';
19871                 }
19872                 continue;
19873             }
19874             
19875             // style cleanup!?
19876             // class cleanup?
19877             
19878         }
19879         
19880         
19881         this.cleanUpChildren(node);
19882         
19883         
19884     },
19885     
19886     /**
19887      * Clean up MS wordisms...
19888      */
19889     cleanWord : function(node)
19890     {
19891         
19892         
19893         if (!node) {
19894             this.cleanWord(this.doc.body);
19895             return;
19896         }
19897         if (node.nodeName == "#text") {
19898             // clean up silly Windows -- stuff?
19899             return; 
19900         }
19901         if (node.nodeName == "#comment") {
19902             node.parentNode.removeChild(node);
19903             // clean up silly Windows -- stuff?
19904             return; 
19905         }
19906         
19907         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19908             node.parentNode.removeChild(node);
19909             return;
19910         }
19911         
19912         // remove - but keep children..
19913         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19914             while (node.childNodes.length) {
19915                 var cn = node.childNodes[0];
19916                 node.removeChild(cn);
19917                 node.parentNode.insertBefore(cn, node);
19918             }
19919             node.parentNode.removeChild(node);
19920             this.iterateChildren(node, this.cleanWord);
19921             return;
19922         }
19923         // clean styles
19924         if (node.className.length) {
19925             
19926             var cn = node.className.split(/\W+/);
19927             var cna = [];
19928             Roo.each(cn, function(cls) {
19929                 if (cls.match(/Mso[a-zA-Z]+/)) {
19930                     return;
19931                 }
19932                 cna.push(cls);
19933             });
19934             node.className = cna.length ? cna.join(' ') : '';
19935             if (!cna.length) {
19936                 node.removeAttribute("class");
19937             }
19938         }
19939         
19940         if (node.hasAttribute("lang")) {
19941             node.removeAttribute("lang");
19942         }
19943         
19944         if (node.hasAttribute("style")) {
19945             
19946             var styles = node.getAttribute("style").split(";");
19947             var nstyle = [];
19948             Roo.each(styles, function(s) {
19949                 if (!s.match(/:/)) {
19950                     return;
19951                 }
19952                 var kv = s.split(":");
19953                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19954                     return;
19955                 }
19956                 // what ever is left... we allow.
19957                 nstyle.push(s);
19958             });
19959             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19960             if (!nstyle.length) {
19961                 node.removeAttribute('style');
19962             }
19963         }
19964         this.iterateChildren(node, this.cleanWord);
19965         
19966         
19967         
19968     },
19969     /**
19970      * iterateChildren of a Node, calling fn each time, using this as the scole..
19971      * @param {DomNode} node node to iterate children of.
19972      * @param {Function} fn method of this class to call on each item.
19973      */
19974     iterateChildren : function(node, fn)
19975     {
19976         if (!node.childNodes.length) {
19977                 return;
19978         }
19979         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19980            fn.call(this, node.childNodes[i])
19981         }
19982     },
19983     
19984     
19985     /**
19986      * cleanTableWidths.
19987      *
19988      * Quite often pasting from word etc.. results in tables with column and widths.
19989      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19990      *
19991      */
19992     cleanTableWidths : function(node)
19993     {
19994          
19995          
19996         if (!node) {
19997             this.cleanTableWidths(this.doc.body);
19998             return;
19999         }
20000         
20001         // ignore list...
20002         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20003             return; 
20004         }
20005         Roo.log(node.tagName);
20006         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20007             this.iterateChildren(node, this.cleanTableWidths);
20008             return;
20009         }
20010         if (node.hasAttribute('width')) {
20011             node.removeAttribute('width');
20012         }
20013         
20014          
20015         if (node.hasAttribute("style")) {
20016             // pretty basic...
20017             
20018             var styles = node.getAttribute("style").split(";");
20019             var nstyle = [];
20020             Roo.each(styles, function(s) {
20021                 if (!s.match(/:/)) {
20022                     return;
20023                 }
20024                 var kv = s.split(":");
20025                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20026                     return;
20027                 }
20028                 // what ever is left... we allow.
20029                 nstyle.push(s);
20030             });
20031             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20032             if (!nstyle.length) {
20033                 node.removeAttribute('style');
20034             }
20035         }
20036         
20037         this.iterateChildren(node, this.cleanTableWidths);
20038         
20039         
20040     },
20041     
20042     
20043     
20044     
20045     domToHTML : function(currentElement, depth, nopadtext) {
20046         
20047         depth = depth || 0;
20048         nopadtext = nopadtext || false;
20049     
20050         if (!currentElement) {
20051             return this.domToHTML(this.doc.body);
20052         }
20053         
20054         //Roo.log(currentElement);
20055         var j;
20056         var allText = false;
20057         var nodeName = currentElement.nodeName;
20058         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20059         
20060         if  (nodeName == '#text') {
20061             
20062             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20063         }
20064         
20065         
20066         var ret = '';
20067         if (nodeName != 'BODY') {
20068              
20069             var i = 0;
20070             // Prints the node tagName, such as <A>, <IMG>, etc
20071             if (tagName) {
20072                 var attr = [];
20073                 for(i = 0; i < currentElement.attributes.length;i++) {
20074                     // quoting?
20075                     var aname = currentElement.attributes.item(i).name;
20076                     if (!currentElement.attributes.item(i).value.length) {
20077                         continue;
20078                     }
20079                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20080                 }
20081                 
20082                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20083             } 
20084             else {
20085                 
20086                 // eack
20087             }
20088         } else {
20089             tagName = false;
20090         }
20091         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20092             return ret;
20093         }
20094         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20095             nopadtext = true;
20096         }
20097         
20098         
20099         // Traverse the tree
20100         i = 0;
20101         var currentElementChild = currentElement.childNodes.item(i);
20102         var allText = true;
20103         var innerHTML  = '';
20104         lastnode = '';
20105         while (currentElementChild) {
20106             // Formatting code (indent the tree so it looks nice on the screen)
20107             var nopad = nopadtext;
20108             if (lastnode == 'SPAN') {
20109                 nopad  = true;
20110             }
20111             // text
20112             if  (currentElementChild.nodeName == '#text') {
20113                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20114                 toadd = nopadtext ? toadd : toadd.trim();
20115                 if (!nopad && toadd.length > 80) {
20116                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20117                 }
20118                 innerHTML  += toadd;
20119                 
20120                 i++;
20121                 currentElementChild = currentElement.childNodes.item(i);
20122                 lastNode = '';
20123                 continue;
20124             }
20125             allText = false;
20126             
20127             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20128                 
20129             // Recursively traverse the tree structure of the child node
20130             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20131             lastnode = currentElementChild.nodeName;
20132             i++;
20133             currentElementChild=currentElement.childNodes.item(i);
20134         }
20135         
20136         ret += innerHTML;
20137         
20138         if (!allText) {
20139                 // The remaining code is mostly for formatting the tree
20140             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20141         }
20142         
20143         
20144         if (tagName) {
20145             ret+= "</"+tagName+">";
20146         }
20147         return ret;
20148         
20149     },
20150         
20151     applyBlacklists : function()
20152     {
20153         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20154         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20155         
20156         this.white = [];
20157         this.black = [];
20158         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20159             if (b.indexOf(tag) > -1) {
20160                 return;
20161             }
20162             this.white.push(tag);
20163             
20164         }, this);
20165         
20166         Roo.each(w, function(tag) {
20167             if (b.indexOf(tag) > -1) {
20168                 return;
20169             }
20170             if (this.white.indexOf(tag) > -1) {
20171                 return;
20172             }
20173             this.white.push(tag);
20174             
20175         }, this);
20176         
20177         
20178         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20179             if (w.indexOf(tag) > -1) {
20180                 return;
20181             }
20182             this.black.push(tag);
20183             
20184         }, this);
20185         
20186         Roo.each(b, function(tag) {
20187             if (w.indexOf(tag) > -1) {
20188                 return;
20189             }
20190             if (this.black.indexOf(tag) > -1) {
20191                 return;
20192             }
20193             this.black.push(tag);
20194             
20195         }, this);
20196         
20197         
20198         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20199         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20200         
20201         this.cwhite = [];
20202         this.cblack = [];
20203         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20204             if (b.indexOf(tag) > -1) {
20205                 return;
20206             }
20207             this.cwhite.push(tag);
20208             
20209         }, this);
20210         
20211         Roo.each(w, function(tag) {
20212             if (b.indexOf(tag) > -1) {
20213                 return;
20214             }
20215             if (this.cwhite.indexOf(tag) > -1) {
20216                 return;
20217             }
20218             this.cwhite.push(tag);
20219             
20220         }, this);
20221         
20222         
20223         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20224             if (w.indexOf(tag) > -1) {
20225                 return;
20226             }
20227             this.cblack.push(tag);
20228             
20229         }, this);
20230         
20231         Roo.each(b, function(tag) {
20232             if (w.indexOf(tag) > -1) {
20233                 return;
20234             }
20235             if (this.cblack.indexOf(tag) > -1) {
20236                 return;
20237             }
20238             this.cblack.push(tag);
20239             
20240         }, this);
20241     },
20242     
20243     setStylesheets : function(stylesheets)
20244     {
20245         if(typeof(stylesheets) == 'string'){
20246             Roo.get(this.iframe.contentDocument.head).createChild({
20247                 tag : 'link',
20248                 rel : 'stylesheet',
20249                 type : 'text/css',
20250                 href : stylesheets
20251             });
20252             
20253             return;
20254         }
20255         var _this = this;
20256      
20257         Roo.each(stylesheets, function(s) {
20258             if(!s.length){
20259                 return;
20260             }
20261             
20262             Roo.get(_this.iframe.contentDocument.head).createChild({
20263                 tag : 'link',
20264                 rel : 'stylesheet',
20265                 type : 'text/css',
20266                 href : s
20267             });
20268         });
20269
20270         
20271     },
20272     
20273     removeStylesheets : function()
20274     {
20275         var _this = this;
20276         
20277         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20278             s.remove();
20279         });
20280     }
20281     
20282     // hide stuff that is not compatible
20283     /**
20284      * @event blur
20285      * @hide
20286      */
20287     /**
20288      * @event change
20289      * @hide
20290      */
20291     /**
20292      * @event focus
20293      * @hide
20294      */
20295     /**
20296      * @event specialkey
20297      * @hide
20298      */
20299     /**
20300      * @cfg {String} fieldClass @hide
20301      */
20302     /**
20303      * @cfg {String} focusClass @hide
20304      */
20305     /**
20306      * @cfg {String} autoCreate @hide
20307      */
20308     /**
20309      * @cfg {String} inputType @hide
20310      */
20311     /**
20312      * @cfg {String} invalidClass @hide
20313      */
20314     /**
20315      * @cfg {String} invalidText @hide
20316      */
20317     /**
20318      * @cfg {String} msgFx @hide
20319      */
20320     /**
20321      * @cfg {String} validateOnBlur @hide
20322      */
20323 });
20324
20325 Roo.HtmlEditorCore.white = [
20326         'area', 'br', 'img', 'input', 'hr', 'wbr',
20327         
20328        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20329        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20330        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20331        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20332        'table',   'ul',         'xmp', 
20333        
20334        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20335       'thead',   'tr', 
20336      
20337       'dir', 'menu', 'ol', 'ul', 'dl',
20338        
20339       'embed',  'object'
20340 ];
20341
20342
20343 Roo.HtmlEditorCore.black = [
20344     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20345         'applet', // 
20346         'base',   'basefont', 'bgsound', 'blink',  'body', 
20347         'frame',  'frameset', 'head',    'html',   'ilayer', 
20348         'iframe', 'layer',  'link',     'meta',    'object',   
20349         'script', 'style' ,'title',  'xml' // clean later..
20350 ];
20351 Roo.HtmlEditorCore.clean = [
20352     'script', 'style', 'title', 'xml'
20353 ];
20354 Roo.HtmlEditorCore.remove = [
20355     'font'
20356 ];
20357 // attributes..
20358
20359 Roo.HtmlEditorCore.ablack = [
20360     'on'
20361 ];
20362     
20363 Roo.HtmlEditorCore.aclean = [ 
20364     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20365 ];
20366
20367 // protocols..
20368 Roo.HtmlEditorCore.pwhite= [
20369         'http',  'https',  'mailto'
20370 ];
20371
20372 // white listed style attributes.
20373 Roo.HtmlEditorCore.cwhite= [
20374       //  'text-align', /// default is to allow most things..
20375       
20376          
20377 //        'font-size'//??
20378 ];
20379
20380 // black listed style attributes.
20381 Roo.HtmlEditorCore.cblack= [
20382       //  'font-size' -- this can be set by the project 
20383 ];
20384
20385
20386 Roo.HtmlEditorCore.swapCodes   =[ 
20387     [    8211, "--" ], 
20388     [    8212, "--" ], 
20389     [    8216,  "'" ],  
20390     [    8217, "'" ],  
20391     [    8220, '"' ],  
20392     [    8221, '"' ],  
20393     [    8226, "*" ],  
20394     [    8230, "..." ]
20395 ]; 
20396
20397     /*
20398  * - LGPL
20399  *
20400  * HtmlEditor
20401  * 
20402  */
20403
20404 /**
20405  * @class Roo.bootstrap.HtmlEditor
20406  * @extends Roo.bootstrap.TextArea
20407  * Bootstrap HtmlEditor class
20408
20409  * @constructor
20410  * Create a new HtmlEditor
20411  * @param {Object} config The config object
20412  */
20413
20414 Roo.bootstrap.HtmlEditor = function(config){
20415     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20416     if (!this.toolbars) {
20417         this.toolbars = [];
20418     }
20419     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20420     this.addEvents({
20421             /**
20422              * @event initialize
20423              * Fires when the editor is fully initialized (including the iframe)
20424              * @param {HtmlEditor} this
20425              */
20426             initialize: true,
20427             /**
20428              * @event activate
20429              * Fires when the editor is first receives the focus. Any insertion must wait
20430              * until after this event.
20431              * @param {HtmlEditor} this
20432              */
20433             activate: true,
20434              /**
20435              * @event beforesync
20436              * Fires before the textarea is updated with content from the editor iframe. Return false
20437              * to cancel the sync.
20438              * @param {HtmlEditor} this
20439              * @param {String} html
20440              */
20441             beforesync: true,
20442              /**
20443              * @event beforepush
20444              * Fires before the iframe editor is updated with content from the textarea. Return false
20445              * to cancel the push.
20446              * @param {HtmlEditor} this
20447              * @param {String} html
20448              */
20449             beforepush: true,
20450              /**
20451              * @event sync
20452              * Fires when the textarea is updated with content from the editor iframe.
20453              * @param {HtmlEditor} this
20454              * @param {String} html
20455              */
20456             sync: true,
20457              /**
20458              * @event push
20459              * Fires when the iframe editor is updated with content from the textarea.
20460              * @param {HtmlEditor} this
20461              * @param {String} html
20462              */
20463             push: true,
20464              /**
20465              * @event editmodechange
20466              * Fires when the editor switches edit modes
20467              * @param {HtmlEditor} this
20468              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20469              */
20470             editmodechange: true,
20471             /**
20472              * @event editorevent
20473              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20474              * @param {HtmlEditor} this
20475              */
20476             editorevent: true,
20477             /**
20478              * @event firstfocus
20479              * Fires when on first focus - needed by toolbars..
20480              * @param {HtmlEditor} this
20481              */
20482             firstfocus: true,
20483             /**
20484              * @event autosave
20485              * Auto save the htmlEditor value as a file into Events
20486              * @param {HtmlEditor} this
20487              */
20488             autosave: true,
20489             /**
20490              * @event savedpreview
20491              * preview the saved version of htmlEditor
20492              * @param {HtmlEditor} this
20493              */
20494             savedpreview: true
20495         });
20496 };
20497
20498
20499 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20500     
20501     
20502       /**
20503      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20504      */
20505     toolbars : false,
20506    
20507      /**
20508      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20509      *                        Roo.resizable.
20510      */
20511     resizable : false,
20512      /**
20513      * @cfg {Number} height (in pixels)
20514      */   
20515     height: 300,
20516    /**
20517      * @cfg {Number} width (in pixels)
20518      */   
20519     width: false,
20520     
20521     /**
20522      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20523      * 
20524      */
20525     stylesheets: false,
20526     
20527     // id of frame..
20528     frameId: false,
20529     
20530     // private properties
20531     validationEvent : false,
20532     deferHeight: true,
20533     initialized : false,
20534     activated : false,
20535     
20536     onFocus : Roo.emptyFn,
20537     iframePad:3,
20538     hideMode:'offsets',
20539     
20540     
20541     tbContainer : false,
20542     
20543     toolbarContainer :function() {
20544         return this.wrap.select('.x-html-editor-tb',true).first();
20545     },
20546
20547     /**
20548      * Protected method that will not generally be called directly. It
20549      * is called when the editor creates its toolbar. Override this method if you need to
20550      * add custom toolbar buttons.
20551      * @param {HtmlEditor} editor
20552      */
20553     createToolbar : function(){
20554         
20555         Roo.log("create toolbars");
20556         
20557         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20558         this.toolbars[0].render(this.toolbarContainer());
20559         
20560         return;
20561         
20562 //        if (!editor.toolbars || !editor.toolbars.length) {
20563 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20564 //        }
20565 //        
20566 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20567 //            editor.toolbars[i] = Roo.factory(
20568 //                    typeof(editor.toolbars[i]) == 'string' ?
20569 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20570 //                Roo.bootstrap.HtmlEditor);
20571 //            editor.toolbars[i].init(editor);
20572 //        }
20573     },
20574
20575      
20576     // private
20577     onRender : function(ct, position)
20578     {
20579        // Roo.log("Call onRender: " + this.xtype);
20580         var _t = this;
20581         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20582       
20583         this.wrap = this.inputEl().wrap({
20584             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20585         });
20586         
20587         this.editorcore.onRender(ct, position);
20588          
20589         if (this.resizable) {
20590             this.resizeEl = new Roo.Resizable(this.wrap, {
20591                 pinned : true,
20592                 wrap: true,
20593                 dynamic : true,
20594                 minHeight : this.height,
20595                 height: this.height,
20596                 handles : this.resizable,
20597                 width: this.width,
20598                 listeners : {
20599                     resize : function(r, w, h) {
20600                         _t.onResize(w,h); // -something
20601                     }
20602                 }
20603             });
20604             
20605         }
20606         this.createToolbar(this);
20607        
20608         
20609         if(!this.width && this.resizable){
20610             this.setSize(this.wrap.getSize());
20611         }
20612         if (this.resizeEl) {
20613             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20614             // should trigger onReize..
20615         }
20616         
20617     },
20618
20619     // private
20620     onResize : function(w, h)
20621     {
20622         Roo.log('resize: ' +w + ',' + h );
20623         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20624         var ew = false;
20625         var eh = false;
20626         
20627         if(this.inputEl() ){
20628             if(typeof w == 'number'){
20629                 var aw = w - this.wrap.getFrameWidth('lr');
20630                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20631                 ew = aw;
20632             }
20633             if(typeof h == 'number'){
20634                  var tbh = -11;  // fixme it needs to tool bar size!
20635                 for (var i =0; i < this.toolbars.length;i++) {
20636                     // fixme - ask toolbars for heights?
20637                     tbh += this.toolbars[i].el.getHeight();
20638                     //if (this.toolbars[i].footer) {
20639                     //    tbh += this.toolbars[i].footer.el.getHeight();
20640                     //}
20641                 }
20642               
20643                 
20644                 
20645                 
20646                 
20647                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20648                 ah -= 5; // knock a few pixes off for look..
20649                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20650                 var eh = ah;
20651             }
20652         }
20653         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20654         this.editorcore.onResize(ew,eh);
20655         
20656     },
20657
20658     /**
20659      * Toggles the editor between standard and source edit mode.
20660      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20661      */
20662     toggleSourceEdit : function(sourceEditMode)
20663     {
20664         this.editorcore.toggleSourceEdit(sourceEditMode);
20665         
20666         if(this.editorcore.sourceEditMode){
20667             Roo.log('editor - showing textarea');
20668             
20669 //            Roo.log('in');
20670 //            Roo.log(this.syncValue());
20671             this.syncValue();
20672             this.inputEl().removeClass(['hide', 'x-hidden']);
20673             this.inputEl().dom.removeAttribute('tabIndex');
20674             this.inputEl().focus();
20675         }else{
20676             Roo.log('editor - hiding textarea');
20677 //            Roo.log('out')
20678 //            Roo.log(this.pushValue()); 
20679             this.pushValue();
20680             
20681             this.inputEl().addClass(['hide', 'x-hidden']);
20682             this.inputEl().dom.setAttribute('tabIndex', -1);
20683             //this.deferFocus();
20684         }
20685          
20686         if(this.resizable){
20687             this.setSize(this.wrap.getSize());
20688         }
20689         
20690         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20691     },
20692  
20693     // private (for BoxComponent)
20694     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20695
20696     // private (for BoxComponent)
20697     getResizeEl : function(){
20698         return this.wrap;
20699     },
20700
20701     // private (for BoxComponent)
20702     getPositionEl : function(){
20703         return this.wrap;
20704     },
20705
20706     // private
20707     initEvents : function(){
20708         this.originalValue = this.getValue();
20709     },
20710
20711 //    /**
20712 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20713 //     * @method
20714 //     */
20715 //    markInvalid : Roo.emptyFn,
20716 //    /**
20717 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20718 //     * @method
20719 //     */
20720 //    clearInvalid : Roo.emptyFn,
20721
20722     setValue : function(v){
20723         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20724         this.editorcore.pushValue();
20725     },
20726
20727      
20728     // private
20729     deferFocus : function(){
20730         this.focus.defer(10, this);
20731     },
20732
20733     // doc'ed in Field
20734     focus : function(){
20735         this.editorcore.focus();
20736         
20737     },
20738       
20739
20740     // private
20741     onDestroy : function(){
20742         
20743         
20744         
20745         if(this.rendered){
20746             
20747             for (var i =0; i < this.toolbars.length;i++) {
20748                 // fixme - ask toolbars for heights?
20749                 this.toolbars[i].onDestroy();
20750             }
20751             
20752             this.wrap.dom.innerHTML = '';
20753             this.wrap.remove();
20754         }
20755     },
20756
20757     // private
20758     onFirstFocus : function(){
20759         //Roo.log("onFirstFocus");
20760         this.editorcore.onFirstFocus();
20761          for (var i =0; i < this.toolbars.length;i++) {
20762             this.toolbars[i].onFirstFocus();
20763         }
20764         
20765     },
20766     
20767     // private
20768     syncValue : function()
20769     {   
20770         this.editorcore.syncValue();
20771     },
20772     
20773     pushValue : function()
20774     {   
20775         this.editorcore.pushValue();
20776     }
20777      
20778     
20779     // hide stuff that is not compatible
20780     /**
20781      * @event blur
20782      * @hide
20783      */
20784     /**
20785      * @event change
20786      * @hide
20787      */
20788     /**
20789      * @event focus
20790      * @hide
20791      */
20792     /**
20793      * @event specialkey
20794      * @hide
20795      */
20796     /**
20797      * @cfg {String} fieldClass @hide
20798      */
20799     /**
20800      * @cfg {String} focusClass @hide
20801      */
20802     /**
20803      * @cfg {String} autoCreate @hide
20804      */
20805     /**
20806      * @cfg {String} inputType @hide
20807      */
20808     /**
20809      * @cfg {String} invalidClass @hide
20810      */
20811     /**
20812      * @cfg {String} invalidText @hide
20813      */
20814     /**
20815      * @cfg {String} msgFx @hide
20816      */
20817     /**
20818      * @cfg {String} validateOnBlur @hide
20819      */
20820 });
20821  
20822     
20823    
20824    
20825    
20826       
20827 Roo.namespace('Roo.bootstrap.htmleditor');
20828 /**
20829  * @class Roo.bootstrap.HtmlEditorToolbar1
20830  * Basic Toolbar
20831  * 
20832  * Usage:
20833  *
20834  new Roo.bootstrap.HtmlEditor({
20835     ....
20836     toolbars : [
20837         new Roo.bootstrap.HtmlEditorToolbar1({
20838             disable : { fonts: 1 , format: 1, ..., ... , ...],
20839             btns : [ .... ]
20840         })
20841     }
20842      
20843  * 
20844  * @cfg {Object} disable List of elements to disable..
20845  * @cfg {Array} btns List of additional buttons.
20846  * 
20847  * 
20848  * NEEDS Extra CSS? 
20849  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20850  */
20851  
20852 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20853 {
20854     
20855     Roo.apply(this, config);
20856     
20857     // default disabled, based on 'good practice'..
20858     this.disable = this.disable || {};
20859     Roo.applyIf(this.disable, {
20860         fontSize : true,
20861         colors : true,
20862         specialElements : true
20863     });
20864     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20865     
20866     this.editor = config.editor;
20867     this.editorcore = config.editor.editorcore;
20868     
20869     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20870     
20871     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20872     // dont call parent... till later.
20873 }
20874 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20875      
20876     bar : true,
20877     
20878     editor : false,
20879     editorcore : false,
20880     
20881     
20882     formats : [
20883         "p" ,  
20884         "h1","h2","h3","h4","h5","h6", 
20885         "pre", "code", 
20886         "abbr", "acronym", "address", "cite", "samp", "var",
20887         'div','span'
20888     ],
20889     
20890     onRender : function(ct, position)
20891     {
20892        // Roo.log("Call onRender: " + this.xtype);
20893         
20894        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20895        Roo.log(this.el);
20896        this.el.dom.style.marginBottom = '0';
20897        var _this = this;
20898        var editorcore = this.editorcore;
20899        var editor= this.editor;
20900        
20901        var children = [];
20902        var btn = function(id,cmd , toggle, handler){
20903        
20904             var  event = toggle ? 'toggle' : 'click';
20905        
20906             var a = {
20907                 size : 'sm',
20908                 xtype: 'Button',
20909                 xns: Roo.bootstrap,
20910                 glyphicon : id,
20911                 cmd : id || cmd,
20912                 enableToggle:toggle !== false,
20913                 //html : 'submit'
20914                 pressed : toggle ? false : null,
20915                 listeners : {}
20916             };
20917             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20918                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20919             };
20920             children.push(a);
20921             return a;
20922        }
20923         
20924         var style = {
20925                 xtype: 'Button',
20926                 size : 'sm',
20927                 xns: Roo.bootstrap,
20928                 glyphicon : 'font',
20929                 //html : 'submit'
20930                 menu : {
20931                     xtype: 'Menu',
20932                     xns: Roo.bootstrap,
20933                     items:  []
20934                 }
20935         };
20936         Roo.each(this.formats, function(f) {
20937             style.menu.items.push({
20938                 xtype :'MenuItem',
20939                 xns: Roo.bootstrap,
20940                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20941                 tagname : f,
20942                 listeners : {
20943                     click : function()
20944                     {
20945                         editorcore.insertTag(this.tagname);
20946                         editor.focus();
20947                     }
20948                 }
20949                 
20950             });
20951         });
20952          children.push(style);   
20953             
20954             
20955         btn('bold',false,true);
20956         btn('italic',false,true);
20957         btn('align-left', 'justifyleft',true);
20958         btn('align-center', 'justifycenter',true);
20959         btn('align-right' , 'justifyright',true);
20960         btn('link', false, false, function(btn) {
20961             //Roo.log("create link?");
20962             var url = prompt(this.createLinkText, this.defaultLinkValue);
20963             if(url && url != 'http:/'+'/'){
20964                 this.editorcore.relayCmd('createlink', url);
20965             }
20966         }),
20967         btn('list','insertunorderedlist',true);
20968         btn('pencil', false,true, function(btn){
20969                 Roo.log(this);
20970                 
20971                 this.toggleSourceEdit(btn.pressed);
20972         });
20973         /*
20974         var cog = {
20975                 xtype: 'Button',
20976                 size : 'sm',
20977                 xns: Roo.bootstrap,
20978                 glyphicon : 'cog',
20979                 //html : 'submit'
20980                 menu : {
20981                     xtype: 'Menu',
20982                     xns: Roo.bootstrap,
20983                     items:  []
20984                 }
20985         };
20986         
20987         cog.menu.items.push({
20988             xtype :'MenuItem',
20989             xns: Roo.bootstrap,
20990             html : Clean styles,
20991             tagname : f,
20992             listeners : {
20993                 click : function()
20994                 {
20995                     editorcore.insertTag(this.tagname);
20996                     editor.focus();
20997                 }
20998             }
20999             
21000         });
21001        */
21002         
21003          
21004        this.xtype = 'NavSimplebar';
21005         
21006         for(var i=0;i< children.length;i++) {
21007             
21008             this.buttons.add(this.addxtypeChild(children[i]));
21009             
21010         }
21011         
21012         editor.on('editorevent', this.updateToolbar, this);
21013     },
21014     onBtnClick : function(id)
21015     {
21016        this.editorcore.relayCmd(id);
21017        this.editorcore.focus();
21018     },
21019     
21020     /**
21021      * Protected method that will not generally be called directly. It triggers
21022      * a toolbar update by reading the markup state of the current selection in the editor.
21023      */
21024     updateToolbar: function(){
21025
21026         if(!this.editorcore.activated){
21027             this.editor.onFirstFocus(); // is this neeed?
21028             return;
21029         }
21030
21031         var btns = this.buttons; 
21032         var doc = this.editorcore.doc;
21033         btns.get('bold').setActive(doc.queryCommandState('bold'));
21034         btns.get('italic').setActive(doc.queryCommandState('italic'));
21035         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21036         
21037         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21038         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21039         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21040         
21041         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21042         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21043          /*
21044         
21045         var ans = this.editorcore.getAllAncestors();
21046         if (this.formatCombo) {
21047             
21048             
21049             var store = this.formatCombo.store;
21050             this.formatCombo.setValue("");
21051             for (var i =0; i < ans.length;i++) {
21052                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21053                     // select it..
21054                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21055                     break;
21056                 }
21057             }
21058         }
21059         
21060         
21061         
21062         // hides menus... - so this cant be on a menu...
21063         Roo.bootstrap.MenuMgr.hideAll();
21064         */
21065         Roo.bootstrap.MenuMgr.hideAll();
21066         //this.editorsyncValue();
21067     },
21068     onFirstFocus: function() {
21069         this.buttons.each(function(item){
21070            item.enable();
21071         });
21072     },
21073     toggleSourceEdit : function(sourceEditMode){
21074         
21075           
21076         if(sourceEditMode){
21077             Roo.log("disabling buttons");
21078            this.buttons.each( function(item){
21079                 if(item.cmd != 'pencil'){
21080                     item.disable();
21081                 }
21082             });
21083           
21084         }else{
21085             Roo.log("enabling buttons");
21086             if(this.editorcore.initialized){
21087                 this.buttons.each( function(item){
21088                     item.enable();
21089                 });
21090             }
21091             
21092         }
21093         Roo.log("calling toggole on editor");
21094         // tell the editor that it's been pressed..
21095         this.editor.toggleSourceEdit(sourceEditMode);
21096        
21097     }
21098 });
21099
21100
21101
21102
21103
21104 /**
21105  * @class Roo.bootstrap.Table.AbstractSelectionModel
21106  * @extends Roo.util.Observable
21107  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21108  * implemented by descendant classes.  This class should not be directly instantiated.
21109  * @constructor
21110  */
21111 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21112     this.locked = false;
21113     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21114 };
21115
21116
21117 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21118     /** @ignore Called by the grid automatically. Do not call directly. */
21119     init : function(grid){
21120         this.grid = grid;
21121         this.initEvents();
21122     },
21123
21124     /**
21125      * Locks the selections.
21126      */
21127     lock : function(){
21128         this.locked = true;
21129     },
21130
21131     /**
21132      * Unlocks the selections.
21133      */
21134     unlock : function(){
21135         this.locked = false;
21136     },
21137
21138     /**
21139      * Returns true if the selections are locked.
21140      * @return {Boolean}
21141      */
21142     isLocked : function(){
21143         return this.locked;
21144     }
21145 });
21146 /**
21147  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21148  * @class Roo.bootstrap.Table.RowSelectionModel
21149  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21150  * It supports multiple selections and keyboard selection/navigation. 
21151  * @constructor
21152  * @param {Object} config
21153  */
21154
21155 Roo.bootstrap.Table.RowSelectionModel = function(config){
21156     Roo.apply(this, config);
21157     this.selections = new Roo.util.MixedCollection(false, function(o){
21158         return o.id;
21159     });
21160
21161     this.last = false;
21162     this.lastActive = false;
21163
21164     this.addEvents({
21165         /**
21166              * @event selectionchange
21167              * Fires when the selection changes
21168              * @param {SelectionModel} this
21169              */
21170             "selectionchange" : true,
21171         /**
21172              * @event afterselectionchange
21173              * Fires after the selection changes (eg. by key press or clicking)
21174              * @param {SelectionModel} this
21175              */
21176             "afterselectionchange" : true,
21177         /**
21178              * @event beforerowselect
21179              * Fires when a row is selected being selected, return false to cancel.
21180              * @param {SelectionModel} this
21181              * @param {Number} rowIndex The selected index
21182              * @param {Boolean} keepExisting False if other selections will be cleared
21183              */
21184             "beforerowselect" : true,
21185         /**
21186              * @event rowselect
21187              * Fires when a row is selected.
21188              * @param {SelectionModel} this
21189              * @param {Number} rowIndex The selected index
21190              * @param {Roo.data.Record} r The record
21191              */
21192             "rowselect" : true,
21193         /**
21194              * @event rowdeselect
21195              * Fires when a row is deselected.
21196              * @param {SelectionModel} this
21197              * @param {Number} rowIndex The selected index
21198              */
21199         "rowdeselect" : true
21200     });
21201     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21202     this.locked = false;
21203 };
21204
21205 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21206     /**
21207      * @cfg {Boolean} singleSelect
21208      * True to allow selection of only one row at a time (defaults to false)
21209      */
21210     singleSelect : false,
21211
21212     // private
21213     initEvents : function(){
21214
21215         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21216             this.grid.on("mousedown", this.handleMouseDown, this);
21217         }else{ // allow click to work like normal
21218             this.grid.on("rowclick", this.handleDragableRowClick, this);
21219         }
21220
21221         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21222             "up" : function(e){
21223                 if(!e.shiftKey){
21224                     this.selectPrevious(e.shiftKey);
21225                 }else if(this.last !== false && this.lastActive !== false){
21226                     var last = this.last;
21227                     this.selectRange(this.last,  this.lastActive-1);
21228                     this.grid.getView().focusRow(this.lastActive);
21229                     if(last !== false){
21230                         this.last = last;
21231                     }
21232                 }else{
21233                     this.selectFirstRow();
21234                 }
21235                 this.fireEvent("afterselectionchange", this);
21236             },
21237             "down" : function(e){
21238                 if(!e.shiftKey){
21239                     this.selectNext(e.shiftKey);
21240                 }else if(this.last !== false && this.lastActive !== false){
21241                     var last = this.last;
21242                     this.selectRange(this.last,  this.lastActive+1);
21243                     this.grid.getView().focusRow(this.lastActive);
21244                     if(last !== false){
21245                         this.last = last;
21246                     }
21247                 }else{
21248                     this.selectFirstRow();
21249                 }
21250                 this.fireEvent("afterselectionchange", this);
21251             },
21252             scope: this
21253         });
21254
21255         var view = this.grid.view;
21256         view.on("refresh", this.onRefresh, this);
21257         view.on("rowupdated", this.onRowUpdated, this);
21258         view.on("rowremoved", this.onRemove, this);
21259     },
21260
21261     // private
21262     onRefresh : function(){
21263         var ds = this.grid.dataSource, i, v = this.grid.view;
21264         var s = this.selections;
21265         s.each(function(r){
21266             if((i = ds.indexOfId(r.id)) != -1){
21267                 v.onRowSelect(i);
21268             }else{
21269                 s.remove(r);
21270             }
21271         });
21272     },
21273
21274     // private
21275     onRemove : function(v, index, r){
21276         this.selections.remove(r);
21277     },
21278
21279     // private
21280     onRowUpdated : function(v, index, r){
21281         if(this.isSelected(r)){
21282             v.onRowSelect(index);
21283         }
21284     },
21285
21286     /**
21287      * Select records.
21288      * @param {Array} records The records to select
21289      * @param {Boolean} keepExisting (optional) True to keep existing selections
21290      */
21291     selectRecords : function(records, keepExisting){
21292         if(!keepExisting){
21293             this.clearSelections();
21294         }
21295         var ds = this.grid.dataSource;
21296         for(var i = 0, len = records.length; i < len; i++){
21297             this.selectRow(ds.indexOf(records[i]), true);
21298         }
21299     },
21300
21301     /**
21302      * Gets the number of selected rows.
21303      * @return {Number}
21304      */
21305     getCount : function(){
21306         return this.selections.length;
21307     },
21308
21309     /**
21310      * Selects the first row in the grid.
21311      */
21312     selectFirstRow : function(){
21313         this.selectRow(0);
21314     },
21315
21316     /**
21317      * Select the last row.
21318      * @param {Boolean} keepExisting (optional) True to keep existing selections
21319      */
21320     selectLastRow : function(keepExisting){
21321         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21322     },
21323
21324     /**
21325      * Selects the row immediately following the last selected row.
21326      * @param {Boolean} keepExisting (optional) True to keep existing selections
21327      */
21328     selectNext : function(keepExisting){
21329         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21330             this.selectRow(this.last+1, keepExisting);
21331             this.grid.getView().focusRow(this.last);
21332         }
21333     },
21334
21335     /**
21336      * Selects the row that precedes the last selected row.
21337      * @param {Boolean} keepExisting (optional) True to keep existing selections
21338      */
21339     selectPrevious : function(keepExisting){
21340         if(this.last){
21341             this.selectRow(this.last-1, keepExisting);
21342             this.grid.getView().focusRow(this.last);
21343         }
21344     },
21345
21346     /**
21347      * Returns the selected records
21348      * @return {Array} Array of selected records
21349      */
21350     getSelections : function(){
21351         return [].concat(this.selections.items);
21352     },
21353
21354     /**
21355      * Returns the first selected record.
21356      * @return {Record}
21357      */
21358     getSelected : function(){
21359         return this.selections.itemAt(0);
21360     },
21361
21362
21363     /**
21364      * Clears all selections.
21365      */
21366     clearSelections : function(fast){
21367         if(this.locked) return;
21368         if(fast !== true){
21369             var ds = this.grid.dataSource;
21370             var s = this.selections;
21371             s.each(function(r){
21372                 this.deselectRow(ds.indexOfId(r.id));
21373             }, this);
21374             s.clear();
21375         }else{
21376             this.selections.clear();
21377         }
21378         this.last = false;
21379     },
21380
21381
21382     /**
21383      * Selects all rows.
21384      */
21385     selectAll : function(){
21386         if(this.locked) return;
21387         this.selections.clear();
21388         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21389             this.selectRow(i, true);
21390         }
21391     },
21392
21393     /**
21394      * Returns True if there is a selection.
21395      * @return {Boolean}
21396      */
21397     hasSelection : function(){
21398         return this.selections.length > 0;
21399     },
21400
21401     /**
21402      * Returns True if the specified row is selected.
21403      * @param {Number/Record} record The record or index of the record to check
21404      * @return {Boolean}
21405      */
21406     isSelected : function(index){
21407         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21408         return (r && this.selections.key(r.id) ? true : false);
21409     },
21410
21411     /**
21412      * Returns True if the specified record id is selected.
21413      * @param {String} id The id of record to check
21414      * @return {Boolean}
21415      */
21416     isIdSelected : function(id){
21417         return (this.selections.key(id) ? true : false);
21418     },
21419
21420     // private
21421     handleMouseDown : function(e, t){
21422         var view = this.grid.getView(), rowIndex;
21423         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21424             return;
21425         };
21426         if(e.shiftKey && this.last !== false){
21427             var last = this.last;
21428             this.selectRange(last, rowIndex, e.ctrlKey);
21429             this.last = last; // reset the last
21430             view.focusRow(rowIndex);
21431         }else{
21432             var isSelected = this.isSelected(rowIndex);
21433             if(e.button !== 0 && isSelected){
21434                 view.focusRow(rowIndex);
21435             }else if(e.ctrlKey && isSelected){
21436                 this.deselectRow(rowIndex);
21437             }else if(!isSelected){
21438                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21439                 view.focusRow(rowIndex);
21440             }
21441         }
21442         this.fireEvent("afterselectionchange", this);
21443     },
21444     // private
21445     handleDragableRowClick :  function(grid, rowIndex, e) 
21446     {
21447         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21448             this.selectRow(rowIndex, false);
21449             grid.view.focusRow(rowIndex);
21450              this.fireEvent("afterselectionchange", this);
21451         }
21452     },
21453     
21454     /**
21455      * Selects multiple rows.
21456      * @param {Array} rows Array of the indexes of the row to select
21457      * @param {Boolean} keepExisting (optional) True to keep existing selections
21458      */
21459     selectRows : function(rows, keepExisting){
21460         if(!keepExisting){
21461             this.clearSelections();
21462         }
21463         for(var i = 0, len = rows.length; i < len; i++){
21464             this.selectRow(rows[i], true);
21465         }
21466     },
21467
21468     /**
21469      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21470      * @param {Number} startRow The index of the first row in the range
21471      * @param {Number} endRow The index of the last row in the range
21472      * @param {Boolean} keepExisting (optional) True to retain existing selections
21473      */
21474     selectRange : function(startRow, endRow, keepExisting){
21475         if(this.locked) return;
21476         if(!keepExisting){
21477             this.clearSelections();
21478         }
21479         if(startRow <= endRow){
21480             for(var i = startRow; i <= endRow; i++){
21481                 this.selectRow(i, true);
21482             }
21483         }else{
21484             for(var i = startRow; i >= endRow; i--){
21485                 this.selectRow(i, true);
21486             }
21487         }
21488     },
21489
21490     /**
21491      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21492      * @param {Number} startRow The index of the first row in the range
21493      * @param {Number} endRow The index of the last row in the range
21494      */
21495     deselectRange : function(startRow, endRow, preventViewNotify){
21496         if(this.locked) return;
21497         for(var i = startRow; i <= endRow; i++){
21498             this.deselectRow(i, preventViewNotify);
21499         }
21500     },
21501
21502     /**
21503      * Selects a row.
21504      * @param {Number} row The index of the row to select
21505      * @param {Boolean} keepExisting (optional) True to keep existing selections
21506      */
21507     selectRow : function(index, keepExisting, preventViewNotify){
21508         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21509         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21510             if(!keepExisting || this.singleSelect){
21511                 this.clearSelections();
21512             }
21513             var r = this.grid.dataSource.getAt(index);
21514             this.selections.add(r);
21515             this.last = this.lastActive = index;
21516             if(!preventViewNotify){
21517                 this.grid.getView().onRowSelect(index);
21518             }
21519             this.fireEvent("rowselect", this, index, r);
21520             this.fireEvent("selectionchange", this);
21521         }
21522     },
21523
21524     /**
21525      * Deselects a row.
21526      * @param {Number} row The index of the row to deselect
21527      */
21528     deselectRow : function(index, preventViewNotify){
21529         if(this.locked) return;
21530         if(this.last == index){
21531             this.last = false;
21532         }
21533         if(this.lastActive == index){
21534             this.lastActive = false;
21535         }
21536         var r = this.grid.dataSource.getAt(index);
21537         this.selections.remove(r);
21538         if(!preventViewNotify){
21539             this.grid.getView().onRowDeselect(index);
21540         }
21541         this.fireEvent("rowdeselect", this, index);
21542         this.fireEvent("selectionchange", this);
21543     },
21544
21545     // private
21546     restoreLast : function(){
21547         if(this._last){
21548             this.last = this._last;
21549         }
21550     },
21551
21552     // private
21553     acceptsNav : function(row, col, cm){
21554         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21555     },
21556
21557     // private
21558     onEditorKey : function(field, e){
21559         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21560         if(k == e.TAB){
21561             e.stopEvent();
21562             ed.completeEdit();
21563             if(e.shiftKey){
21564                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21565             }else{
21566                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21567             }
21568         }else if(k == e.ENTER && !e.ctrlKey){
21569             e.stopEvent();
21570             ed.completeEdit();
21571             if(e.shiftKey){
21572                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21573             }else{
21574                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21575             }
21576         }else if(k == e.ESC){
21577             ed.cancelEdit();
21578         }
21579         if(newCell){
21580             g.startEditing(newCell[0], newCell[1]);
21581         }
21582     }
21583 });/*
21584  * Based on:
21585  * Ext JS Library 1.1.1
21586  * Copyright(c) 2006-2007, Ext JS, LLC.
21587  *
21588  * Originally Released Under LGPL - original licence link has changed is not relivant.
21589  *
21590  * Fork - LGPL
21591  * <script type="text/javascript">
21592  */
21593  
21594 /**
21595  * @class Roo.bootstrap.PagingToolbar
21596  * @extends Roo.Row
21597  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21598  * @constructor
21599  * Create a new PagingToolbar
21600  * @param {Object} config The config object
21601  */
21602 Roo.bootstrap.PagingToolbar = function(config)
21603 {
21604     // old args format still supported... - xtype is prefered..
21605         // created from xtype...
21606     var ds = config.dataSource;
21607     this.toolbarItems = [];
21608     if (config.items) {
21609         this.toolbarItems = config.items;
21610 //        config.items = [];
21611     }
21612     
21613     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21614     this.ds = ds;
21615     this.cursor = 0;
21616     if (ds) { 
21617         this.bind(ds);
21618     }
21619     
21620     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21621     
21622 };
21623
21624 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21625     /**
21626      * @cfg {Roo.data.Store} dataSource
21627      * The underlying data store providing the paged data
21628      */
21629     /**
21630      * @cfg {String/HTMLElement/Element} container
21631      * container The id or element that will contain the toolbar
21632      */
21633     /**
21634      * @cfg {Boolean} displayInfo
21635      * True to display the displayMsg (defaults to false)
21636      */
21637     /**
21638      * @cfg {Number} pageSize
21639      * The number of records to display per page (defaults to 20)
21640      */
21641     pageSize: 20,
21642     /**
21643      * @cfg {String} displayMsg
21644      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21645      */
21646     displayMsg : 'Displaying {0} - {1} of {2}',
21647     /**
21648      * @cfg {String} emptyMsg
21649      * The message to display when no records are found (defaults to "No data to display")
21650      */
21651     emptyMsg : 'No data to display',
21652     /**
21653      * Customizable piece of the default paging text (defaults to "Page")
21654      * @type String
21655      */
21656     beforePageText : "Page",
21657     /**
21658      * Customizable piece of the default paging text (defaults to "of %0")
21659      * @type String
21660      */
21661     afterPageText : "of {0}",
21662     /**
21663      * Customizable piece of the default paging text (defaults to "First Page")
21664      * @type String
21665      */
21666     firstText : "First Page",
21667     /**
21668      * Customizable piece of the default paging text (defaults to "Previous Page")
21669      * @type String
21670      */
21671     prevText : "Previous Page",
21672     /**
21673      * Customizable piece of the default paging text (defaults to "Next Page")
21674      * @type String
21675      */
21676     nextText : "Next Page",
21677     /**
21678      * Customizable piece of the default paging text (defaults to "Last Page")
21679      * @type String
21680      */
21681     lastText : "Last Page",
21682     /**
21683      * Customizable piece of the default paging text (defaults to "Refresh")
21684      * @type String
21685      */
21686     refreshText : "Refresh",
21687
21688     buttons : false,
21689     // private
21690     onRender : function(ct, position) 
21691     {
21692         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21693         this.navgroup.parentId = this.id;
21694         this.navgroup.onRender(this.el, null);
21695         // add the buttons to the navgroup
21696         
21697         if(this.displayInfo){
21698             Roo.log(this.el.select('ul.navbar-nav',true).first());
21699             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21700             this.displayEl = this.el.select('.x-paging-info', true).first();
21701 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21702 //            this.displayEl = navel.el.select('span',true).first();
21703         }
21704         
21705         var _this = this;
21706         
21707         if(this.buttons){
21708             Roo.each(_this.buttons, function(e){
21709                Roo.factory(e).onRender(_this.el, null);
21710             });
21711         }
21712             
21713         Roo.each(_this.toolbarItems, function(e) {
21714             _this.navgroup.addItem(e);
21715         });
21716         
21717         
21718         this.first = this.navgroup.addItem({
21719             tooltip: this.firstText,
21720             cls: "prev",
21721             icon : 'fa fa-backward',
21722             disabled: true,
21723             preventDefault: true,
21724             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21725         });
21726         
21727         this.prev =  this.navgroup.addItem({
21728             tooltip: this.prevText,
21729             cls: "prev",
21730             icon : 'fa fa-step-backward',
21731             disabled: true,
21732             preventDefault: true,
21733             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21734         });
21735     //this.addSeparator();
21736         
21737         
21738         var field = this.navgroup.addItem( {
21739             tagtype : 'span',
21740             cls : 'x-paging-position',
21741             
21742             html : this.beforePageText  +
21743                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21744                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21745          } ); //?? escaped?
21746         
21747         this.field = field.el.select('input', true).first();
21748         this.field.on("keydown", this.onPagingKeydown, this);
21749         this.field.on("focus", function(){this.dom.select();});
21750     
21751     
21752         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21753         //this.field.setHeight(18);
21754         //this.addSeparator();
21755         this.next = this.navgroup.addItem({
21756             tooltip: this.nextText,
21757             cls: "next",
21758             html : ' <i class="fa fa-step-forward">',
21759             disabled: true,
21760             preventDefault: true,
21761             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21762         });
21763         this.last = this.navgroup.addItem({
21764             tooltip: this.lastText,
21765             icon : 'fa fa-forward',
21766             cls: "next",
21767             disabled: true,
21768             preventDefault: true,
21769             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21770         });
21771     //this.addSeparator();
21772         this.loading = this.navgroup.addItem({
21773             tooltip: this.refreshText,
21774             icon: 'fa fa-refresh',
21775             preventDefault: true,
21776             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21777         });
21778
21779     },
21780
21781     // private
21782     updateInfo : function(){
21783         if(this.displayEl){
21784             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21785             var msg = count == 0 ?
21786                 this.emptyMsg :
21787                 String.format(
21788                     this.displayMsg,
21789                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21790                 );
21791             this.displayEl.update(msg);
21792         }
21793     },
21794
21795     // private
21796     onLoad : function(ds, r, o){
21797        this.cursor = o.params ? o.params.start : 0;
21798        var d = this.getPageData(),
21799             ap = d.activePage,
21800             ps = d.pages;
21801         
21802        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21803        this.field.dom.value = ap;
21804        this.first.setDisabled(ap == 1);
21805        this.prev.setDisabled(ap == 1);
21806        this.next.setDisabled(ap == ps);
21807        this.last.setDisabled(ap == ps);
21808        this.loading.enable();
21809        this.updateInfo();
21810     },
21811
21812     // private
21813     getPageData : function(){
21814         var total = this.ds.getTotalCount();
21815         return {
21816             total : total,
21817             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21818             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21819         };
21820     },
21821
21822     // private
21823     onLoadError : function(){
21824         this.loading.enable();
21825     },
21826
21827     // private
21828     onPagingKeydown : function(e){
21829         var k = e.getKey();
21830         var d = this.getPageData();
21831         if(k == e.RETURN){
21832             var v = this.field.dom.value, pageNum;
21833             if(!v || isNaN(pageNum = parseInt(v, 10))){
21834                 this.field.dom.value = d.activePage;
21835                 return;
21836             }
21837             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21838             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21839             e.stopEvent();
21840         }
21841         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))
21842         {
21843           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21844           this.field.dom.value = pageNum;
21845           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21846           e.stopEvent();
21847         }
21848         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21849         {
21850           var v = this.field.dom.value, pageNum; 
21851           var increment = (e.shiftKey) ? 10 : 1;
21852           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21853             increment *= -1;
21854           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21855             this.field.dom.value = d.activePage;
21856             return;
21857           }
21858           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21859           {
21860             this.field.dom.value = parseInt(v, 10) + increment;
21861             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21862             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21863           }
21864           e.stopEvent();
21865         }
21866     },
21867
21868     // private
21869     beforeLoad : function(){
21870         if(this.loading){
21871             this.loading.disable();
21872         }
21873     },
21874
21875     // private
21876     onClick : function(which){
21877         
21878         var ds = this.ds;
21879         if (!ds) {
21880             return;
21881         }
21882         
21883         switch(which){
21884             case "first":
21885                 ds.load({params:{start: 0, limit: this.pageSize}});
21886             break;
21887             case "prev":
21888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21889             break;
21890             case "next":
21891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21892             break;
21893             case "last":
21894                 var total = ds.getTotalCount();
21895                 var extra = total % this.pageSize;
21896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21898             break;
21899             case "refresh":
21900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21901             break;
21902         }
21903     },
21904
21905     /**
21906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21907      * @param {Roo.data.Store} store The data store to unbind
21908      */
21909     unbind : function(ds){
21910         ds.un("beforeload", this.beforeLoad, this);
21911         ds.un("load", this.onLoad, this);
21912         ds.un("loadexception", this.onLoadError, this);
21913         ds.un("remove", this.updateInfo, this);
21914         ds.un("add", this.updateInfo, this);
21915         this.ds = undefined;
21916     },
21917
21918     /**
21919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21920      * @param {Roo.data.Store} store The data store to bind
21921      */
21922     bind : function(ds){
21923         ds.on("beforeload", this.beforeLoad, this);
21924         ds.on("load", this.onLoad, this);
21925         ds.on("loadexception", this.onLoadError, this);
21926         ds.on("remove", this.updateInfo, this);
21927         ds.on("add", this.updateInfo, this);
21928         this.ds = ds;
21929     }
21930 });/*
21931  * - LGPL
21932  *
21933  * element
21934  * 
21935  */
21936
21937 /**
21938  * @class Roo.bootstrap.MessageBar
21939  * @extends Roo.bootstrap.Component
21940  * Bootstrap MessageBar class
21941  * @cfg {String} html contents of the MessageBar
21942  * @cfg {String} weight (info | success | warning | danger) default info
21943  * @cfg {String} beforeClass insert the bar before the given class
21944  * @cfg {Boolean} closable (true | false) default false
21945  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21946  * 
21947  * @constructor
21948  * Create a new Element
21949  * @param {Object} config The config object
21950  */
21951
21952 Roo.bootstrap.MessageBar = function(config){
21953     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21954 };
21955
21956 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21957     
21958     html: '',
21959     weight: 'info',
21960     closable: false,
21961     fixed: false,
21962     beforeClass: 'bootstrap-sticky-wrap',
21963     
21964     getAutoCreate : function(){
21965         
21966         var cfg = {
21967             tag: 'div',
21968             cls: 'alert alert-dismissable alert-' + this.weight,
21969             cn: [
21970                 {
21971                     tag: 'span',
21972                     cls: 'message',
21973                     html: this.html || ''
21974                 }
21975             ]
21976         }
21977         
21978         if(this.fixed){
21979             cfg.cls += ' alert-messages-fixed';
21980         }
21981         
21982         if(this.closable){
21983             cfg.cn.push({
21984                 tag: 'button',
21985                 cls: 'close',
21986                 html: 'x'
21987             });
21988         }
21989         
21990         return cfg;
21991     },
21992     
21993     onRender : function(ct, position)
21994     {
21995         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21996         
21997         if(!this.el){
21998             var cfg = Roo.apply({},  this.getAutoCreate());
21999             cfg.id = Roo.id();
22000             
22001             if (this.cls) {
22002                 cfg.cls += ' ' + this.cls;
22003             }
22004             if (this.style) {
22005                 cfg.style = this.style;
22006             }
22007             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22008             
22009             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22010         }
22011         
22012         this.el.select('>button.close').on('click', this.hide, this);
22013         
22014     },
22015     
22016     show : function()
22017     {
22018         if (!this.rendered) {
22019             this.render();
22020         }
22021         
22022         this.el.show();
22023         
22024         this.fireEvent('show', this);
22025         
22026     },
22027     
22028     hide : function()
22029     {
22030         if (!this.rendered) {
22031             this.render();
22032         }
22033         
22034         this.el.hide();
22035         
22036         this.fireEvent('hide', this);
22037     },
22038     
22039     update : function()
22040     {
22041 //        var e = this.el.dom.firstChild;
22042 //        
22043 //        if(this.closable){
22044 //            e = e.nextSibling;
22045 //        }
22046 //        
22047 //        e.data = this.html || '';
22048
22049         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22050     }
22051    
22052 });
22053
22054  
22055
22056      /*
22057  * - LGPL
22058  *
22059  * Graph
22060  * 
22061  */
22062
22063
22064 /**
22065  * @class Roo.bootstrap.Graph
22066  * @extends Roo.bootstrap.Component
22067  * Bootstrap Graph class
22068 > Prameters
22069  -sm {number} sm 4
22070  -md {number} md 5
22071  @cfg {String} graphtype  bar | vbar | pie
22072  @cfg {number} g_x coodinator | centre x (pie)
22073  @cfg {number} g_y coodinator | centre y (pie)
22074  @cfg {number} g_r radius (pie)
22075  @cfg {number} g_height height of the chart (respected by all elements in the set)
22076  @cfg {number} g_width width of the chart (respected by all elements in the set)
22077  @cfg {Object} title The title of the chart
22078     
22079  -{Array}  values
22080  -opts (object) options for the chart 
22081      o {
22082      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22083      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22084      o vgutter (number)
22085      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.
22086      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22087      o to
22088      o stretch (boolean)
22089      o }
22090  -opts (object) options for the pie
22091      o{
22092      o cut
22093      o startAngle (number)
22094      o endAngle (number)
22095      } 
22096  *
22097  * @constructor
22098  * Create a new Input
22099  * @param {Object} config The config object
22100  */
22101
22102 Roo.bootstrap.Graph = function(config){
22103     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22104     
22105     this.addEvents({
22106         // img events
22107         /**
22108          * @event click
22109          * The img click event for the img.
22110          * @param {Roo.EventObject} e
22111          */
22112         "click" : true
22113     });
22114 };
22115
22116 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22117     
22118     sm: 4,
22119     md: 5,
22120     graphtype: 'bar',
22121     g_height: 250,
22122     g_width: 400,
22123     g_x: 50,
22124     g_y: 50,
22125     g_r: 30,
22126     opts:{
22127         //g_colors: this.colors,
22128         g_type: 'soft',
22129         g_gutter: '20%'
22130
22131     },
22132     title : false,
22133
22134     getAutoCreate : function(){
22135         
22136         var cfg = {
22137             tag: 'div',
22138             html : null
22139         }
22140         
22141         
22142         return  cfg;
22143     },
22144
22145     onRender : function(ct,position){
22146         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22147         this.raphael = Raphael(this.el.dom);
22148         
22149                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22150                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22151                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22152                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22153                 /*
22154                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22155                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22156                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22157                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22158                 
22159                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22160                 r.barchart(330, 10, 300, 220, data1);
22161                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22162                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22163                 */
22164                 
22165                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22166                 // r.barchart(30, 30, 560, 250,  xdata, {
22167                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22168                 //     axis : "0 0 1 1",
22169                 //     axisxlabels :  xdata
22170                 //     //yvalues : cols,
22171                    
22172                 // });
22173 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22174 //        
22175 //        this.load(null,xdata,{
22176 //                axis : "0 0 1 1",
22177 //                axisxlabels :  xdata
22178 //                });
22179
22180     },
22181
22182     load : function(graphtype,xdata,opts){
22183         this.raphael.clear();
22184         if(!graphtype) {
22185             graphtype = this.graphtype;
22186         }
22187         if(!opts){
22188             opts = this.opts;
22189         }
22190         var r = this.raphael,
22191             fin = function () {
22192                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22193             },
22194             fout = function () {
22195                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22196             },
22197             pfin = function() {
22198                 this.sector.stop();
22199                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22200
22201                 if (this.label) {
22202                     this.label[0].stop();
22203                     this.label[0].attr({ r: 7.5 });
22204                     this.label[1].attr({ "font-weight": 800 });
22205                 }
22206             },
22207             pfout = function() {
22208                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22209
22210                 if (this.label) {
22211                     this.label[0].animate({ r: 5 }, 500, "bounce");
22212                     this.label[1].attr({ "font-weight": 400 });
22213                 }
22214             };
22215
22216         switch(graphtype){
22217             case 'bar':
22218                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22219                 break;
22220             case 'hbar':
22221                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22222                 break;
22223             case 'pie':
22224 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22225 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22226 //            
22227                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22228                 
22229                 break;
22230
22231         }
22232         
22233         if(this.title){
22234             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22235         }
22236         
22237     },
22238     
22239     setTitle: function(o)
22240     {
22241         this.title = o;
22242     },
22243     
22244     initEvents: function() {
22245         
22246         if(!this.href){
22247             this.el.on('click', this.onClick, this);
22248         }
22249     },
22250     
22251     onClick : function(e)
22252     {
22253         Roo.log('img onclick');
22254         this.fireEvent('click', this, e);
22255     }
22256    
22257 });
22258
22259  
22260 /*
22261  * - LGPL
22262  *
22263  * numberBox
22264  * 
22265  */
22266 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22267
22268 /**
22269  * @class Roo.bootstrap.dash.NumberBox
22270  * @extends Roo.bootstrap.Component
22271  * Bootstrap NumberBox class
22272  * @cfg {String} headline Box headline
22273  * @cfg {String} content Box content
22274  * @cfg {String} icon Box icon
22275  * @cfg {String} footer Footer text
22276  * @cfg {String} fhref Footer href
22277  * 
22278  * @constructor
22279  * Create a new NumberBox
22280  * @param {Object} config The config object
22281  */
22282
22283
22284 Roo.bootstrap.dash.NumberBox = function(config){
22285     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22286     
22287 };
22288
22289 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22290     
22291     headline : '',
22292     content : '',
22293     icon : '',
22294     footer : '',
22295     fhref : '',
22296     ficon : '',
22297     
22298     getAutoCreate : function(){
22299         
22300         var cfg = {
22301             tag : 'div',
22302             cls : 'small-box ',
22303             cn : [
22304                 {
22305                     tag : 'div',
22306                     cls : 'inner',
22307                     cn :[
22308                         {
22309                             tag : 'h3',
22310                             cls : 'roo-headline',
22311                             html : this.headline
22312                         },
22313                         {
22314                             tag : 'p',
22315                             cls : 'roo-content',
22316                             html : this.content
22317                         }
22318                     ]
22319                 }
22320             ]
22321         }
22322         
22323         if(this.icon){
22324             cfg.cn.push({
22325                 tag : 'div',
22326                 cls : 'icon',
22327                 cn :[
22328                     {
22329                         tag : 'i',
22330                         cls : 'ion ' + this.icon
22331                     }
22332                 ]
22333             });
22334         }
22335         
22336         if(this.footer){
22337             var footer = {
22338                 tag : 'a',
22339                 cls : 'small-box-footer',
22340                 href : this.fhref || '#',
22341                 html : this.footer
22342             };
22343             
22344             cfg.cn.push(footer);
22345             
22346         }
22347         
22348         return  cfg;
22349     },
22350
22351     onRender : function(ct,position){
22352         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22353
22354
22355        
22356                 
22357     },
22358
22359     setHeadline: function (value)
22360     {
22361         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22362     },
22363     
22364     setFooter: function (value, href)
22365     {
22366         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22367         
22368         if(href){
22369             this.el.select('a.small-box-footer',true).first().attr('href', href);
22370         }
22371         
22372     },
22373
22374     setContent: function (value)
22375     {
22376         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22377     },
22378
22379     initEvents: function() 
22380     {   
22381         
22382     }
22383     
22384 });
22385
22386  
22387 /*
22388  * - LGPL
22389  *
22390  * TabBox
22391  * 
22392  */
22393 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22394
22395 /**
22396  * @class Roo.bootstrap.dash.TabBox
22397  * @extends Roo.bootstrap.Component
22398  * Bootstrap TabBox class
22399  * @cfg {String} title Title of the TabBox
22400  * @cfg {String} icon Icon of the TabBox
22401  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22402  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22403  * 
22404  * @constructor
22405  * Create a new TabBox
22406  * @param {Object} config The config object
22407  */
22408
22409
22410 Roo.bootstrap.dash.TabBox = function(config){
22411     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22412     this.addEvents({
22413         // raw events
22414         /**
22415          * @event addpane
22416          * When a pane is added
22417          * @param {Roo.bootstrap.dash.TabPane} pane
22418          */
22419         "addpane" : true,
22420         /**
22421          * @event activatepane
22422          * When a pane is activated
22423          * @param {Roo.bootstrap.dash.TabPane} pane
22424          */
22425         "activatepane" : true
22426         
22427          
22428     });
22429     
22430     this.panes = [];
22431 };
22432
22433 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22434
22435     title : '',
22436     icon : false,
22437     showtabs : true,
22438     tabScrollable : false,
22439     
22440     getChildContainer : function()
22441     {
22442         return this.el.select('.tab-content', true).first();
22443     },
22444     
22445     getAutoCreate : function(){
22446         
22447         var header = {
22448             tag: 'li',
22449             cls: 'pull-left header',
22450             html: this.title,
22451             cn : []
22452         };
22453         
22454         if(this.icon){
22455             header.cn.push({
22456                 tag: 'i',
22457                 cls: 'fa ' + this.icon
22458             });
22459         }
22460         
22461         var h = {
22462             tag: 'ul',
22463             cls: 'nav nav-tabs pull-right',
22464             cn: [
22465                 header
22466             ]
22467         };
22468         
22469         if(this.tabScrollable){
22470             h = {
22471                 tag: 'div',
22472                 cls: 'tab-header',
22473                 cn: [
22474                     {
22475                         tag: 'ul',
22476                         cls: 'nav nav-tabs pull-right',
22477                         cn: [
22478                             header
22479                         ]
22480                     }
22481                 ]
22482             }
22483         }
22484         
22485         var cfg = {
22486             tag: 'div',
22487             cls: 'nav-tabs-custom',
22488             cn: [
22489                 h,
22490                 {
22491                     tag: 'div',
22492                     cls: 'tab-content no-padding',
22493                     cn: []
22494                 }
22495             ]
22496         }
22497
22498         return  cfg;
22499     },
22500     initEvents : function()
22501     {
22502         //Roo.log('add add pane handler');
22503         this.on('addpane', this.onAddPane, this);
22504     },
22505      /**
22506      * Updates the box title
22507      * @param {String} html to set the title to.
22508      */
22509     setTitle : function(value)
22510     {
22511         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22512     },
22513     onAddPane : function(pane)
22514     {
22515         this.panes.push(pane);
22516         //Roo.log('addpane');
22517         //Roo.log(pane);
22518         // tabs are rendere left to right..
22519         if(!this.showtabs){
22520             return;
22521         }
22522         
22523         var ctr = this.el.select('.nav-tabs', true).first();
22524          
22525          
22526         var existing = ctr.select('.nav-tab',true);
22527         var qty = existing.getCount();;
22528         
22529         
22530         var tab = ctr.createChild({
22531             tag : 'li',
22532             cls : 'nav-tab' + (qty ? '' : ' active'),
22533             cn : [
22534                 {
22535                     tag : 'a',
22536                     href:'#',
22537                     html : pane.title
22538                 }
22539             ]
22540         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22541         pane.tab = tab;
22542         
22543         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22544         if (!qty) {
22545             pane.el.addClass('active');
22546         }
22547         
22548                 
22549     },
22550     onTabClick : function(ev,un,ob,pane)
22551     {
22552         //Roo.log('tab - prev default');
22553         ev.preventDefault();
22554         
22555         
22556         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22557         pane.tab.addClass('active');
22558         //Roo.log(pane.title);
22559         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22560         // technically we should have a deactivate event.. but maybe add later.
22561         // and it should not de-activate the selected tab...
22562         this.fireEvent('activatepane', pane);
22563         pane.el.addClass('active');
22564         pane.fireEvent('activate');
22565         
22566         
22567     },
22568     
22569     getActivePane : function()
22570     {
22571         var r = false;
22572         Roo.each(this.panes, function(p) {
22573             if(p.el.hasClass('active')){
22574                 r = p;
22575                 return false;
22576             }
22577             
22578             return;
22579         });
22580         
22581         return r;
22582     }
22583     
22584     
22585 });
22586
22587  
22588 /*
22589  * - LGPL
22590  *
22591  * Tab pane
22592  * 
22593  */
22594 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22595 /**
22596  * @class Roo.bootstrap.TabPane
22597  * @extends Roo.bootstrap.Component
22598  * Bootstrap TabPane class
22599  * @cfg {Boolean} active (false | true) Default false
22600  * @cfg {String} title title of panel
22601
22602  * 
22603  * @constructor
22604  * Create a new TabPane
22605  * @param {Object} config The config object
22606  */
22607
22608 Roo.bootstrap.dash.TabPane = function(config){
22609     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22610     
22611     this.addEvents({
22612         // raw events
22613         /**
22614          * @event activate
22615          * When a pane is activated
22616          * @param {Roo.bootstrap.dash.TabPane} pane
22617          */
22618         "activate" : true
22619          
22620     });
22621 };
22622
22623 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22624     
22625     active : false,
22626     title : '',
22627     
22628     // the tabBox that this is attached to.
22629     tab : false,
22630      
22631     getAutoCreate : function() 
22632     {
22633         var cfg = {
22634             tag: 'div',
22635             cls: 'tab-pane'
22636         }
22637         
22638         if(this.active){
22639             cfg.cls += ' active';
22640         }
22641         
22642         return cfg;
22643     },
22644     initEvents  : function()
22645     {
22646         //Roo.log('trigger add pane handler');
22647         this.parent().fireEvent('addpane', this)
22648     },
22649     
22650      /**
22651      * Updates the tab title 
22652      * @param {String} html to set the title to.
22653      */
22654     setTitle: function(str)
22655     {
22656         if (!this.tab) {
22657             return;
22658         }
22659         this.title = str;
22660         this.tab.select('a', true).first().dom.innerHTML = str;
22661         
22662     }
22663     
22664     
22665     
22666 });
22667
22668  
22669
22670
22671  /*
22672  * - LGPL
22673  *
22674  * menu
22675  * 
22676  */
22677 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22678
22679 /**
22680  * @class Roo.bootstrap.menu.Menu
22681  * @extends Roo.bootstrap.Component
22682  * Bootstrap Menu class - container for Menu
22683  * @cfg {String} html Text of the menu
22684  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22685  * @cfg {String} icon Font awesome icon
22686  * @cfg {String} pos Menu align to (top | bottom) default bottom
22687  * 
22688  * 
22689  * @constructor
22690  * Create a new Menu
22691  * @param {Object} config The config object
22692  */
22693
22694
22695 Roo.bootstrap.menu.Menu = function(config){
22696     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22697     
22698     this.addEvents({
22699         /**
22700          * @event beforeshow
22701          * Fires before this menu is displayed
22702          * @param {Roo.bootstrap.menu.Menu} this
22703          */
22704         beforeshow : true,
22705         /**
22706          * @event beforehide
22707          * Fires before this menu is hidden
22708          * @param {Roo.bootstrap.menu.Menu} this
22709          */
22710         beforehide : true,
22711         /**
22712          * @event show
22713          * Fires after this menu is displayed
22714          * @param {Roo.bootstrap.menu.Menu} this
22715          */
22716         show : true,
22717         /**
22718          * @event hide
22719          * Fires after this menu is hidden
22720          * @param {Roo.bootstrap.menu.Menu} this
22721          */
22722         hide : true,
22723         /**
22724          * @event click
22725          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22726          * @param {Roo.bootstrap.menu.Menu} this
22727          * @param {Roo.EventObject} e
22728          */
22729         click : true
22730     });
22731     
22732 };
22733
22734 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22735     
22736     submenu : false,
22737     html : '',
22738     weight : 'default',
22739     icon : false,
22740     pos : 'bottom',
22741     
22742     
22743     getChildContainer : function() {
22744         if(this.isSubMenu){
22745             return this.el;
22746         }
22747         
22748         return this.el.select('ul.dropdown-menu', true).first();  
22749     },
22750     
22751     getAutoCreate : function()
22752     {
22753         var text = [
22754             {
22755                 tag : 'span',
22756                 cls : 'roo-menu-text',
22757                 html : this.html
22758             }
22759         ];
22760         
22761         if(this.icon){
22762             text.unshift({
22763                 tag : 'i',
22764                 cls : 'fa ' + this.icon
22765             })
22766         }
22767         
22768         
22769         var cfg = {
22770             tag : 'div',
22771             cls : 'btn-group',
22772             cn : [
22773                 {
22774                     tag : 'button',
22775                     cls : 'dropdown-button btn btn-' + this.weight,
22776                     cn : text
22777                 },
22778                 {
22779                     tag : 'button',
22780                     cls : 'dropdown-toggle btn btn-' + this.weight,
22781                     cn : [
22782                         {
22783                             tag : 'span',
22784                             cls : 'caret'
22785                         }
22786                     ]
22787                 },
22788                 {
22789                     tag : 'ul',
22790                     cls : 'dropdown-menu'
22791                 }
22792             ]
22793             
22794         };
22795         
22796         if(this.pos == 'top'){
22797             cfg.cls += ' dropup';
22798         }
22799         
22800         if(this.isSubMenu){
22801             cfg = {
22802                 tag : 'ul',
22803                 cls : 'dropdown-menu'
22804             }
22805         }
22806         
22807         return cfg;
22808     },
22809     
22810     onRender : function(ct, position)
22811     {
22812         this.isSubMenu = ct.hasClass('dropdown-submenu');
22813         
22814         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22815     },
22816     
22817     initEvents : function() 
22818     {
22819         if(this.isSubMenu){
22820             return;
22821         }
22822         
22823         this.hidden = true;
22824         
22825         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22826         this.triggerEl.on('click', this.onTriggerPress, this);
22827         
22828         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22829         this.buttonEl.on('click', this.onClick, this);
22830         
22831     },
22832     
22833     list : function()
22834     {
22835         if(this.isSubMenu){
22836             return this.el;
22837         }
22838         
22839         return this.el.select('ul.dropdown-menu', true).first();
22840     },
22841     
22842     onClick : function(e)
22843     {
22844         this.fireEvent("click", this, e);
22845     },
22846     
22847     onTriggerPress  : function(e)
22848     {   
22849         if (this.isVisible()) {
22850             this.hide();
22851         } else {
22852             this.show();
22853         }
22854     },
22855     
22856     isVisible : function(){
22857         return !this.hidden;
22858     },
22859     
22860     show : function()
22861     {
22862         this.fireEvent("beforeshow", this);
22863         
22864         this.hidden = false;
22865         this.el.addClass('open');
22866         
22867         Roo.get(document).on("mouseup", this.onMouseUp, this);
22868         
22869         this.fireEvent("show", this);
22870         
22871         
22872     },
22873     
22874     hide : function()
22875     {
22876         this.fireEvent("beforehide", this);
22877         
22878         this.hidden = true;
22879         this.el.removeClass('open');
22880         
22881         Roo.get(document).un("mouseup", this.onMouseUp);
22882         
22883         this.fireEvent("hide", this);
22884     },
22885     
22886     onMouseUp : function()
22887     {
22888         this.hide();
22889     }
22890     
22891 });
22892
22893  
22894  /*
22895  * - LGPL
22896  *
22897  * menu item
22898  * 
22899  */
22900 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22901
22902 /**
22903  * @class Roo.bootstrap.menu.Item
22904  * @extends Roo.bootstrap.Component
22905  * Bootstrap MenuItem class
22906  * @cfg {Boolean} submenu (true | false) default false
22907  * @cfg {String} html text of the item
22908  * @cfg {String} href the link
22909  * @cfg {Boolean} disable (true | false) default false
22910  * @cfg {Boolean} preventDefault (true | false) default true
22911  * @cfg {String} icon Font awesome icon
22912  * @cfg {String} pos Submenu align to (left | right) default right 
22913  * 
22914  * 
22915  * @constructor
22916  * Create a new Item
22917  * @param {Object} config The config object
22918  */
22919
22920
22921 Roo.bootstrap.menu.Item = function(config){
22922     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22923     this.addEvents({
22924         /**
22925          * @event mouseover
22926          * Fires when the mouse is hovering over this menu
22927          * @param {Roo.bootstrap.menu.Item} this
22928          * @param {Roo.EventObject} e
22929          */
22930         mouseover : true,
22931         /**
22932          * @event mouseout
22933          * Fires when the mouse exits this menu
22934          * @param {Roo.bootstrap.menu.Item} this
22935          * @param {Roo.EventObject} e
22936          */
22937         mouseout : true,
22938         // raw events
22939         /**
22940          * @event click
22941          * The raw click event for the entire grid.
22942          * @param {Roo.EventObject} e
22943          */
22944         click : true
22945     });
22946 };
22947
22948 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22949     
22950     submenu : false,
22951     href : '',
22952     html : '',
22953     preventDefault: true,
22954     disable : false,
22955     icon : false,
22956     pos : 'right',
22957     
22958     getAutoCreate : function()
22959     {
22960         var text = [
22961             {
22962                 tag : 'span',
22963                 cls : 'roo-menu-item-text',
22964                 html : this.html
22965             }
22966         ];
22967         
22968         if(this.icon){
22969             text.unshift({
22970                 tag : 'i',
22971                 cls : 'fa ' + this.icon
22972             })
22973         }
22974         
22975         var cfg = {
22976             tag : 'li',
22977             cn : [
22978                 {
22979                     tag : 'a',
22980                     href : this.href || '#',
22981                     cn : text
22982                 }
22983             ]
22984         };
22985         
22986         if(this.disable){
22987             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22988         }
22989         
22990         if(this.submenu){
22991             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22992             
22993             if(this.pos == 'left'){
22994                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22995             }
22996         }
22997         
22998         return cfg;
22999     },
23000     
23001     initEvents : function() 
23002     {
23003         this.el.on('mouseover', this.onMouseOver, this);
23004         this.el.on('mouseout', this.onMouseOut, this);
23005         
23006         this.el.select('a', true).first().on('click', this.onClick, this);
23007         
23008     },
23009     
23010     onClick : function(e)
23011     {
23012         if(this.preventDefault){
23013             e.preventDefault();
23014         }
23015         
23016         this.fireEvent("click", this, e);
23017     },
23018     
23019     onMouseOver : function(e)
23020     {
23021         if(this.submenu && this.pos == 'left'){
23022             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23023         }
23024         
23025         this.fireEvent("mouseover", this, e);
23026     },
23027     
23028     onMouseOut : function(e)
23029     {
23030         this.fireEvent("mouseout", this, e);
23031     }
23032 });
23033
23034  
23035
23036  /*
23037  * - LGPL
23038  *
23039  * menu separator
23040  * 
23041  */
23042 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23043
23044 /**
23045  * @class Roo.bootstrap.menu.Separator
23046  * @extends Roo.bootstrap.Component
23047  * Bootstrap Separator class
23048  * 
23049  * @constructor
23050  * Create a new Separator
23051  * @param {Object} config The config object
23052  */
23053
23054
23055 Roo.bootstrap.menu.Separator = function(config){
23056     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23057 };
23058
23059 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23060     
23061     getAutoCreate : function(){
23062         var cfg = {
23063             tag : 'li',
23064             cls: 'divider'
23065         };
23066         
23067         return cfg;
23068     }
23069    
23070 });
23071
23072  
23073
23074  /*
23075  * - LGPL
23076  *
23077  * Tooltip
23078  * 
23079  */
23080
23081 /**
23082  * @class Roo.bootstrap.Tooltip
23083  * Bootstrap Tooltip class
23084  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23085  * to determine which dom element triggers the tooltip.
23086  * 
23087  * It needs to add support for additional attributes like tooltip-position
23088  * 
23089  * @constructor
23090  * Create a new Toolti
23091  * @param {Object} config The config object
23092  */
23093
23094 Roo.bootstrap.Tooltip = function(config){
23095     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23096 };
23097
23098 Roo.apply(Roo.bootstrap.Tooltip, {
23099     /**
23100      * @function init initialize tooltip monitoring.
23101      * @static
23102      */
23103     currentEl : false,
23104     currentTip : false,
23105     currentRegion : false,
23106     
23107     //  init : delay?
23108     
23109     init : function()
23110     {
23111         Roo.get(document).on('mouseover', this.enter ,this);
23112         Roo.get(document).on('mouseout', this.leave, this);
23113          
23114         
23115         this.currentTip = new Roo.bootstrap.Tooltip();
23116     },
23117     
23118     enter : function(ev)
23119     {
23120         var dom = ev.getTarget();
23121         
23122         //Roo.log(['enter',dom]);
23123         var el = Roo.fly(dom);
23124         if (this.currentEl) {
23125             //Roo.log(dom);
23126             //Roo.log(this.currentEl);
23127             //Roo.log(this.currentEl.contains(dom));
23128             if (this.currentEl == el) {
23129                 return;
23130             }
23131             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23132                 return;
23133             }
23134
23135         }
23136         
23137         
23138         
23139         if (this.currentTip.el) {
23140             this.currentTip.el.hide(); // force hiding...
23141         }    
23142         //Roo.log(ev);
23143         var bindEl = el;
23144         
23145         // you can not look for children, as if el is the body.. then everythign is the child..
23146         if (!el.attr('tooltip')) { //
23147             if (!el.select("[tooltip]").elements.length) {
23148                 return;
23149             }
23150             // is the mouse over this child...?
23151             bindEl = el.select("[tooltip]").first();
23152             var xy = ev.getXY();
23153             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23154                 //Roo.log("not in region.");
23155                 return;
23156             }
23157             //Roo.log("child element over..");
23158             
23159         }
23160         this.currentEl = bindEl;
23161         this.currentTip.bind(bindEl);
23162         this.currentRegion = Roo.lib.Region.getRegion(dom);
23163         this.currentTip.enter();
23164         
23165     },
23166     leave : function(ev)
23167     {
23168         var dom = ev.getTarget();
23169         //Roo.log(['leave',dom]);
23170         if (!this.currentEl) {
23171             return;
23172         }
23173         
23174         
23175         if (dom != this.currentEl.dom) {
23176             return;
23177         }
23178         var xy = ev.getXY();
23179         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23180             return;
23181         }
23182         // only activate leave if mouse cursor is outside... bounding box..
23183         
23184         
23185         
23186         
23187         if (this.currentTip) {
23188             this.currentTip.leave();
23189         }
23190         //Roo.log('clear currentEl');
23191         this.currentEl = false;
23192         
23193         
23194     },
23195     alignment : {
23196         'left' : ['r-l', [-2,0], 'right'],
23197         'right' : ['l-r', [2,0], 'left'],
23198         'bottom' : ['t-b', [0,2], 'top'],
23199         'top' : [ 'b-t', [0,-2], 'bottom']
23200     }
23201     
23202 });
23203
23204
23205 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23206     
23207     
23208     bindEl : false,
23209     
23210     delay : null, // can be { show : 300 , hide: 500}
23211     
23212     timeout : null,
23213     
23214     hoverState : null, //???
23215     
23216     placement : 'bottom', 
23217     
23218     getAutoCreate : function(){
23219     
23220         var cfg = {
23221            cls : 'tooltip',
23222            role : 'tooltip',
23223            cn : [
23224                 {
23225                     cls : 'tooltip-arrow'
23226                 },
23227                 {
23228                     cls : 'tooltip-inner'
23229                 }
23230            ]
23231         };
23232         
23233         return cfg;
23234     },
23235     bind : function(el)
23236     {
23237         this.bindEl = el;
23238     },
23239       
23240     
23241     enter : function () {
23242        
23243         if (this.timeout != null) {
23244             clearTimeout(this.timeout);
23245         }
23246         
23247         this.hoverState = 'in';
23248          //Roo.log("enter - show");
23249         if (!this.delay || !this.delay.show) {
23250             this.show();
23251             return;
23252         }
23253         var _t = this;
23254         this.timeout = setTimeout(function () {
23255             if (_t.hoverState == 'in') {
23256                 _t.show();
23257             }
23258         }, this.delay.show);
23259     },
23260     leave : function()
23261     {
23262         clearTimeout(this.timeout);
23263     
23264         this.hoverState = 'out';
23265          if (!this.delay || !this.delay.hide) {
23266             this.hide();
23267             return;
23268         }
23269        
23270         var _t = this;
23271         this.timeout = setTimeout(function () {
23272             //Roo.log("leave - timeout");
23273             
23274             if (_t.hoverState == 'out') {
23275                 _t.hide();
23276                 Roo.bootstrap.Tooltip.currentEl = false;
23277             }
23278         }, delay);
23279     },
23280     
23281     show : function ()
23282     {
23283         if (!this.el) {
23284             this.render(document.body);
23285         }
23286         // set content.
23287         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23288         
23289         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23290         
23291         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23292         
23293         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23294         
23295         var placement = typeof this.placement == 'function' ?
23296             this.placement.call(this, this.el, on_el) :
23297             this.placement;
23298             
23299         var autoToken = /\s?auto?\s?/i;
23300         var autoPlace = autoToken.test(placement);
23301         if (autoPlace) {
23302             placement = placement.replace(autoToken, '') || 'top';
23303         }
23304         
23305         //this.el.detach()
23306         //this.el.setXY([0,0]);
23307         this.el.show();
23308         //this.el.dom.style.display='block';
23309         this.el.addClass(placement);
23310         
23311         //this.el.appendTo(on_el);
23312         
23313         var p = this.getPosition();
23314         var box = this.el.getBox();
23315         
23316         if (autoPlace) {
23317             // fixme..
23318         }
23319         var align = Roo.bootstrap.Tooltip.alignment[placement];
23320         this.el.alignTo(this.bindEl, align[0],align[1]);
23321         //var arrow = this.el.select('.arrow',true).first();
23322         //arrow.set(align[2], 
23323         
23324         this.el.addClass('in fade');
23325         this.hoverState = null;
23326         
23327         if (this.el.hasClass('fade')) {
23328             // fade it?
23329         }
23330         
23331     },
23332     hide : function()
23333     {
23334          
23335         if (!this.el) {
23336             return;
23337         }
23338         //this.el.setXY([0,0]);
23339         this.el.removeClass('in');
23340         //this.el.hide();
23341         
23342     }
23343     
23344 });
23345  
23346
23347  /*
23348  * - LGPL
23349  *
23350  * Location Picker
23351  * 
23352  */
23353
23354 /**
23355  * @class Roo.bootstrap.LocationPicker
23356  * @extends Roo.bootstrap.Component
23357  * Bootstrap LocationPicker class
23358  * @cfg {Number} latitude Position when init default 0
23359  * @cfg {Number} longitude Position when init default 0
23360  * @cfg {Number} zoom default 15
23361  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23362  * @cfg {Boolean} mapTypeControl default false
23363  * @cfg {Boolean} disableDoubleClickZoom default false
23364  * @cfg {Boolean} scrollwheel default true
23365  * @cfg {Boolean} streetViewControl default false
23366  * @cfg {Number} radius default 0
23367  * @cfg {String} locationName
23368  * @cfg {Boolean} draggable default true
23369  * @cfg {Boolean} enableAutocomplete default false
23370  * @cfg {Boolean} enableReverseGeocode default true
23371  * @cfg {String} markerTitle
23372  * 
23373  * @constructor
23374  * Create a new LocationPicker
23375  * @param {Object} config The config object
23376  */
23377
23378
23379 Roo.bootstrap.LocationPicker = function(config){
23380     
23381     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23382     
23383     this.addEvents({
23384         /**
23385          * @event initial
23386          * Fires when the picker initialized.
23387          * @param {Roo.bootstrap.LocationPicker} this
23388          * @param {Google Location} location
23389          */
23390         initial : true,
23391         /**
23392          * @event positionchanged
23393          * Fires when the picker position changed.
23394          * @param {Roo.bootstrap.LocationPicker} this
23395          * @param {Google Location} location
23396          */
23397         positionchanged : true,
23398         /**
23399          * @event resize
23400          * Fires when the map resize.
23401          * @param {Roo.bootstrap.LocationPicker} this
23402          */
23403         resize : true,
23404         /**
23405          * @event show
23406          * Fires when the map show.
23407          * @param {Roo.bootstrap.LocationPicker} this
23408          */
23409         show : true,
23410         /**
23411          * @event hide
23412          * Fires when the map hide.
23413          * @param {Roo.bootstrap.LocationPicker} this
23414          */
23415         hide : true,
23416         /**
23417          * @event mapClick
23418          * Fires when click the map.
23419          * @param {Roo.bootstrap.LocationPicker} this
23420          * @param {Map event} e
23421          */
23422         mapClick : true,
23423         /**
23424          * @event mapRightClick
23425          * Fires when right click the map.
23426          * @param {Roo.bootstrap.LocationPicker} this
23427          * @param {Map event} e
23428          */
23429         mapRightClick : true,
23430         /**
23431          * @event markerClick
23432          * Fires when click the marker.
23433          * @param {Roo.bootstrap.LocationPicker} this
23434          * @param {Map event} e
23435          */
23436         markerClick : true,
23437         /**
23438          * @event markerRightClick
23439          * Fires when right click the marker.
23440          * @param {Roo.bootstrap.LocationPicker} this
23441          * @param {Map event} e
23442          */
23443         markerRightClick : true,
23444         /**
23445          * @event OverlayViewDraw
23446          * Fires when OverlayView Draw
23447          * @param {Roo.bootstrap.LocationPicker} this
23448          */
23449         OverlayViewDraw : true,
23450         /**
23451          * @event OverlayViewOnAdd
23452          * Fires when OverlayView Draw
23453          * @param {Roo.bootstrap.LocationPicker} this
23454          */
23455         OverlayViewOnAdd : true,
23456         /**
23457          * @event OverlayViewOnRemove
23458          * Fires when OverlayView Draw
23459          * @param {Roo.bootstrap.LocationPicker} this
23460          */
23461         OverlayViewOnRemove : true,
23462         /**
23463          * @event OverlayViewShow
23464          * Fires when OverlayView Draw
23465          * @param {Roo.bootstrap.LocationPicker} this
23466          * @param {Pixel} cpx
23467          */
23468         OverlayViewShow : true,
23469         /**
23470          * @event OverlayViewHide
23471          * Fires when OverlayView Draw
23472          * @param {Roo.bootstrap.LocationPicker} this
23473          */
23474         OverlayViewHide : true
23475     });
23476         
23477 };
23478
23479 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23480     
23481     gMapContext: false,
23482     
23483     latitude: 0,
23484     longitude: 0,
23485     zoom: 15,
23486     mapTypeId: false,
23487     mapTypeControl: false,
23488     disableDoubleClickZoom: false,
23489     scrollwheel: true,
23490     streetViewControl: false,
23491     radius: 0,
23492     locationName: '',
23493     draggable: true,
23494     enableAutocomplete: false,
23495     enableReverseGeocode: true,
23496     markerTitle: '',
23497     
23498     getAutoCreate: function()
23499     {
23500
23501         var cfg = {
23502             tag: 'div',
23503             cls: 'roo-location-picker'
23504         };
23505         
23506         return cfg
23507     },
23508     
23509     initEvents: function(ct, position)
23510     {       
23511         if(!this.el.getWidth() || this.isApplied()){
23512             return;
23513         }
23514         
23515         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23516         
23517         this.initial();
23518     },
23519     
23520     initial: function()
23521     {
23522         if(!this.mapTypeId){
23523             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23524         }
23525         
23526         this.gMapContext = this.GMapContext();
23527         
23528         this.initOverlayView();
23529         
23530         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23531         
23532         var _this = this;
23533                 
23534         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23535             _this.setPosition(_this.gMapContext.marker.position);
23536         });
23537         
23538         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23539             _this.fireEvent('mapClick', this, event);
23540             
23541         });
23542
23543         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23544             _this.fireEvent('mapRightClick', this, event);
23545             
23546         });
23547         
23548         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23549             _this.fireEvent('markerClick', this, event);
23550             
23551         });
23552
23553         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23554             _this.fireEvent('markerRightClick', this, event);
23555             
23556         });
23557         
23558         this.setPosition(this.gMapContext.location);
23559         
23560         this.fireEvent('initial', this, this.gMapContext.location);
23561     },
23562     
23563     initOverlayView: function()
23564     {
23565         var _this = this;
23566         
23567         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23568             
23569             draw: function()
23570             {
23571                 _this.fireEvent('OverlayViewDraw', _this);
23572             },
23573             
23574             onAdd: function()
23575             {
23576                 _this.fireEvent('OverlayViewOnAdd', _this);
23577             },
23578             
23579             onRemove: function()
23580             {
23581                 _this.fireEvent('OverlayViewOnRemove', _this);
23582             },
23583             
23584             show: function(cpx)
23585             {
23586                 _this.fireEvent('OverlayViewShow', _this, cpx);
23587             },
23588             
23589             hide: function()
23590             {
23591                 _this.fireEvent('OverlayViewHide', _this);
23592             }
23593             
23594         });
23595     },
23596     
23597     fromLatLngToContainerPixel: function(event)
23598     {
23599         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23600     },
23601     
23602     isApplied: function() 
23603     {
23604         return this.getGmapContext() == false ? false : true;
23605     },
23606     
23607     getGmapContext: function() 
23608     {
23609         return this.gMapContext
23610     },
23611     
23612     GMapContext: function() 
23613     {
23614         var position = new google.maps.LatLng(this.latitude, this.longitude);
23615         
23616         var _map = new google.maps.Map(this.el.dom, {
23617             center: position,
23618             zoom: this.zoom,
23619             mapTypeId: this.mapTypeId,
23620             mapTypeControl: this.mapTypeControl,
23621             disableDoubleClickZoom: this.disableDoubleClickZoom,
23622             scrollwheel: this.scrollwheel,
23623             streetViewControl: this.streetViewControl,
23624             locationName: this.locationName,
23625             draggable: this.draggable,
23626             enableAutocomplete: this.enableAutocomplete,
23627             enableReverseGeocode: this.enableReverseGeocode
23628         });
23629         
23630         var _marker = new google.maps.Marker({
23631             position: position,
23632             map: _map,
23633             title: this.markerTitle,
23634             draggable: this.draggable
23635         });
23636         
23637         return {
23638             map: _map,
23639             marker: _marker,
23640             circle: null,
23641             location: position,
23642             radius: this.radius,
23643             locationName: this.locationName,
23644             addressComponents: {
23645                 formatted_address: null,
23646                 addressLine1: null,
23647                 addressLine2: null,
23648                 streetName: null,
23649                 streetNumber: null,
23650                 city: null,
23651                 district: null,
23652                 state: null,
23653                 stateOrProvince: null
23654             },
23655             settings: this,
23656             domContainer: this.el.dom,
23657             geodecoder: new google.maps.Geocoder()
23658         };
23659     },
23660     
23661     drawCircle: function(center, radius, options) 
23662     {
23663         if (this.gMapContext.circle != null) {
23664             this.gMapContext.circle.setMap(null);
23665         }
23666         if (radius > 0) {
23667             radius *= 1;
23668             options = Roo.apply({}, options, {
23669                 strokeColor: "#0000FF",
23670                 strokeOpacity: .35,
23671                 strokeWeight: 2,
23672                 fillColor: "#0000FF",
23673                 fillOpacity: .2
23674             });
23675             
23676             options.map = this.gMapContext.map;
23677             options.radius = radius;
23678             options.center = center;
23679             this.gMapContext.circle = new google.maps.Circle(options);
23680             return this.gMapContext.circle;
23681         }
23682         
23683         return null;
23684     },
23685     
23686     setPosition: function(location) 
23687     {
23688         this.gMapContext.location = location;
23689         this.gMapContext.marker.setPosition(location);
23690         this.gMapContext.map.panTo(location);
23691         this.drawCircle(location, this.gMapContext.radius, {});
23692         
23693         var _this = this;
23694         
23695         if (this.gMapContext.settings.enableReverseGeocode) {
23696             this.gMapContext.geodecoder.geocode({
23697                 latLng: this.gMapContext.location
23698             }, function(results, status) {
23699                 
23700                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23701                     _this.gMapContext.locationName = results[0].formatted_address;
23702                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23703                     
23704                     _this.fireEvent('positionchanged', this, location);
23705                 }
23706             });
23707             
23708             return;
23709         }
23710         
23711         this.fireEvent('positionchanged', this, location);
23712     },
23713     
23714     resize: function()
23715     {
23716         google.maps.event.trigger(this.gMapContext.map, "resize");
23717         
23718         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23719         
23720         this.fireEvent('resize', this);
23721     },
23722     
23723     setPositionByLatLng: function(latitude, longitude)
23724     {
23725         this.setPosition(new google.maps.LatLng(latitude, longitude));
23726     },
23727     
23728     getCurrentPosition: function() 
23729     {
23730         return {
23731             latitude: this.gMapContext.location.lat(),
23732             longitude: this.gMapContext.location.lng()
23733         };
23734     },
23735     
23736     getAddressName: function() 
23737     {
23738         return this.gMapContext.locationName;
23739     },
23740     
23741     getAddressComponents: function() 
23742     {
23743         return this.gMapContext.addressComponents;
23744     },
23745     
23746     address_component_from_google_geocode: function(address_components) 
23747     {
23748         var result = {};
23749         
23750         for (var i = 0; i < address_components.length; i++) {
23751             var component = address_components[i];
23752             if (component.types.indexOf("postal_code") >= 0) {
23753                 result.postalCode = component.short_name;
23754             } else if (component.types.indexOf("street_number") >= 0) {
23755                 result.streetNumber = component.short_name;
23756             } else if (component.types.indexOf("route") >= 0) {
23757                 result.streetName = component.short_name;
23758             } else if (component.types.indexOf("neighborhood") >= 0) {
23759                 result.city = component.short_name;
23760             } else if (component.types.indexOf("locality") >= 0) {
23761                 result.city = component.short_name;
23762             } else if (component.types.indexOf("sublocality") >= 0) {
23763                 result.district = component.short_name;
23764             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23765                 result.stateOrProvince = component.short_name;
23766             } else if (component.types.indexOf("country") >= 0) {
23767                 result.country = component.short_name;
23768             }
23769         }
23770         
23771         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23772         result.addressLine2 = "";
23773         return result;
23774     },
23775     
23776     setZoomLevel: function(zoom)
23777     {
23778         this.gMapContext.map.setZoom(zoom);
23779     },
23780     
23781     show: function()
23782     {
23783         if(!this.el){
23784             return;
23785         }
23786         
23787         this.el.show();
23788         
23789         this.resize();
23790         
23791         this.fireEvent('show', this);
23792     },
23793     
23794     hide: function()
23795     {
23796         if(!this.el){
23797             return;
23798         }
23799         
23800         this.el.hide();
23801         
23802         this.fireEvent('hide', this);
23803     }
23804     
23805 });
23806
23807 Roo.apply(Roo.bootstrap.LocationPicker, {
23808     
23809     OverlayView : function(map, options)
23810     {
23811         options = options || {};
23812         
23813         this.setMap(map);
23814     }
23815     
23816     
23817 });/*
23818  * - LGPL
23819  *
23820  * Alert
23821  * 
23822  */
23823
23824 /**
23825  * @class Roo.bootstrap.Alert
23826  * @extends Roo.bootstrap.Component
23827  * Bootstrap Alert class
23828  * @cfg {String} title The title of alert
23829  * @cfg {String} html The content of alert
23830  * @cfg {String} weight (  success | info | warning | danger )
23831  * @cfg {String} faicon font-awesomeicon
23832  * 
23833  * @constructor
23834  * Create a new alert
23835  * @param {Object} config The config object
23836  */
23837
23838
23839 Roo.bootstrap.Alert = function(config){
23840     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23841     
23842 };
23843
23844 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23845     
23846     title: '',
23847     html: '',
23848     weight: false,
23849     faicon: false,
23850     
23851     getAutoCreate : function()
23852     {
23853         
23854         var cfg = {
23855             tag : 'div',
23856             cls : 'alert',
23857             cn : [
23858                 {
23859                     tag : 'i',
23860                     cls : 'roo-alert-icon'
23861                     
23862                 },
23863                 {
23864                     tag : 'b',
23865                     cls : 'roo-alert-title',
23866                     html : this.title
23867                 },
23868                 {
23869                     tag : 'span',
23870                     cls : 'roo-alert-text',
23871                     html : this.html
23872                 }
23873             ]
23874         };
23875         
23876         if(this.faicon){
23877             cfg.cn[0].cls += ' fa ' + this.faicon;
23878         }
23879         
23880         if(this.weight){
23881             cfg.cls += ' alert-' + this.weight;
23882         }
23883         
23884         return cfg;
23885     },
23886     
23887     initEvents: function() 
23888     {
23889         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23890     },
23891     
23892     setTitle : function(str)
23893     {
23894         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23895     },
23896     
23897     setText : function(str)
23898     {
23899         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23900     },
23901     
23902     setWeight : function(weight)
23903     {
23904         if(this.weight){
23905             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23906         }
23907         
23908         this.weight = weight;
23909         
23910         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23911     },
23912     
23913     setIcon : function(icon)
23914     {
23915         if(this.faicon){
23916             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23917         }
23918         
23919         this.faicon = icon
23920         
23921         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23922     },
23923     
23924     hide: function() 
23925     {
23926         this.el.hide();   
23927     },
23928     
23929     show: function() 
23930     {  
23931         this.el.show();   
23932     }
23933     
23934 });
23935
23936  
23937 /*
23938 * Licence: LGPL
23939 */
23940
23941 /**
23942  * @class Roo.bootstrap.UploadCropbox
23943  * @extends Roo.bootstrap.Component
23944  * Bootstrap UploadCropbox class
23945  * @cfg {String} emptyText show when image has been loaded
23946  * @cfg {Number} minWidth default 300
23947  * @cfg {Number} minHeight default 300
23948  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23949  * 
23950  * @constructor
23951  * Create a new UploadCropbox
23952  * @param {Object} config The config object
23953  */
23954
23955 Roo.bootstrap.UploadCropbox = function(config){
23956     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23957     
23958     this.addEvents({
23959         /**
23960          * @event beforeselectfile
23961          * Fire before select file
23962          * @param {Roo.bootstrap.UploadCropbox} this
23963          */
23964         "beforeselectfile" : true,
23965         /**
23966          * @event initial
23967          * Fire after initEvent
23968          * @param {Roo.bootstrap.UploadCropbox} this
23969          */
23970         "initial" : true,
23971         /**
23972          * @event crop
23973          * Fire after initEvent
23974          * @param {Roo.bootstrap.UploadCropbox} this
23975          * @param {String} data
23976          */
23977         "crop" : true,
23978         /**
23979          * @event prepare
23980          * Fire when preparing the file data
23981          * @param {Roo.bootstrap.UploadCropbox} this
23982          * @param {Object} file
23983          */
23984         "prepare" : true,
23985         /**
23986          * @event exception
23987          * Fire when get exception
23988          * @param {Roo.bootstrap.UploadCropbox} this
23989          * @param {Object} options
23990          */
23991         "exception" : true,
23992         /**
23993          * @event beforeloadcanvas
23994          * Fire before load the canvas
23995          * @param {Roo.bootstrap.UploadCropbox} this
23996          * @param {String} src
23997          */
23998         "beforeloadcanvas" : true,
23999         /**
24000          * @event trash
24001          * Fire when trash image
24002          * @param {Roo.bootstrap.UploadCropbox} this
24003          */
24004         "trash" : true,
24005         /**
24006          * @event download
24007          * Fire when download the image
24008          * @param {Roo.bootstrap.UploadCropbox} this
24009          */
24010         "download" : true,
24011         /**
24012          * @event footerbuttonclick
24013          * Fire when footerbuttonclick
24014          * @param {Roo.bootstrap.UploadCropbox} this
24015          * @param {String} type
24016          */
24017         "footerbuttonclick" : true
24018         
24019     });
24020     
24021     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24022 };
24023
24024 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24025     
24026     emptyText : 'Click to upload image',
24027     scale : 0,
24028     baseScale : 1,
24029     rotate : 0,
24030     dragable : false,
24031     pinching : false,
24032     mouseX : 0,
24033     mouseY : 0,
24034     cropData : false,
24035     minWidth : 300,
24036     minHeight : 300,
24037     file : false,
24038     exif : {},
24039     baseRotate : 1,
24040     cropType : 'image/jpeg',
24041     buttons : false,
24042     
24043     getAutoCreate : function()
24044     {
24045         var cfg = {
24046             tag : 'div',
24047             cls : 'roo-upload-cropbox',
24048             cn : [
24049                 {
24050                     tag : 'div',
24051                     cls : 'roo-upload-cropbox-body',
24052                     cn : [
24053                         {
24054                             tag : 'div',
24055                             cls : 'roo-upload-cropbox-preview'
24056                         },
24057                         {
24058                             tag : 'div',
24059                             cls : 'roo-upload-cropbox-thumb'
24060                         },
24061                         {
24062                             tag : 'div',
24063                             cls : 'roo-upload-cropbox-empty-notify',
24064                             html : this.emptyText
24065                         }
24066                     ]
24067                 },
24068                 {
24069                     tag : 'div',
24070                     cls : 'roo-upload-cropbox-footer',
24071                     cn : {
24072                         tag : 'div',
24073                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24074                         cn : []
24075                     }
24076                 }
24077             ]
24078         };
24079         
24080         return cfg;
24081     },
24082     
24083     onRender : function(ct, position)
24084     {
24085         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24086         
24087         if (this.buttons.length) {
24088             
24089             Roo.each(this.buttons, function(bb) {
24090                 
24091                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24092                 
24093                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24094                 
24095             }, this);
24096         }
24097     },
24098     
24099     initEvents : function()
24100     {
24101         this.urlAPI = (window.createObjectURL && window) || 
24102                                 (window.URL && URL.revokeObjectURL && URL) || 
24103                                 (window.webkitURL && webkitURL);
24104                         
24105         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24106         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24107         this.bodyHasOnClickEvent = false;
24108         
24109         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24110         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24111         
24112         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24113         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24114         this.thumbEl.hide();
24115         
24116         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24117         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24118         
24119         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24120         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24121         this.footerEl.hide();
24122         
24123         this.setThumbBoxSize();
24124         
24125         this.bind();
24126         
24127         this.fireEvent('initial', this);
24128     },
24129
24130     bind : function()
24131     {
24132         var _this = this;
24133         
24134         window.addEventListener("resize", function() { _this.resize(); } );
24135         
24136         if(!this.bodyHasOnClickEvent){
24137             this.bodyEl.on('click', this.beforeSelectFile, this);
24138             this.bodyHasOnClickEvent = true;
24139         }
24140         
24141         if(Roo.isTouch){
24142             this.bodyEl.on('touchstart', this.onTouchStart, this);
24143             this.bodyEl.on('touchmove', this.onTouchMove, this);
24144             this.bodyEl.on('touchend', this.onTouchEnd, this);
24145         }
24146         
24147         if(!Roo.isTouch){
24148             this.bodyEl.on('mousedown', this.onMouseDown, this);
24149             this.bodyEl.on('mousemove', this.onMouseMove, this);
24150             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24151             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24152             Roo.get(document).on('mouseup', this.onMouseUp, this);
24153         }
24154     },
24155     
24156     reset : function()
24157     {    
24158         this.scale = 0;
24159         this.baseScale = 1;
24160         this.rotate = 0;
24161         this.baseRotate = 1;
24162         this.dragable = false;
24163         this.pinching = false;
24164         this.mouseX = 0;
24165         this.mouseY = 0;
24166         this.cropData = false;
24167         
24168     },
24169     
24170     resize : function()
24171     {
24172         this.setThumbBoxPosition();
24173         this.setCanvasPosition();
24174     },
24175     
24176     onFooterButtonClick : function(e, el, o, type)
24177     {
24178         switch (type) {
24179             case 'rotate-left' :
24180                 this.onRotateLeft(e);
24181                 break;
24182             case 'rotate-right' :
24183                 this.onRotateRight(e);
24184                 break;
24185             case 'picture' :
24186                 this.beforeSelectFile(e);
24187                 break;
24188             case 'trash' :
24189                 this.trash(e);
24190                 break;
24191             case 'crop' :
24192                 this.crop(e);
24193                 break;
24194             case 'download' :
24195                 this.download(e);
24196                 break;
24197             default :
24198                 break;
24199         }
24200         
24201         this.fireEvent('footerbuttonclick', this, type);
24202     },
24203     
24204     beforeSelectFile : function(e)
24205     {
24206         if(e){
24207             e.preventDefault();
24208         }
24209         
24210         this.fireEvent('beforeselectfile', this);
24211     },
24212     
24213     trash : function(e)
24214     {
24215         if(e){
24216             e.preventDefault();
24217         }
24218         
24219         this.fireEvent('trash', this);
24220     },
24221     
24222     download : function(e)
24223     {
24224         if(e){
24225             e.preventDefault();
24226         }
24227         
24228         this.fireEvent('download', this);
24229     },
24230     
24231     loadCanvas : function(src)
24232     {   
24233         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24234             
24235             this.reset();
24236             
24237             this.imageEl = document.createElement('img');
24238             
24239             var _this = this;
24240             
24241             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24242             
24243             this.imageEl.src = src;
24244         }
24245     },
24246     
24247     onLoadCanvas : function()
24248     {   
24249         if(this.bodyHasOnClickEvent){
24250             this.bodyEl.un('click', this.beforeSelectFile, this);
24251             this.bodyHasOnClickEvent = false;
24252         }
24253         
24254         this.notifyEl.hide();
24255         this.thumbEl.show();
24256         this.footerEl.show();
24257         
24258         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24259         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24260         
24261         this.setThumbBoxPosition();
24262         this.baseRotateLevel();
24263         this.baseScaleLevel();
24264         
24265         this.draw();
24266         
24267     },
24268     
24269     setCanvasPosition : function()
24270     {   
24271         if(!this.canvasEl){
24272             return;
24273         }
24274         
24275         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24276         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24277         
24278         this.previewEl.setLeft(pw);
24279         this.previewEl.setTop(ph);
24280     },
24281     
24282     onMouseDown : function(e)
24283     {   
24284         e.stopEvent();
24285         
24286         this.dragable = true;
24287         this.pinching = false;
24288         
24289         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24290         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24291         
24292     },
24293     
24294     onMouseMove : function(e)
24295     {   
24296         e.stopEvent();
24297         
24298         if (!this.dragable){
24299             return;
24300         }
24301         
24302         var minX = Math.ceil(this.thumbEl.getLeft(true));
24303         var minY = Math.ceil(this.thumbEl.getTop(true));
24304         
24305         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24306         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24307         
24308         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24309         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24310         
24311         x = x - this.mouseX;
24312         y = y - this.mouseY;
24313         
24314         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24315         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24316         
24317         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24318         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24319         
24320         this.previewEl.setLeft(bgX);
24321         this.previewEl.setTop(bgY);
24322         
24323         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24324         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24325     },
24326     
24327     onMouseUp : function(e)
24328     {   
24329         e.stopEvent();
24330         
24331         this.dragable = false;
24332     },
24333     
24334     onMouseWheel : function(e)
24335     {   
24336         e.stopEvent();
24337         
24338         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24339         
24340         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24341         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24342         
24343         if(
24344                 e.getWheelDelta() == -1 &&
24345                 (
24346                     (
24347                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24348                     )
24349                     ||
24350                     (
24351                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24352                     )
24353                 )
24354         ){
24355             this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
24356             return;
24357         }
24358         
24359         this.draw();
24360     },
24361     
24362     onRotateLeft : function(e)
24363     {
24364         if(e){
24365             e.preventDefault();
24366         }
24367         
24368         if(
24369                 (
24370                     (this.rotate == 0 || this.rotate == 180) 
24371                     &&
24372                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24373                 )
24374                 ||
24375                 (
24376                     (this.rotate == 90 || this.rotate == 270) 
24377                     &&
24378                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24379                 )
24380                 
24381         ){
24382             return;
24383         }
24384         
24385         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24386
24387         this.draw();
24388         
24389     },
24390     
24391     onRotateRight : function(e)
24392     {
24393         if(e){
24394             e.preventDefault();
24395         }
24396         
24397         if(
24398                 (
24399                     (this.rotate == 0 || this.rotate == 180) 
24400                     &&
24401                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24402                 )
24403                 ||
24404                 (
24405                     (this.rotate == 90 || this.rotate == 270) 
24406                     &&
24407                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24408                 )
24409                 
24410         ){
24411             return false;
24412         }
24413         
24414         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24415
24416         this.draw();
24417         
24418     },
24419     
24420     draw : function()
24421     {
24422         this.previewEl.dom.innerHTML = '';
24423         
24424         var canvasEl = document.createElement("canvas");
24425         
24426         var contextEl = canvasEl.getContext("2d");
24427         
24428         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24429         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24430         var center = this.imageEl.OriginWidth / 2;
24431         
24432         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24433             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24434             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24435             center = this.imageEl.OriginHeight / 2;
24436         }
24437         
24438         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24439         
24440         contextEl.translate(center, center);
24441         contextEl.rotate(this.rotate * Math.PI / 180);
24442
24443         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24444         
24445         this.canvasEl = document.createElement("canvas");
24446         
24447         this.contextEl = this.canvasEl.getContext("2d");
24448         
24449         switch (this.rotate) {
24450             case 0 :
24451                 
24452                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24453                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24454                 
24455                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24456                 
24457                 break;
24458             case 90 : 
24459                 
24460                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24461                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24462                 
24463                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24464                     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);
24465                     break;
24466                 }
24467                 
24468                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24469                 
24470                 break;
24471             case 180 :
24472                 
24473                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24474                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24475                 
24476                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24477                     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);
24478                     break;
24479                 }
24480                 
24481                 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);
24482                 
24483                 break;
24484             case 270 :
24485                 
24486                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24487                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24488         
24489                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24490                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24491                     break;
24492                 }
24493                 
24494                 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);
24495                 
24496                 break;
24497             default : 
24498                 break;
24499         }
24500         
24501         this.previewEl.appendChild(this.canvasEl);
24502         
24503         this.setCanvasPosition();
24504     },
24505     
24506     crop : function()
24507     {
24508         var canvas = document.createElement("canvas");
24509         
24510         var context = canvas.getContext("2d");
24511         
24512         canvas.width = this.minWidth;
24513         canvas.height = this.minHeight;
24514         
24515         var cropWidth = this.thumbEl.getWidth();
24516         var cropHeight = this.thumbEl.getHeight();
24517         
24518         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24519         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24520         
24521         if(this.canvasEl.width - cropWidth < x){
24522             x = this.canvasEl.width - cropWidth;
24523         }
24524         
24525         if(this.canvasEl.height - cropHeight < y){
24526             y = this.canvasEl.height - cropHeight;
24527         }
24528         
24529         x = x < 0 ? 0 : x;
24530         y = y < 0 ? 0 : y;
24531         
24532         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24533         
24534         this.cropData = canvas.toDataURL(this.cropType);
24535         
24536         this.fireEvent('crop', this, this.cropData);
24537         
24538     },
24539     
24540     setThumbBoxSize : function()
24541     {
24542         var height = 300;
24543         var width = Math.ceil(this.minWidth * height / this.minHeight);
24544         
24545         if(this.minWidth > this.minHeight){
24546             width = 300;
24547             height = Math.ceil(this.minHeight * width / this.minWidth);
24548         }
24549         
24550         this.thumbEl.setStyle({
24551             width : width + 'px',
24552             height : height + 'px'
24553         });
24554
24555         return;
24556             
24557     },
24558     
24559     setThumbBoxPosition : function()
24560     {
24561         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24562         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24563         
24564         this.thumbEl.setLeft(x);
24565         this.thumbEl.setTop(y);
24566         
24567     },
24568     
24569     baseRotateLevel : function()
24570     {
24571         this.baseRotate = 1;
24572         
24573         if(
24574                 typeof(this.exif) != 'undefined' &&
24575                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24576                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24577         ){
24578             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24579         }
24580         
24581         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24582         
24583     },
24584     
24585     baseScaleLevel : function()
24586     {
24587         var width, height;
24588         
24589         if(this.baseRotate == 6 || this.baseRotate == 8){
24590             
24591             width = this.thumbEl.getHeight();
24592             this.baseScale = height / this.imageEl.OriginHeight;
24593             
24594             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24595                 height = this.thumbEl.getWidth();
24596                 this.baseScale = height / this.imageEl.OriginHeight;
24597             }
24598             
24599             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24600                 height = this.thumbEl.getWidth();
24601                 this.baseScale = height / this.imageEl.OriginHeight;
24602                 
24603                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24604                     width = this.thumbEl.getHeight();
24605                     this.baseScale = width / this.imageEl.OriginWidth;
24606                 }
24607             }
24608             
24609             return;
24610         }
24611         
24612         width = this.thumbEl.getWidth();
24613         this.baseScale = width / this.imageEl.OriginWidth;
24614         
24615         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24616             height = this.thumbEl.getHeight();
24617             this.baseScale = height / this.imageEl.OriginHeight;
24618         }
24619         
24620         
24621         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24622             
24623             height = this.thumbEl.getHeight();
24624             this.baseScale = height / this.imageEl.OriginHeight;
24625             
24626             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24627                 width = this.thumbEl.getWidth();
24628                 this.baseScale = width / this.imageEl.OriginWidth;
24629             }
24630             
24631         }
24632         
24633         return;
24634     },
24635     
24636     getScaleLevel : function()
24637     {
24638         return this.baseScale * Math.pow(1.1, this.scale);
24639     },
24640     
24641     onTouchStart : function(e)
24642     {
24643         e.stopEvent();
24644         
24645         var touches = e.browserEvent.touches;
24646         
24647         if(!touches){
24648             return;
24649         }
24650         
24651         if(touches.length == 1){
24652             this.onMouseDown(e);
24653             return;
24654         }
24655         
24656         if(touches.length != 2){
24657             return;
24658         }
24659         
24660         var coords = [];
24661         
24662         for(var i = 0, finger; finger = touches[i]; i++){
24663             coords.push(finger.pageX, finger.pageY);
24664         }
24665         
24666         var x = Math.pow(coords[0] - coords[2], 2);
24667         var y = Math.pow(coords[1] - coords[3], 2);
24668         
24669         this.startDistance = Math.sqrt(x + y);
24670         
24671         this.startScale = this.scale;
24672         
24673         this.pinching = true;
24674         this.dragable = false;
24675         
24676     },
24677     
24678     onTouchMove : function(e)
24679     {
24680         e.stopEvent();
24681         
24682         if(!this.pinching && !this.dragable){
24683             return;
24684         }
24685         
24686         var touches = e.browserEvent.touches;
24687         
24688         if(!touches){
24689             return;
24690         }
24691         
24692         if(this.dragable){
24693             this.onMouseMove(e);
24694             return;
24695         }
24696         
24697         var coords = [];
24698         
24699         for(var i = 0, finger; finger = touches[i]; i++){
24700             coords.push(finger.pageX, finger.pageY);
24701         }
24702         
24703         var x = Math.pow(coords[0] - coords[2], 2);
24704         var y = Math.pow(coords[1] - coords[3], 2);
24705         
24706         this.endDistance = Math.sqrt(x + y);
24707         
24708         var scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24709         
24710         var width = Math.ceil(this.imageEl.OriginWidth * this.baseScale * Math.pow(1.1, scale));
24711         var height = Math.ceil(this.imageEl.OriginHeight * this.baseScale * Math.pow(1.1, scale));
24712         
24713         if(
24714                 this.endDistance / this.startDistance < 1 &&
24715                 (
24716                     (
24717                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24718                     )
24719                     ||
24720                     (
24721                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24722                     )
24723                 )
24724         ){
24725             return;
24726         }
24727         
24728         this.scale = scale;
24729         
24730         this.draw();
24731         
24732     },
24733     
24734     onTouchEnd : function(e)
24735     {
24736         e.stopEvent();
24737         
24738         this.pinching = false;
24739         this.dragable = false;
24740         
24741     },
24742     
24743     prepare : function(input)
24744     {        
24745         this.file = false;
24746         this.exif = {};
24747         
24748         if(typeof(input) === 'string'){
24749             this.loadCanvas(input);
24750             return;
24751         }
24752         
24753         if(!input.files || !input.files[0] || !this.urlAPI){
24754             return;
24755         }
24756         
24757         this.file = input.files[0];
24758         this.cropType = this.file.type;
24759         
24760         var _this = this;
24761         
24762         if(this.fireEvent('prepare', this, this.file) != false){
24763             
24764             var reader = new FileReader();
24765             
24766             reader.onload = function (e) {
24767                 if (e.target.error) {
24768                     Roo.log(e.target.error);
24769                     return;
24770                 }
24771                 
24772                 var buffer = e.target.result,
24773                     dataView = new DataView(buffer),
24774                     offset = 2,
24775                     maxOffset = dataView.byteLength - 4,
24776                     markerBytes,
24777                     markerLength;
24778                 
24779                 if (dataView.getUint16(0) === 0xffd8) {
24780                     while (offset < maxOffset) {
24781                         markerBytes = dataView.getUint16(offset);
24782                         
24783                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24784                             markerLength = dataView.getUint16(offset + 2) + 2;
24785                             if (offset + markerLength > dataView.byteLength) {
24786                                 Roo.log('Invalid meta data: Invalid segment size.');
24787                                 break;
24788                             }
24789                             
24790                             if(markerBytes == 0xffe1){
24791                                 _this.parseExifData(
24792                                     dataView,
24793                                     offset,
24794                                     markerLength
24795                                 );
24796                             }
24797                             
24798                             offset += markerLength;
24799                             
24800                             continue;
24801                         }
24802                         
24803                         break;
24804                     }
24805                     
24806                 }
24807                 
24808                 var url = _this.urlAPI.createObjectURL(_this.file);
24809                 
24810                 _this.loadCanvas(url);
24811                 
24812                 return;
24813             }
24814             
24815             reader.readAsArrayBuffer(this.file);
24816             
24817         }
24818         
24819     },
24820     
24821     parseExifData : function(dataView, offset, length)
24822     {
24823         var tiffOffset = offset + 10,
24824             littleEndian,
24825             dirOffset;
24826     
24827         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24828             // No Exif data, might be XMP data instead
24829             return;
24830         }
24831         
24832         // Check for the ASCII code for "Exif" (0x45786966):
24833         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24834             // No Exif data, might be XMP data instead
24835             return;
24836         }
24837         if (tiffOffset + 8 > dataView.byteLength) {
24838             Roo.log('Invalid Exif data: Invalid segment size.');
24839             return;
24840         }
24841         // Check for the two null bytes:
24842         if (dataView.getUint16(offset + 8) !== 0x0000) {
24843             Roo.log('Invalid Exif data: Missing byte alignment offset.');
24844             return;
24845         }
24846         // Check the byte alignment:
24847         switch (dataView.getUint16(tiffOffset)) {
24848         case 0x4949:
24849             littleEndian = true;
24850             break;
24851         case 0x4D4D:
24852             littleEndian = false;
24853             break;
24854         default:
24855             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24856             return;
24857         }
24858         // Check for the TIFF tag marker (0x002A):
24859         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24860             Roo.log('Invalid Exif data: Missing TIFF marker.');
24861             return;
24862         }
24863         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24864         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24865         
24866         this.parseExifTags(
24867             dataView,
24868             tiffOffset,
24869             tiffOffset + dirOffset,
24870             littleEndian
24871         );
24872     },
24873     
24874     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24875     {
24876         var tagsNumber,
24877             dirEndOffset,
24878             i;
24879         if (dirOffset + 6 > dataView.byteLength) {
24880             Roo.log('Invalid Exif data: Invalid directory offset.');
24881             return;
24882         }
24883         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24884         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24885         if (dirEndOffset + 4 > dataView.byteLength) {
24886             Roo.log('Invalid Exif data: Invalid directory size.');
24887             return;
24888         }
24889         for (i = 0; i < tagsNumber; i += 1) {
24890             this.parseExifTag(
24891                 dataView,
24892                 tiffOffset,
24893                 dirOffset + 2 + 12 * i, // tag offset
24894                 littleEndian
24895             );
24896         }
24897         // Return the offset to the next directory:
24898         return dataView.getUint32(dirEndOffset, littleEndian);
24899     },
24900     
24901     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
24902     {
24903         var tag = dataView.getUint16(offset, littleEndian);
24904         
24905         this.exif[tag] = this.getExifValue(
24906             dataView,
24907             tiffOffset,
24908             offset,
24909             dataView.getUint16(offset + 2, littleEndian), // tag type
24910             dataView.getUint32(offset + 4, littleEndian), // tag length
24911             littleEndian
24912         );
24913     },
24914     
24915     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24916     {
24917         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
24918             tagSize,
24919             dataOffset,
24920             values,
24921             i,
24922             str,
24923             c;
24924     
24925         if (!tagType) {
24926             Roo.log('Invalid Exif data: Invalid tag type.');
24927             return;
24928         }
24929         
24930         tagSize = tagType.size * length;
24931         // Determine if the value is contained in the dataOffset bytes,
24932         // or if the value at the dataOffset is a pointer to the actual data:
24933         dataOffset = tagSize > 4 ?
24934                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
24935         if (dataOffset + tagSize > dataView.byteLength) {
24936             Roo.log('Invalid Exif data: Invalid data offset.');
24937             return;
24938         }
24939         if (length === 1) {
24940             return tagType.getValue(dataView, dataOffset, littleEndian);
24941         }
24942         values = [];
24943         for (i = 0; i < length; i += 1) {
24944             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
24945         }
24946         
24947         if (tagType.ascii) {
24948             str = '';
24949             // Concatenate the chars:
24950             for (i = 0; i < values.length; i += 1) {
24951                 c = values[i];
24952                 // Ignore the terminating NULL byte(s):
24953                 if (c === '\u0000') {
24954                     break;
24955                 }
24956                 str += c;
24957             }
24958             return str;
24959         }
24960         return values;
24961     }
24962     
24963 });
24964
24965 Roo.apply(Roo.bootstrap.UploadCropbox, {
24966     tags : {
24967         'Orientation': 0x0112
24968     },
24969     
24970     Orientation: {
24971             1: 0, //'top-left',
24972 //            2: 'top-right',
24973             3: 180, //'bottom-right',
24974 //            4: 'bottom-left',
24975 //            5: 'left-top',
24976             6: 90, //'right-top',
24977 //            7: 'right-bottom',
24978             8: 270 //'left-bottom'
24979     },
24980     
24981     exifTagTypes : {
24982         // byte, 8-bit unsigned int:
24983         1: {
24984             getValue: function (dataView, dataOffset) {
24985                 return dataView.getUint8(dataOffset);
24986             },
24987             size: 1
24988         },
24989         // ascii, 8-bit byte:
24990         2: {
24991             getValue: function (dataView, dataOffset) {
24992                 return String.fromCharCode(dataView.getUint8(dataOffset));
24993             },
24994             size: 1,
24995             ascii: true
24996         },
24997         // short, 16 bit int:
24998         3: {
24999             getValue: function (dataView, dataOffset, littleEndian) {
25000                 return dataView.getUint16(dataOffset, littleEndian);
25001             },
25002             size: 2
25003         },
25004         // long, 32 bit int:
25005         4: {
25006             getValue: function (dataView, dataOffset, littleEndian) {
25007                 return dataView.getUint32(dataOffset, littleEndian);
25008             },
25009             size: 4
25010         },
25011         // rational = two long values, first is numerator, second is denominator:
25012         5: {
25013             getValue: function (dataView, dataOffset, littleEndian) {
25014                 return dataView.getUint32(dataOffset, littleEndian) /
25015                     dataView.getUint32(dataOffset + 4, littleEndian);
25016             },
25017             size: 8
25018         },
25019         // slong, 32 bit signed int:
25020         9: {
25021             getValue: function (dataView, dataOffset, littleEndian) {
25022                 return dataView.getInt32(dataOffset, littleEndian);
25023             },
25024             size: 4
25025         },
25026         // srational, two slongs, first is numerator, second is denominator:
25027         10: {
25028             getValue: function (dataView, dataOffset, littleEndian) {
25029                 return dataView.getInt32(dataOffset, littleEndian) /
25030                     dataView.getInt32(dataOffset + 4, littleEndian);
25031             },
25032             size: 8
25033         }
25034     },
25035     
25036     footer : {
25037         STANDARD : [
25038             {
25039                 tag : 'div',
25040                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25041                 action : 'rotate-left',
25042                 cn : [
25043                     {
25044                         tag : 'button',
25045                         cls : 'btn btn-default',
25046                         html : '<i class="fa fa-undo"></i>'
25047                     }
25048                 ]
25049             },
25050             {
25051                 tag : 'div',
25052                 cls : 'btn-group roo-upload-cropbox-picture',
25053                 action : 'picture',
25054                 cn : [
25055                     {
25056                         tag : 'button',
25057                         cls : 'btn btn-default',
25058                         html : '<i class="fa fa-picture-o"></i>'
25059                     }
25060                 ]
25061             },
25062             {
25063                 tag : 'div',
25064                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25065                 action : 'rotate-right',
25066                 cn : [
25067                     {
25068                         tag : 'button',
25069                         cls : 'btn btn-default',
25070                         html : '<i class="fa fa-repeat"></i>'
25071                     }
25072                 ]
25073             }
25074         ],
25075         DOCUMENT : [
25076             {
25077                 tag : 'div',
25078                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25079                 action : 'rotate-left',
25080                 cn : [
25081                     {
25082                         tag : 'button',
25083                         cls : 'btn btn-default',
25084                         html : '<i class="fa fa-undo"></i>'
25085                     }
25086                 ]
25087             },
25088             {
25089                 tag : 'div',
25090                 cls : 'btn-group roo-upload-cropbox-download',
25091                 action : 'download',
25092                 cn : [
25093                     {
25094                         tag : 'button',
25095                         cls : 'btn btn-default',
25096                         html : '<i class="fa fa-download"></i>'
25097                     }
25098                 ]
25099             },
25100             {
25101                 tag : 'div',
25102                 cls : 'btn-group roo-upload-cropbox-crop',
25103                 action : 'crop',
25104                 cn : [
25105                     {
25106                         tag : 'button',
25107                         cls : 'btn btn-default',
25108                         html : '<i class="fa fa-crop"></i>'
25109                     }
25110                 ]
25111             },
25112             {
25113                 tag : 'div',
25114                 cls : 'btn-group roo-upload-cropbox-trash',
25115                 action : 'trash',
25116                 cn : [
25117                     {
25118                         tag : 'button',
25119                         cls : 'btn btn-default',
25120                         html : '<i class="fa fa-trash"></i>'
25121                     }
25122                 ]
25123             },
25124             {
25125                 tag : 'div',
25126                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25127                 action : 'rotate-right',
25128                 cn : [
25129                     {
25130                         tag : 'button',
25131                         cls : 'btn btn-default',
25132                         html : '<i class="fa fa-repeat"></i>'
25133                     }
25134                 ]
25135             }
25136         ]
25137     }
25138 });
25139
25140 /*
25141 * Licence: LGPL
25142 */
25143
25144 /**
25145  * @class Roo.bootstrap.DocumentManager
25146  * @extends Roo.bootstrap.Component
25147  * Bootstrap DocumentManager class
25148  * @cfg {String} paramName default 'imageUpload'
25149  * @cfg {String} method default POST
25150  * @cfg {String} url action url
25151  * @cfg {Number} boxes number of boxes to show default 12
25152  * @cfg {Boolean} multiple multiple upload default true
25153  * @cfg {Number} minWidth default 300
25154  * @cfg {Number} minHeight default 300
25155  * @cfg {Number} thumbSize default 300
25156  * 
25157  * @constructor
25158  * Create a new DocumentManager
25159  * @param {Object} config The config object
25160  */
25161
25162 Roo.bootstrap.DocumentManager = function(config){
25163     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25164     
25165     this.addEvents({
25166         /**
25167          * @event inspect
25168          * inspect selected file
25169          * @param {Roo.bootstrap.DocumentManager} this
25170          * @param {File} file
25171          */
25172         "inspect" : true,
25173         /**
25174          * @event exception
25175          * Fire when xhr load exception
25176          * @param {Roo.bootstrap.DocumentManager} this
25177          * @param {XMLHttpRequest} xhr
25178          */
25179         "exception" : true,
25180         /**
25181          * @event prepare
25182          * prepare the form data
25183          * @param {Roo.bootstrap.DocumentManager} this
25184          * @param {Object} formData
25185          */
25186         "prepare" : true,
25187         /**
25188          * @event remove
25189          * Fire when remove the file
25190          * @param {Roo.bootstrap.DocumentManager} this
25191          * @param {Object} file
25192          */
25193         "remove" : true,
25194         /**
25195          * @event refresh
25196          * Fire after refresh the file
25197          * @param {Roo.bootstrap.DocumentManager} this
25198          */
25199         "refresh" : true,
25200         /**
25201          * @event click
25202          * Fire after click the image
25203          * @param {Roo.bootstrap.DocumentManager} this
25204          * @param {Object} file
25205          */
25206         "click" : true
25207         
25208     });
25209 };
25210
25211 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25212     
25213     boxes : 12,
25214     inputName : '',
25215     minWidth : 300,
25216     minHeight : 300,
25217     thumbSize : 300,
25218     multiple : true,
25219     files : [],
25220     method : 'POST',
25221     url : '',
25222     paramName : 'imageUpload',
25223     
25224     getAutoCreate : function()
25225     {
25226         var cfg = {
25227             tag : 'div',
25228             cls : 'roo-document-manager',
25229             cn : [
25230                 {
25231                     tag : 'input',
25232                     cls : 'roo-document-manager-selector',
25233                     type : 'file'
25234                 },
25235                 {
25236                     tag : 'div',
25237                     cls : 'roo-document-manager-uploader',
25238                     cn : [
25239                         {
25240                             tag : 'div',
25241                             cls : 'roo-document-manager-upload-btn',
25242                             html : '<i class="fa fa-plus"></i>'
25243                         }
25244                     ]
25245                     
25246                 }
25247             ]
25248         };
25249         
25250         return cfg;
25251         
25252     },
25253     
25254     initEvents : function()
25255     {
25256         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25257         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25258         this.selectorEl.hide();
25259         
25260         if(this.multiple){
25261             this.selectorEl.attr('multiple', 'multiple');
25262         }
25263         
25264         this.selectorEl.on('change', this.onSelect, this);
25265         
25266         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25267         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25268         
25269         this.uploader.on('click', this.onUpload, this);
25270         
25271         var _this = this;
25272         
25273         window.addEventListener("resize", function() { _this.refresh(); } );
25274         
25275     },
25276     
25277     onUpload : function(e)
25278     {
25279         e.preventDefault();
25280         
25281         this.selectorEl.dom.click();
25282         
25283     },
25284     
25285     onSelect : function(e)
25286     {
25287         e.preventDefault();
25288         
25289         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25290             return;
25291         }
25292         
25293         Roo.each(this.selectorEl.dom.files, function(file){
25294             if(this.fireEvent('inspect', this, file) != false){
25295                 this.files.push(file);
25296             }
25297         }, this);
25298         
25299         this.process();
25300         
25301     },
25302     
25303     process : function()
25304     {
25305         this.selectorEl.dom.value = '';
25306         
25307         if(!this.files.length){
25308             return;
25309         }
25310         
25311         if(this.files.length > 12){
25312             this.files = this.files.slice(0, 12);
25313         }
25314         
25315         var xhr = new XMLHttpRequest();
25316         
25317         Roo.each(this.files, function(file, index){
25318             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25319                 return;
25320             }
25321             
25322             file.xhr = xhr;
25323             
25324             this.el.createChild({
25325                 tag : 'div',
25326                 cls : 'roo-document-manager-loading',
25327                 cn : [
25328                     {
25329                         tag : 'div',
25330                         tooltip : file.name,
25331                         cls : 'roo-document-manager-thumb',
25332                         html : '<i class="fa fa-spinner fa-pulse"></i>'
25333                     }
25334                 ]
25335
25336             });
25337             
25338         }, this);
25339         
25340         if(this.files.length > 11){
25341             this.uploader.hide();
25342         }
25343         
25344         var headers = {
25345             "Accept": "application/json",
25346             "Cache-Control": "no-cache",
25347             "X-Requested-With": "XMLHttpRequest"
25348         };
25349         
25350         xhr.open(this.method, this.url, true);
25351         
25352         for (var headerName in headers) {
25353             var headerValue = headers[headerName];
25354             if (headerValue) {
25355                 xhr.setRequestHeader(headerName, headerValue);
25356             }
25357         }
25358         
25359         var _this = this;
25360         
25361         xhr.onload = function()
25362         {
25363             _this.xhrOnLoad(xhr);
25364         }
25365         
25366         xhr.onerror = function()
25367         {
25368             _this.xhrOnError(xhr);
25369         }
25370         
25371         var formData = new FormData();
25372
25373         formData.append('returnHTML', 'NO');
25374         
25375         Roo.each(this.files, function(file, index){
25376             
25377             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25378                 return;
25379             }
25380             
25381             formData.append(this.getParamName(index), file, file.name);
25382             
25383         }, this);
25384         
25385         if(this.fireEvent('prepare', this, formData) != false){
25386             xhr.send(formData);
25387         };
25388         
25389     },
25390     
25391     getParamName : function(i)
25392     {
25393         if(!this.multiple){
25394             return this.paramName;
25395         }
25396         
25397         return this.paramName + "_" + i;
25398     },
25399     
25400     refresh : function()
25401     {
25402         Roo.each(this.el.select('.roo-document-manager-loading', true).elements, function(el){
25403             el.remove();
25404         }, this);
25405         
25406         
25407         var files = [];
25408         
25409         Roo.each(this.files, function(file){
25410             
25411             if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25412                 return;
25413             }
25414             
25415             if(file.target){
25416                 files.push(file);
25417                 return;
25418             }
25419             
25420             var previewEl = this.el.createChild({
25421                 tag : 'div',
25422                 cls : 'roo-document-manager-preview',
25423                 cn : [
25424                     {
25425                         tag : 'div',
25426                         tooltip : file.filename,
25427                         cls : 'roo-document-manager-thumb',
25428                         html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25429                     },
25430                     {
25431                         tag : 'button',
25432                         cls : 'close',
25433                         html : 'x'
25434                     }
25435                 ]
25436             });
25437             
25438             var close = previewEl.select('button.close', true).first();
25439             
25440             close.on('click', this.onRemove, this, file);
25441             
25442             file.target = previewEl;
25443             
25444             var image = previewEl.select('img', true).first();
25445             
25446             image.on('click', this.onClick, this, file);
25447             
25448             files.push(file);
25449             
25450             return;
25451             
25452         }, this);
25453         
25454         this.files = files;
25455         
25456         this.uploader.show();
25457         
25458         if(this.files.length > 11){
25459             this.uploader.hide();
25460         }
25461         
25462         Roo.isTouch ? this.closable(false) : this.closable(true);
25463         
25464         this.fireEvent('refresh', this);
25465     },
25466     
25467     onRemove : function(e, el, o)
25468     {
25469         e.preventDefault();
25470         
25471         this.fireEvent('remove', this, o);
25472         
25473     },
25474     
25475     remove : function(o)
25476     {
25477         var files = [];
25478         
25479         Roo.each(this.files, function(file){
25480             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25481                 files.push(file);
25482                 return;
25483             }
25484
25485             o.target.remove();
25486
25487         }, this);
25488         
25489         this.files = files;
25490         
25491         this.refresh();
25492     },
25493     
25494     onClick : function(e, el, o)
25495     {
25496         e.preventDefault();
25497         
25498         this.fireEvent('click', this, o);
25499         
25500     },
25501     
25502     closable : function(closable)
25503     {
25504         Roo.each(this.el.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25505             
25506             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25507             
25508             if(closable){
25509                 el.show();
25510                 return;
25511             }
25512             
25513             el.hide();
25514             
25515         }, this);
25516     },
25517     
25518     xhrOnLoad : function(xhr)
25519     {
25520         if (xhr.readyState !== 4) {
25521             this.refresh();
25522             this.fireEvent('exception', this, xhr);
25523             return;
25524         }
25525
25526         var response = Roo.decode(xhr.responseText);
25527         
25528         if(!response.success){
25529             this.refresh();
25530             this.fireEvent('exception', this, xhr);
25531             return;
25532         }
25533         
25534         var i = 0;
25535         
25536         Roo.each(this.files, function(file, index){
25537             
25538             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25539                 return;
25540             }
25541             
25542             this.files[index] = response.data[i];
25543             i++;
25544             
25545             return;
25546             
25547         }, this);
25548         
25549         this.refresh();
25550         
25551     },
25552     
25553     xhrOnError : function()
25554     {
25555         Roo.log('xhr on error');
25556         
25557         var response = Roo.decode(xhr.responseText);
25558           
25559         Roo.log(response);
25560     }
25561     
25562     
25563     
25564 });
25565 /*
25566 * Licence: LGPL
25567 */
25568
25569 /**
25570  * @class Roo.bootstrap.DocumentViewer
25571  * @extends Roo.bootstrap.Component
25572  * Bootstrap DocumentViewer class
25573  * 
25574  * @constructor
25575  * Create a new DocumentViewer
25576  * @param {Object} config The config object
25577  */
25578
25579 Roo.bootstrap.DocumentViewer = function(config){
25580     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25581     
25582     this.addEvents({
25583         /**
25584          * @event initial
25585          * Fire after initEvent
25586          * @param {Roo.bootstrap.DocumentViewer} this
25587          */
25588         "initial" : true,
25589         /**
25590          * @event click
25591          * Fire after click
25592          * @param {Roo.bootstrap.DocumentViewer} this
25593          */
25594         "click" : true,
25595         /**
25596          * @event trash
25597          * Fire after trash button
25598          * @param {Roo.bootstrap.DocumentViewer} this
25599          */
25600         "trash" : true
25601         
25602     });
25603 };
25604
25605 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
25606     
25607     getAutoCreate : function()
25608     {
25609         var cfg = {
25610             tag : 'div',
25611             cls : 'roo-document-viewer',
25612             cn : [
25613                 {
25614                     tag : 'div',
25615                     cls : 'roo-document-viewer-body',
25616                     cn : [
25617                         {
25618                             tag : 'div',
25619                             cls : 'roo-document-viewer-thumb',
25620                             cn : [
25621                                 {
25622                                     tag : 'img',
25623                                     cls : 'roo-document-viewer-image'
25624                                 }
25625                             ]
25626                         }
25627                     ]
25628                 },
25629                 {
25630                     tag : 'div',
25631                     cls : 'roo-document-viewer-footer',
25632                     cn : {
25633                         tag : 'div',
25634                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25635                         cn : [
25636                             {
25637                                 tag : 'div',
25638                                 cls : 'btn-group',
25639                                 cn : [
25640                                     {
25641                                         tag : 'button',
25642                                         cls : 'btn btn-default roo-document-viewer-trash',
25643                                         html : '<i class="fa fa-trash"></i>'
25644                                     }
25645                                 ]
25646                             }
25647                         ]
25648                     }
25649                 }
25650             ]
25651         };
25652         
25653         return cfg;
25654     },
25655     
25656     initEvents : function()
25657     {
25658         
25659         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25660         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25661         
25662         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25663         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25664         
25665         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25666         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25667         
25668         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25669         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25670         
25671         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25672         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25673         
25674         this.bodyEl.on('click', this.onClick, this);
25675         
25676         this.trashBtn.on('click', this.onTrash, this);
25677         
25678     },
25679     
25680     initial : function()
25681     {
25682 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25683         
25684         
25685         this.fireEvent('initial', this);
25686         
25687     },
25688     
25689     onClick : function(e)
25690     {
25691         e.preventDefault();
25692         
25693         this.fireEvent('click', this);
25694     },
25695     
25696     onTrash : function(e)
25697     {
25698         e.preventDefault();
25699         
25700         this.fireEvent('trash', this);
25701     }
25702     
25703 });